FN-7133: fix PR merge status repository lookup
Fix PR-mode auto-merge status checks to query GitHub with the project repository. - Resolve the current project owner/repo once from the task cwd before PR status checks. - Pass owner/repo/number to getPrMergeStatus for shared-group, task, and retry paths. - Cover repository resolution and PR status argument behavior in lifecycle tests. - Add a patch changeset for the published CLI fix. Files changed: .changeset/FN-7133-pr-merge-status-repo-args.md | 7 ++++ .../src/commands/__tests__/task-lifecycle.test.ts | 39 +++++++++++++++++++++- packages/cli/src/commands/task-lifecycle.ts | 17 +++++++--- 3 files changed, 58 insertions(+), 5 deletions(-) Fusion-Task-Id: FN-7133 Fusion-Task-Lineage: 3f9bfd65-6950-40dc-9505-53140bd6a6a1 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/FN-7133-pr-merge-status-repo-args.md
Normal file
7
.changeset/FN-7133-pr-merge-status-repo-args.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Fix PR-mode auto-merge failing with "error connecting to <branch>".
|
||||
category: fix
|
||||
dev: processPullRequestMergeTask now resolves owner/repo via getCurrentRepo(cwd) and passes (owner, repo, number) to getPrMergeStatus at all three call sites (shared-group, per-task, retry); the local GitHubOperations interface param names corrected from base/head to owner/repo. FN-7133.
|
||||
@@ -36,6 +36,7 @@ vi.mock("@fusion/core", async () => {
|
||||
};
|
||||
});
|
||||
|
||||
import { getCurrentRepo } from "@fusion/core";
|
||||
import { activeSessionRegistry } from "@fusion/engine";
|
||||
import {
|
||||
cleanupMergedTaskArtifacts,
|
||||
@@ -153,6 +154,7 @@ describe("processPullRequestMergeTask", () => {
|
||||
beforeEach(() => {
|
||||
execMock.mockReset();
|
||||
execFileCalls.length = 0;
|
||||
vi.mocked(getCurrentRepo).mockReturnValue({ owner: "owner", repo: "repo" });
|
||||
});
|
||||
|
||||
it("pushes the per-task branch to origin before creating a new PR", async () => {
|
||||
@@ -421,7 +423,7 @@ describe("processPullRequestMergeTask", () => {
|
||||
await processPullRequestMergeTask(store as never, "/repo", task.id, github as never, () => undefined);
|
||||
|
||||
expect(github.createPr).not.toHaveBeenCalled();
|
||||
expect(github.getPrMergeStatus).toHaveBeenCalledWith("main", "fusion/groups/p-2", 22);
|
||||
expect(github.getPrMergeStatus).toHaveBeenCalledWith("owner", "repo", 22);
|
||||
expect(store.updateBranchGroup).toHaveBeenCalledWith("BG-2", expect.objectContaining({
|
||||
prNumber: 22,
|
||||
prUrl: "https://github.com/x/y/pull/22",
|
||||
@@ -429,6 +431,38 @@ describe("processPullRequestMergeTask", () => {
|
||||
}));
|
||||
});
|
||||
|
||||
it("rejects before PR status checks when the project repository cannot be resolved", async () => {
|
||||
vi.mocked(getCurrentRepo).mockReturnValueOnce(null);
|
||||
const task: MockTask = {
|
||||
id: "FN-7133",
|
||||
title: "test",
|
||||
description: "desc",
|
||||
column: "in-review",
|
||||
prInfo: {
|
||||
number: 7133,
|
||||
url: "https://github.com/x/y/pull/7133",
|
||||
status: "open",
|
||||
headBranch: "fusion/fn-7133",
|
||||
baseBranch: "main",
|
||||
},
|
||||
};
|
||||
const store = makeStore(task);
|
||||
const github = {
|
||||
findPrForBranch: vi.fn(),
|
||||
createPr: vi.fn(),
|
||||
getPrMergeStatus: vi.fn(),
|
||||
mergePr: vi.fn(),
|
||||
};
|
||||
|
||||
await expect(
|
||||
processPullRequestMergeTask(store as never, "/repo", task.id, github as never, () => undefined),
|
||||
).rejects.toThrow("processPullRequestMergeTask: could not determine repository");
|
||||
|
||||
expect(github.getPrMergeStatus).not.toHaveBeenCalled();
|
||||
expect(github.findPrForBranch).not.toHaveBeenCalled();
|
||||
expect(github.createPr).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("finalizes branch group and member tasks when shared group PR is already merged", async () => {
|
||||
const taskA: MockTask = {
|
||||
id: "FN-9015",
|
||||
@@ -594,6 +628,7 @@ describe("processPullRequestMergeTask", () => {
|
||||
expect(github.createPr).toHaveBeenCalledWith(expect.objectContaining({
|
||||
base: "main",
|
||||
}));
|
||||
expect(github.getPrMergeStatus).toHaveBeenCalledWith("owner", "repo", 7);
|
||||
});
|
||||
|
||||
it("skips the push when an existing PR already covers the branch", async () => {
|
||||
@@ -966,6 +1001,8 @@ describe("processPullRequestMergeTask", () => {
|
||||
expect(result).toBe("merged");
|
||||
expect(github.mergePr).toHaveBeenCalledWith({ number: 124, method: "squash" });
|
||||
expect(github.getPrMergeStatus).toHaveBeenCalledTimes(2);
|
||||
expect(github.getPrMergeStatus).toHaveBeenNthCalledWith(1, "owner", "repo", 124);
|
||||
expect(github.getPrMergeStatus).toHaveBeenNthCalledWith(2, "owner", "repo", 124);
|
||||
expect(store.updatePrInfo).toHaveBeenLastCalledWith("FN-9104", expect.objectContaining({ status: "merged" }));
|
||||
expect(store.updateTask).toHaveBeenCalledWith("FN-9104", { status: null, mergeRetries: 0 });
|
||||
expect(store.moveTask).toHaveBeenCalledWith("FN-9104", "done");
|
||||
|
||||
@@ -43,7 +43,7 @@ import type {
|
||||
interface GitHubOperations {
|
||||
findPrForBranch(params: { head: string; state?: "open" | "closed" | "all" }): Promise<PrInfo | null>;
|
||||
createPr(params: { title: string; body: string; head: string; base?: string }): Promise<PrInfo>;
|
||||
getPrMergeStatus(base?: string, head?: string, number?: number): Promise<{
|
||||
getPrMergeStatus(owner?: string, repo?: string, number?: number): Promise<{
|
||||
prInfo: PrInfo;
|
||||
reviewDecision: string | null;
|
||||
checks: Array<{ name: string; required: boolean; state: string }>;
|
||||
@@ -667,6 +667,15 @@ export async function processPullRequestMergeTask(
|
||||
return "skipped";
|
||||
}
|
||||
|
||||
/*
|
||||
* FNXC:PrMergeAutoMerge 2026-06-27-13:14:
|
||||
* FN-7133 requires PR-mode merge status to resolve owner/repo from the project cwd because multi-project daemons cannot rely on process cwd. Never pass branch names into gh pr view --repo; getPrMergeStatus forwards its first two args as the repository slug.
|
||||
*/
|
||||
const prRepo = getCurrentRepo(cwd);
|
||||
if (!prRepo) {
|
||||
throw new Error("processPullRequestMergeTask: could not determine repository");
|
||||
}
|
||||
|
||||
const branch = getTaskBranchName(task.id);
|
||||
const settings = await store.getSettings();
|
||||
// `requirePrApproval` MOVED to workflow settings (U4): resolve the task's
|
||||
@@ -756,7 +765,7 @@ export async function processPullRequestMergeTask(
|
||||
prState: toBranchGroupPrState(groupPrInfo),
|
||||
});
|
||||
|
||||
const mergeStatus = await github.getPrMergeStatus(projectDefaultBranch, branchGroup.branchName, groupPrInfo.number);
|
||||
const mergeStatus = await github.getPrMergeStatus(prRepo.owner, prRepo.repo, groupPrInfo.number);
|
||||
const refreshedPrInfo: PrInfo = {
|
||||
...groupPrInfo,
|
||||
...mergeStatus.prInfo,
|
||||
@@ -858,7 +867,7 @@ export async function processPullRequestMergeTask(
|
||||
throw new Error(`Failed to create or resolve pull request for ${task.id}`);
|
||||
}
|
||||
|
||||
const mergeStatus = await github.getPrMergeStatus(mergeTarget.branch, branch, prInfo.number);
|
||||
const mergeStatus = await github.getPrMergeStatus(prRepo.owner, prRepo.repo, prInfo.number);
|
||||
const refreshedPrInfo: PrInfo = {
|
||||
...prInfo,
|
||||
...mergeStatus.prInfo,
|
||||
@@ -904,7 +913,7 @@ export async function processPullRequestMergeTask(
|
||||
} catch (err: unknown) {
|
||||
let refreshedStatus: Awaited<ReturnType<GitHubOperations["getPrMergeStatus"]>>;
|
||||
try {
|
||||
refreshedStatus = await github.getPrMergeStatus(mergeTarget.branch, branch, prInfo.number);
|
||||
refreshedStatus = await github.getPrMergeStatus(prRepo.owner, prRepo.repo, prInfo.number);
|
||||
} catch {
|
||||
throw err;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user