diff --git a/entry b/entry index 7a992c7..c69e4db 100755 --- a/entry +++ b/entry @@ -83327,7 +83327,7 @@ function query({ // package.json var package_default = { name: "@pullfrog/action", - version: "0.0.143", + version: "0.0.144", type: "module", files: [ "index.js", @@ -123871,39 +123871,34 @@ async function setupGit(ctx) { } catch { log.debug("No existing authentication headers to remove"); } - const originUrl = `https://x-access-token:${ctx.githubInstallationToken}@github.com/${ctx.owner}/${ctx.name}.git`; - $("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir }); - log.info("\u2713 Updated origin URL with authentication token"); if (ctx.payload.event.is_pr !== true || !ctx.payload.event.issue_number) { - log.debug("Not a PR event, staying on default branch"); + const originUrl = `https://x-access-token:${ctx.githubInstallationToken}@github.com/${ctx.owner}/${ctx.name}.git`; + $("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir }); + log.info("\u2713 Updated origin URL with authentication token"); return { pushRemote: "origin" }; } const prNumber = ctx.payload.event.issue_number; - log.info(`\u{1F33F} Checking out PR #${prNumber}...`); - $("gh", ["pr", "checkout", prNumber.toString()], { - cwd: repoDir, - env: { GH_TOKEN: ctx.githubInstallationToken } - }); - log.info(`\u2713 Successfully checked out PR #${prNumber}`); const pr = await ctx.octokit.rest.pulls.get({ owner: ctx.owner, repo: ctx.name, pull_number: prNumber }); const headRepo = pr.data.head.repo; - const baseRepo = pr.data.base.repo; - if (!headRepo || headRepo.full_name === baseRepo.full_name) { - return { pushRemote: "origin" }; - } - if (!pr.data.maintainer_can_modify) { - log.warning( - `\u26A0\uFE0F Fork PR from ${headRepo.owner.login} does not allow maintainer edits. Push may fail.` - ); + if (!headRepo) { + throw new Error(`PR #${prNumber} source repository was deleted`); } + const branch = pr.data.head.ref; + log.info(`\u{1F33F} Checking out PR #${prNumber} (${branch})...`); + $("git", ["fetch", "origin", `pull/${prNumber}/head:${branch}`], { cwd: repoDir }); + $("git", ["checkout", branch], { cwd: repoDir }); + log.info(`\u2713 Successfully checked out PR #${prNumber}`); const token = process.env.GITHUB_TOKEN || ctx.githubInstallationToken; - const forkUrl = `https://x-access-token:${token}@github.com/${headRepo.full_name}.git`; - log.info(`\u{1F374} Fork PR detected, will push to: ${headRepo.full_name}`); - return { pushRemote: forkUrl }; + const pushUrl = `https://x-access-token:${token}@github.com/${headRepo.full_name}.git`; + const isFork = headRepo.full_name !== pr.data.base.repo.full_name; + if (isFork) { + log.info(`\u{1F374} Fork PR detected, will push to: ${headRepo.full_name}`); + } + return { pushRemote: pushUrl }; } // utils/timer.ts diff --git a/package.json b/package.json index f281a92..36635a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pullfrog/action", - "version": "0.0.143", + "version": "0.0.144", "type": "module", "files": [ "index.js", diff --git a/utils/setup.ts b/utils/setup.ts index 8247a2e..8047336 100644 --- a/utils/setup.ts +++ b/utils/setup.ts @@ -68,8 +68,8 @@ export type SetupGitResult = { /** * Unified git setup: configures authentication and checks out PR branch if applicable. - * For fork PRs, returns a full URL to push to (since gh pr checkout doesn't set up remotes in Actions). - * For same-repo PRs, returns "origin". + * For PR events, always returns a push URL constructed from the PR's head repo. + * This works for both same-repo and fork PRs with a single code path. */ export async function setupGit(ctx: Context): Promise { const repoDir = process.cwd(); @@ -87,27 +87,16 @@ export async function setupGit(ctx: Context): Promise { log.debug("No existing authentication headers to remove"); } - // embed token directly in origin URL - simple and doesn't expose token in env - const originUrl = `https://x-access-token:${ctx.githubInstallationToken}@github.com/${ctx.owner}/${ctx.name}.git`; - $("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir }); - log.info("✓ Updated origin URL with authentication token"); - - // non-PR events: stay on default branch, push to origin + // non-PR events: set up origin with token, stay on default branch if (ctx.payload.event.is_pr !== true || !ctx.payload.event.issue_number) { - log.debug("Not a PR event, staying on default branch"); + const originUrl = `https://x-access-token:${ctx.githubInstallationToken}@github.com/${ctx.owner}/${ctx.name}.git`; + $("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir }); + log.info("✓ Updated origin URL with authentication token"); return { pushRemote: "origin" }; } - // checkout PR branch + // PR event: fetch PR info to get branch name and head repo const prNumber = ctx.payload.event.issue_number; - log.info(`🌿 Checking out PR #${prNumber}...`); - $("gh", ["pr", "checkout", prNumber.toString()], { - cwd: repoDir, - env: { GH_TOKEN: ctx.githubInstallationToken }, - }); - log.info(`✓ Successfully checked out PR #${prNumber}`); - - // check if this is a fork PR - gh pr checkout in Actions doesn't set up remotes for forks const pr = await ctx.octokit.rest.pulls.get({ owner: ctx.owner, repo: ctx.name, @@ -115,24 +104,23 @@ export async function setupGit(ctx: Context): Promise { }); const headRepo = pr.data.head.repo; - const baseRepo = pr.data.base.repo; - - // not a fork - push to origin - if (!headRepo || headRepo.full_name === baseRepo.full_name) { - return { pushRemote: "origin" }; + if (!headRepo) { + throw new Error(`PR #${prNumber} source repository was deleted`); } - // fork PR - return the full URL with auth token embedded - // git push accepts URLs directly, no remote needed - if (!pr.data.maintainer_can_modify) { - log.warning( - `⚠️ Fork PR from ${headRepo.owner.login} does not allow maintainer edits. Push may fail.` - ); - } + // checkout PR branch using plain git (no gh cli needed) + const branch = pr.data.head.ref; + log.info(`🌿 Checking out PR #${prNumber} (${branch})...`); + $("git", ["fetch", "origin", `pull/${prNumber}/head:${branch}`], { cwd: repoDir }); + $("git", ["checkout", branch], { cwd: repoDir }); + log.info(`✓ Successfully checked out PR #${prNumber}`); - // use GITHUB_TOKEN for fork push - it has the right permissions in Actions + // unified push URL - works for both same-repo and fork PRs const token = process.env.GITHUB_TOKEN || ctx.githubInstallationToken; - const forkUrl = `https://x-access-token:${token}@github.com/${headRepo.full_name}.git`; - log.info(`🍴 Fork PR detected, will push to: ${headRepo.full_name}`); - return { pushRemote: forkUrl }; + const pushUrl = `https://x-access-token:${token}@github.com/${headRepo.full_name}.git`; + const isFork = headRepo.full_name !== pr.data.base.repo.full_name; + if (isFork) { + log.info(`🍴 Fork PR detected, will push to: ${headRepo.full_name}`); + } + return { pushRemote: pushUrl }; }