fix: treat origin/main commits as integrated contamination base
This commit is contained in:
5
.changeset/remote-integration-contamination.md
Normal file
5
.changeset/remote-integration-contamination.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Treat foreign-attributed commits reachable from origin/main as already integrated during branch contamination checks to avoid false-positive recovery loops when local main is stale.
|
||||||
@@ -467,8 +467,8 @@ describe("branch-conflicts", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// FN-5475 / option-2 promotion check: a commit attributed to another
|
// FN-5475 / option-2 promotion check: a commit attributed to another
|
||||||
// task that's already reachable from local `main` was integrated via
|
// task that's already reachable from the integration target was integrated
|
||||||
// fast-forward and shouldn't be treated as contamination on a
|
// via fast-forward and shouldn't be treated as contamination on a
|
||||||
// downstream branch that briefly inherited it.
|
// downstream branch that briefly inherited it.
|
||||||
it("assertCleanBranchAtBase treats foreign-attributed commits that are ancestors of main as promoted", async () => {
|
it("assertCleanBranchAtBase treats foreign-attributed commits that are ancestors of main as promoted", async () => {
|
||||||
mockedExecSync.mockImplementation((cmd: string | string[]) => {
|
mockedExecSync.mockImplementation((cmd: string | string[]) => {
|
||||||
@@ -477,7 +477,7 @@ describe("branch-conflicts", () => {
|
|||||||
return Buffer.from("bbb222feat(FN-4386): foreignFusion-Task-Id: FN-4386\n");
|
return Buffer.from("bbb222feat(FN-4386): foreignFusion-Task-Id: FN-4386\n");
|
||||||
}
|
}
|
||||||
if (command.includes("git merge-base --is-ancestor 'bbb222' 'main'")) {
|
if (command.includes("git merge-base --is-ancestor 'bbb222' 'main'")) {
|
||||||
// Simulate the FN-5475 case: foreign commit is already on main.
|
// Simulate the FN-5475 case: foreign commit is already on local main.
|
||||||
return Buffer.from("");
|
return Buffer.from("");
|
||||||
}
|
}
|
||||||
throw new Error(`Unexpected command: ${command}`);
|
throw new Error(`Unexpected command: ${command}`);
|
||||||
@@ -488,14 +488,38 @@ describe("branch-conflicts", () => {
|
|||||||
).resolves.toBeUndefined();
|
).resolves.toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("assertCleanBranchAtBase still throws when foreign-attributed commits are NOT on main", async () => {
|
it("assertCleanBranchAtBase treats foreign-attributed commits that are only ancestors of origin/main as promoted", async () => {
|
||||||
|
mockedExecSync.mockImplementation((cmd: string | string[]) => {
|
||||||
|
const command = typeof cmd === "string" ? cmd : cmd[0];
|
||||||
|
if (command.includes("git log --format=%H%x1f%s%x1f%b 'main..fusion/fn-4068'")) {
|
||||||
|
return Buffer.from("bbb222feat(FN-4386): foreignFusion-Task-Id: FN-4386\n");
|
||||||
|
}
|
||||||
|
if (command.includes("git merge-base --is-ancestor 'bbb222' 'main'")) {
|
||||||
|
// Local main is stale and does not yet contain the promoted dependency.
|
||||||
|
const err = new Error("not an ancestor") as Error & { stderr?: string };
|
||||||
|
err.stderr = "";
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
if (command.includes("git merge-base --is-ancestor 'bbb222' 'origin/main'")) {
|
||||||
|
// Remote-tracking integration branch already contains it.
|
||||||
|
return Buffer.from("");
|
||||||
|
}
|
||||||
|
throw new Error(`Unexpected command: ${command}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
assertCleanBranchAtBase("/tmp/repo", "fusion/fn-4068", "main", "FN-4068"),
|
||||||
|
).resolves.toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("assertCleanBranchAtBase still throws when foreign-attributed commits are NOT on any integration ref", async () => {
|
||||||
mockedExecSync.mockImplementation((cmd: string | string[]) => {
|
mockedExecSync.mockImplementation((cmd: string | string[]) => {
|
||||||
const command = typeof cmd === "string" ? cmd : cmd[0];
|
const command = typeof cmd === "string" ? cmd : cmd[0];
|
||||||
if (command.includes("git log --format=%H%x1f%s%x1f%b 'main..fusion/fn-4068'")) {
|
if (command.includes("git log --format=%H%x1f%s%x1f%b 'main..fusion/fn-4068'")) {
|
||||||
return Buffer.from("bbb222feat(FN-4386): foreignFusion-Task-Id: FN-4386\n");
|
return Buffer.from("bbb222feat(FN-4386): foreignFusion-Task-Id: FN-4386\n");
|
||||||
}
|
}
|
||||||
if (command.includes("git merge-base --is-ancestor")) {
|
if (command.includes("git merge-base --is-ancestor")) {
|
||||||
// Not on main — exits non-zero.
|
// Not on any integration ref — exits non-zero.
|
||||||
const err = new Error("not an ancestor") as Error & { stderr?: string };
|
const err = new Error("not an ancestor") as Error & { stderr?: string };
|
||||||
err.stderr = "";
|
err.stderr = "";
|
||||||
throw err;
|
throw err;
|
||||||
|
|||||||
@@ -447,14 +447,23 @@ export async function assertCleanBranchAtBase(
|
|||||||
|
|
||||||
if (candidateForeign.length === 0) return;
|
if (candidateForeign.length === 0) return;
|
||||||
|
|
||||||
// FN-5475: a commit attributed to another task that's already reachable
|
// FN-5475/FN-219: a commit attributed to another task that's already
|
||||||
// from local `main` was promoted through integration. Treat it as
|
// reachable from the integration target was promoted through integration.
|
||||||
// ancestral, not contamination. This closes the race where a sibling
|
// Treat it as ancestral, not contamination. Check both local `main` and
|
||||||
// task's commit briefly sat at local-main's tip while a downstream
|
// `origin/main`: long-running dashboards can have stale local main while
|
||||||
// worktree was created (cross-task tip absorption).
|
// the remote-tracking branch already contains the promoted dependency, and
|
||||||
|
// using only local main produces false branch-cross-contamination loops.
|
||||||
|
const integratedRefs = ["main", "origin/main"];
|
||||||
const foreignCommits: BranchCrossContaminationCommit[] = [];
|
const foreignCommits: BranchCrossContaminationCommit[] = [];
|
||||||
for (const commit of candidateForeign) {
|
for (const commit of candidateForeign) {
|
||||||
if (await isAncestorOf(repoDir, commit.sha, "main")) continue;
|
let alreadyIntegrated = false;
|
||||||
|
for (const ref of integratedRefs) {
|
||||||
|
if (await isAncestorOf(repoDir, commit.sha, ref)) {
|
||||||
|
alreadyIntegrated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (alreadyIntegrated) continue;
|
||||||
foreignCommits.push(commit);
|
foreignCommits.push(commit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user