diff --git a/src/github/api/queries/github.ts b/src/github/api/queries/github.ts index 7bceb8f..08dc25b 100644 --- a/src/github/api/queries/github.ts +++ b/src/github/api/queries/github.ts @@ -12,6 +12,13 @@ export const PR_QUERY = ` baseRefName headRefName headRefOid + isCrossRepository + headRepository { + owner { + login + } + name + } createdAt updatedAt lastEditedAt diff --git a/src/github/operations/branch.ts b/src/github/operations/branch.ts index b2c145b..4de3546 100644 --- a/src/github/operations/branch.ts +++ b/src/github/operations/branch.ts @@ -166,9 +166,23 @@ export async function setupBranch( // Validate branch names before use to prevent command injection validateBranchName(branchName); - // Execute git commands to checkout PR branch (dynamic depth based on PR size) - // Using execFileSync instead of shell template literals for security - execGit(["fetch", "origin", `--depth=${fetchDepth}`, branchName]); + // For cross-repository (fork) PRs, fetch via the pull ref since the + // branch only exists on the fork's remote, not on origin. + if (prData.isCrossRepository) { + console.log( + `PR #${entityNumber} is from a fork, fetching via refs/pull/${entityNumber}/head...`, + ); + execGit([ + "fetch", + "origin", + `--depth=${fetchDepth}`, + `pull/${entityNumber}/head:${branchName}`, + ]); + } else { + // Execute git commands to checkout PR branch (dynamic depth based on PR size) + // Using execFileSync instead of shell template literals for security + execGit(["fetch", "origin", `--depth=${fetchDepth}`, branchName]); + } execGit(["checkout", branchName, "--"]); console.log(`Successfully checked out PR branch for PR #${entityNumber}`); diff --git a/src/github/types.ts b/src/github/types.ts index d982620..6ed41e3 100644 --- a/src/github/types.ts +++ b/src/github/types.ts @@ -57,6 +57,13 @@ export type GitHubPullRequest = { baseRefName: string; headRefName: string; headRefOid: string; + isCrossRepository: boolean; + headRepository: { + owner: { + login: string; + }; + name: string; + } | null; createdAt: string; updatedAt?: string; lastEditedAt?: string; diff --git a/test/create-prompt.test.ts b/test/create-prompt.test.ts index cc3f306..f4b3b34 100644 --- a/test/create-prompt.test.ts +++ b/test/create-prompt.test.ts @@ -27,6 +27,8 @@ describe("generatePrompt", () => { baseRefName: "main", headRefName: "feature-branch", headRefOid: "abc123", + isCrossRepository: false, + headRepository: { owner: { login: "testowner" }, name: "testrepo" }, commits: { totalCount: 2, nodes: [ diff --git a/test/data-fetcher.test.ts b/test/data-fetcher.test.ts index 98f52e5..c474264 100644 --- a/test/data-fetcher.test.ts +++ b/test/data-fetcher.test.ts @@ -1006,6 +1006,8 @@ describe("fetchGitHubData integration with time filtering", () => { baseRefName: "main", headRefName: "feature", headRefOid: "abc123", + isCrossRepository: false, + headRepository: { owner: { login: "testowner" }, name: "testrepo" }, createdAt: "2024-01-15T10:00:00Z", updatedAt: "2024-01-15T12:30:00Z", // Edited after trigger lastEditedAt: "2024-01-15T12:30:00Z", // Edited after trigger diff --git a/test/data-formatter.test.ts b/test/data-formatter.test.ts index 4c6b150..375bc58 100644 --- a/test/data-formatter.test.ts +++ b/test/data-formatter.test.ts @@ -24,6 +24,8 @@ describe("formatContext", () => { baseRefName: "main", headRefName: "feature/test", headRefOid: "abc123", + isCrossRepository: false, + headRepository: { owner: { login: "testowner" }, name: "testrepo" }, createdAt: "2023-01-01T00:00:00Z", additions: 50, deletions: 30, diff --git a/test/pull-request-target.test.ts b/test/pull-request-target.test.ts index 6d9cac0..666c1b3 100644 --- a/test/pull-request-target.test.ts +++ b/test/pull-request-target.test.ts @@ -17,6 +17,8 @@ describe("pull_request_target event support", () => { baseRefName: "main", headRefName: "feature-branch", headRefOid: "abc123", + isCrossRepository: false, + headRepository: { owner: { login: "testowner" }, name: "testrepo" }, commits: { totalCount: 2, nodes: [