Skip to content

Commit 95689f2

Browse files
fix(root): deepen main history for pr change detection
1 parent 77eaf00 commit 95689f2

3 files changed

Lines changed: 80 additions & 25 deletions

File tree

packages/docs/logs/2026-05-11_renovate-dashboard-481-max-updates.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ User asked to work in a new dissociated clone, update as much of Renovate dashbo
1616
- Created branch `chore/renovate-481-max` from fresh `origin/main`.
1717
- Opened draft PR #773: <https://github.com/shepherdjerred/monorepo/pull/773>.
1818
- Pushed commit `a7ee0bdbc` (`chore(root): update renovate dashboard 481 dependencies`).
19+
- Fixed the Buildkite PR pickup failure by deepening shallow `origin/main` history when the CI pipeline generator cannot compute a merge base.
1920
- Applied the Renovate dashboard sweep across Bun/npm package manifests and locks, Rust crates, Maven deps, Swift packages, Go modules, Terraform/OpenTofu providers, tool pins, Gradle wrapper, and Docker/image digests.
2021
- Migrated Prisma 7 generation/runtime setup for Birmel and Scout backend, including Prisma config files and libSQL adapters.
2122
- Updated Zod 4 call sites and replaced the DPP config loader's `zconf` dependency with TOML parsing plus explicit Zod validation.
@@ -32,5 +33,6 @@ User asked to work in a new dissociated clone, update as much of Renovate dashbo
3233
### Caveats
3334

3435
- Validation passed: `bun run scripts/setup.ts`, `bun run lint`, `bun run typecheck`, `bun run test`, Clauderon `cargo check`, Scout desktop `cargo check`, Castle Casters `mvn test`, ASUSWRT provider `go test ./...`, all three updated Tofu `validate` runs, and Android `./gradlew help` with the local Android SDK exported.
36+
- CI pickup fix validation passed: `cd scripts/ci && bun test src/__tests__/change-detection.test.ts`, `cd scripts/ci && bun run typecheck`, `cd scripts/ci && bun test`, and local PR pipeline generation with `BUILDKITE_PULL_REQUEST=773`.
3537
- Android validation installed NDK `26.1.10909125` into `/Users/jerred/Library/Android/sdk` because the updated Gradle project requested it.
3638
- Some validators still print non-fatal warnings/hints: SwiftLint renamed-rule warnings, Astro inline-script hints, Gradle deprecation warnings, and KSP warnings under Kotlin 2.3.20.

scripts/ci/src/__tests__/change-detection.test.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -593,20 +593,49 @@ describe("fail-fast base detection", () => {
593593
it("rejects when merge-base cannot be computed", async () => {
594594
process.env["BUILDKITE_BRANCH"] = "feature";
595595
process.env["BUILDKITE_PULL_REQUEST"] = "42";
596-
// Pretend origin/main is already resolvable so ensureOriginMain short-circuits;
597-
// merge-base then fails (e.g. shallow clone history doesn't reach the common ancestor).
598596
const execFn: ExecFn = async (cmd) => {
599597
if (cmd[1] === "rev-parse") {
600598
return { stdout: "deadbeef", exitCode: 0 };
601599
}
600+
if (cmd[1] === "fetch") {
601+
return { stdout: "", exitCode: 0 };
602+
}
602603
return { stdout: "", exitCode: 1 };
603604
};
604605

605606
await expect(_getBaseRevision(execFn)).rejects.toThrow(
606-
"Unable to compute merge-base",
607+
"Unable to compute merge-base with origin/main after deepening shallow history",
607608
);
608609
});
609610

611+
it("deepens origin/main history when the shallow checkout lacks a merge-base", async () => {
612+
process.env["BUILDKITE_BRANCH"] = "feature";
613+
process.env["BUILDKITE_PULL_REQUEST"] = "42";
614+
const calls: string[][] = [];
615+
let mergeBaseCalls = 0;
616+
const execFn: ExecFn = async (cmd) => {
617+
calls.push(cmd);
618+
if (cmd[1] === "rev-parse") {
619+
return { stdout: "deadbeef", exitCode: 0 };
620+
}
621+
if (cmd[1] === "fetch") {
622+
return { stdout: "", exitCode: 0 };
623+
}
624+
if (cmd[1] === "merge-base") {
625+
mergeBaseCalls += 1;
626+
if (mergeBaseCalls === 1) {
627+
return { stdout: "", exitCode: 1 };
628+
}
629+
return { stdout: "abc123", exitCode: 0 };
630+
}
631+
return { stdout: "", exitCode: 1 };
632+
};
633+
634+
const base = await _getBaseRevision(execFn);
635+
expect(base).toBe("abc123");
636+
expect(calls.some((c) => c.includes("--deepen=1000"))).toBe(true);
637+
});
638+
610639
it("fetches origin/main when missing before merge-base", async () => {
611640
process.env["BUILDKITE_BRANCH"] = "feature";
612641
process.env["BUILDKITE_PULL_REQUEST"] = "42";
@@ -629,6 +658,7 @@ describe("fail-fast base detection", () => {
629658
expect(base).toBe("abc123");
630659
const fetchCmd = calls.find((c) => c[1] === "fetch");
631660
expect(fetchCmd).toBeDefined();
661+
expect(fetchCmd).toContain("--depth=100");
632662
expect(fetchCmd).toContain("+refs/heads/main:refs/remotes/origin/main");
633663
});
634664

scripts/ci/src/change-detection.ts

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,25 @@ async function exec(cmd: string[]): Promise<ExecResult> {
282282
return { stdout: stdout.trim(), exitCode };
283283
}
284284

285+
const ORIGIN_MAIN_REFSPEC = "+refs/heads/main:refs/remotes/origin/main";
286+
287+
async function fetchOriginMain(
288+
execFn: ExecFn,
289+
depthArg: string,
290+
): Promise<void> {
291+
const fetch = await execFn([
292+
"git",
293+
"fetch",
294+
"--no-tags",
295+
depthArg,
296+
"origin",
297+
ORIGIN_MAIN_REFSPEC,
298+
]);
299+
if (fetch.exitCode !== 0) {
300+
throw new Error("Unable to fetch origin/main for merge-base computation");
301+
}
302+
}
303+
285304
// Buildkite's checkout uses --depth=100 and only fetches the PR HEAD ref, so
286305
// `origin/main` is often missing as a remote-tracking ref. Fetch it explicitly
287306
// before any merge-base call.
@@ -296,43 +315,47 @@ async function ensureOriginMain(execFn: ExecFn): Promise<void> {
296315
if (check.exitCode === 0 && check.stdout !== "") {
297316
return;
298317
}
299-
const fetch = await execFn([
300-
"git",
301-
"fetch",
302-
"--no-tags",
303-
"--depth=100",
304-
"origin",
305-
"+refs/heads/main:refs/remotes/origin/main",
306-
]);
307-
if (fetch.exitCode !== 0) {
308-
throw new Error("Unable to fetch origin/main for merge-base computation");
318+
await fetchOriginMain(execFn, "--depth=100");
319+
}
320+
321+
async function getMergeBaseWithOriginMain(execFn: ExecFn): Promise<string> {
322+
await ensureOriginMain(execFn);
323+
324+
const initial = await execFn(["git", "merge-base", "HEAD", "origin/main"]);
325+
if (initial.exitCode === 0 && initial.stdout !== "") {
326+
return initial.stdout;
309327
}
328+
329+
// The remote-tracking ref can exist while the shallow clone still lacks the
330+
// common ancestor. Deepen in bounded steps so normal PRs stay cheap while
331+
// long-lived branches still get a fair retry before failing.
332+
for (const depthArg of ["--deepen=1000", "--deepen=10000"]) {
333+
await fetchOriginMain(execFn, depthArg);
334+
const result = await execFn(["git", "merge-base", "HEAD", "origin/main"]);
335+
if (result.exitCode === 0 && result.stdout !== "") {
336+
return result.stdout;
337+
}
338+
}
339+
340+
throw new Error(
341+
"Unable to compute merge-base with origin/main after deepening shallow history",
342+
);
310343
}
311344

312345
async function getBaseRevision(execFn: ExecFn = exec): Promise<string> {
313346
const branch = process.env["BUILDKITE_BRANCH"] ?? "";
314347
const pullRequest = process.env["BUILDKITE_PULL_REQUEST"] ?? "false";
315348

316349
if (pullRequest && pullRequest !== "false") {
317-
await ensureOriginMain(execFn);
318-
const result = await execFn(["git", "merge-base", "HEAD", "origin/main"]);
319-
if (result.exitCode !== 0 || result.stdout === "") {
320-
throw new Error("Unable to compute merge-base with origin/main");
321-
}
322-
return result.stdout;
350+
return getMergeBaseWithOriginMain(execFn);
323351
}
324352

325353
if (branch === "main") {
326354
return getLastGreenCommit();
327355
}
328356

329357
// Feature branch without PR
330-
await ensureOriginMain(execFn);
331-
const result = await execFn(["git", "merge-base", "HEAD", "origin/main"]);
332-
if (result.exitCode !== 0 || result.stdout === "") {
333-
throw new Error("Unable to compute merge-base with origin/main");
334-
}
335-
return result.stdout;
358+
return getMergeBaseWithOriginMain(execFn);
336359
}
337360

338361
async function getChangedFiles(execFn: ExecFn = exec): Promise<string[]> {

0 commit comments

Comments
 (0)