feat(FN-4688): complete Step 2 — use range stats in landed commit lookup
Fusion-Task-Id: FN-4688 Fusion-Task-Lineage: 49abde1d-3480-4127-beb0-5dadce30b9e1
This commit is contained in:
committed by
gsxdsm
parent
6c542462ed
commit
9f970ff22c
@@ -2380,6 +2380,65 @@ describe("SelfHealingManager", () => {
|
|||||||
managerWithRecovery.stop();
|
managerWithRecovery.stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses rebase range shortstat and propagates rebaseBaseSha when mergeDetails provides it", async () => {
|
||||||
|
const managerWithRecovery = new SelfHealingManager(store, {
|
||||||
|
rootDir: "/tmp/test-project",
|
||||||
|
});
|
||||||
|
(store.getSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||||
|
taskStuckTimeoutMs: 60_000,
|
||||||
|
});
|
||||||
|
const staleUpdatedAt = new Date(Date.now() - 61_000).toISOString();
|
||||||
|
|
||||||
|
(store.listTasks as ReturnType<typeof vi.fn>).mockResolvedValue([
|
||||||
|
{
|
||||||
|
id: "FN-2901",
|
||||||
|
column: "in-review",
|
||||||
|
status: "merging",
|
||||||
|
error: null,
|
||||||
|
paused: false,
|
||||||
|
worktree: "/tmp/test-project/.worktrees/fn-2901",
|
||||||
|
branch: "fusion/fn-2901",
|
||||||
|
baseCommitSha: "base901",
|
||||||
|
updatedAt: staleUpdatedAt,
|
||||||
|
steps: [{ name: "Ship it", status: "done" }],
|
||||||
|
workflowStepResults: [],
|
||||||
|
mergeDetails: { rebaseBaseSha: "rebasebase901" },
|
||||||
|
log: [],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
mockedExistsSync.mockReturnValue(true);
|
||||||
|
mockedExecSync.mockImplementation((command) => {
|
||||||
|
const cmd = String(command);
|
||||||
|
if (cmd.includes("git log") && cmd.includes("Fusion-Task-Id: FN-2901")) {
|
||||||
|
return "rangeSha901\u001ffeat: ship something opaque\n" as any;
|
||||||
|
}
|
||||||
|
if (cmd.includes("git diff --shortstat") && cmd.includes("rebasebase901..rangeSha901")) {
|
||||||
|
return " 4 files changed, 104 insertions(+), 1 deletion(-)\n" as any;
|
||||||
|
}
|
||||||
|
return "" as any;
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await managerWithRecovery.recoverInterruptedMergingTasks();
|
||||||
|
|
||||||
|
expect(result).toBe(1);
|
||||||
|
expect(mockedExecSync.mock.calls.some(([cmd]) => String(cmd).includes("git diff --shortstat") && String(cmd).includes("rebasebase901..rangeSha901"))).toBe(true);
|
||||||
|
expect(mockedExecSync.mock.calls.some(([cmd]) => String(cmd).includes("git show --shortstat") && String(cmd).includes("rangeSha901"))).toBe(false);
|
||||||
|
expect(store.updateTask).toHaveBeenCalledWith("FN-2901", {
|
||||||
|
status: null,
|
||||||
|
error: null,
|
||||||
|
mergeRetries: 0,
|
||||||
|
mergeDetails: expect.objectContaining({
|
||||||
|
commitSha: "rangeSha901",
|
||||||
|
rebaseBaseSha: "rebasebase901",
|
||||||
|
filesChanged: 4,
|
||||||
|
insertions: 104,
|
||||||
|
deletions: 1,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
managerWithRecovery.stop();
|
||||||
|
});
|
||||||
|
|
||||||
it("finalizes stale merging tasks when baseCommitSha was advanced past the landed commit", async () => {
|
it("finalizes stale merging tasks when baseCommitSha was advanced past the landed commit", async () => {
|
||||||
// Reproduces the case where the merger fast-forward-rebased the task branch
|
// Reproduces the case where the merger fast-forward-rebased the task branch
|
||||||
// and updated baseCommitSha to the new HEAD; the bounded `base..HEAD` range
|
// and updated baseCommitSha to the new HEAD; the bounded `base..HEAD` range
|
||||||
|
|||||||
@@ -731,6 +731,7 @@ export class SelfHealingManager {
|
|||||||
// miss; (3) catches commits authored before the trailer was introduced.
|
// miss; (3) catches commits authored before the trailer was introduced.
|
||||||
|
|
||||||
// ── (1) Stored sha ────────────────────────────────────────────────────
|
// ── (1) Stored sha ────────────────────────────────────────────────────
|
||||||
|
const rebaseBaseSha = task.mergeDetails?.rebaseBaseSha;
|
||||||
const storedSha = task.mergeDetails?.commitSha;
|
const storedSha = task.mergeDetails?.commitSha;
|
||||||
if (storedSha) {
|
if (storedSha) {
|
||||||
try {
|
try {
|
||||||
@@ -744,13 +745,12 @@ export class SelfHealingManager {
|
|||||||
);
|
);
|
||||||
const [sha, subject = "", body = ""] = stdout.trim().split("\x1f");
|
const [sha, subject = "", body = ""] = stdout.trim().split("\x1f");
|
||||||
if (sha && commitOwnedByTask(task.id, task.lineageId, subject, body)) {
|
if (sha && commitOwnedByTask(task.id, task.lineageId, subject, body)) {
|
||||||
const commit: LandedTaskCommit = { sha, subject };
|
const commit: LandedTaskCommit = { sha, subject, rebaseBaseSha };
|
||||||
try {
|
try {
|
||||||
const stats = await execAsync(`git show --shortstat --format= ${shellQuote(sha)}`, {
|
const shortstat = await this.readShortstatForSha(sha, rebaseBaseSha);
|
||||||
cwd: this.options.rootDir,
|
if (shortstat) {
|
||||||
maxBuffer: 1024 * 1024,
|
Object.assign(commit, shortstat);
|
||||||
});
|
}
|
||||||
Object.assign(commit, parseShortstat(stats.stdout));
|
|
||||||
} catch { /* stats are optional */ }
|
} catch { /* stats are optional */ }
|
||||||
return commit;
|
return commit;
|
||||||
}
|
}
|
||||||
@@ -829,13 +829,12 @@ export class SelfHealingManager {
|
|||||||
const [sha, subject] = firstLine.split("\x1f");
|
const [sha, subject] = firstLine.split("\x1f");
|
||||||
if (!sha) return null;
|
if (!sha) return null;
|
||||||
|
|
||||||
const commit: LandedTaskCommit = { sha, subject };
|
const commit: LandedTaskCommit = { sha, subject, rebaseBaseSha };
|
||||||
try {
|
try {
|
||||||
const stats = await execAsync(`git show --shortstat --format= ${shellQuote(sha)}`, {
|
const shortstat = await this.readShortstatForSha(sha, rebaseBaseSha);
|
||||||
cwd: this.options.rootDir,
|
if (shortstat) {
|
||||||
maxBuffer: 1024 * 1024,
|
Object.assign(commit, shortstat);
|
||||||
});
|
}
|
||||||
Object.assign(commit, parseShortstat(stats.stdout));
|
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||||
log.warn(
|
log.warn(
|
||||||
|
|||||||
Reference in New Issue
Block a user