Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| efb4ad186f | |||
| c2cedce1bc | |||
| e383dd33dd |
@@ -83327,7 +83327,7 @@ function query({
|
|||||||
// package.json
|
// package.json
|
||||||
var package_default = {
|
var package_default = {
|
||||||
name: "@pullfrog/action",
|
name: "@pullfrog/action",
|
||||||
version: "0.0.141",
|
version: "0.0.143",
|
||||||
type: "module",
|
type: "module",
|
||||||
files: [
|
files: [
|
||||||
"index.js",
|
"index.js",
|
||||||
@@ -98180,8 +98180,9 @@ function buildPullfrogFooter(params) {
|
|||||||
parts.push(`Using [${params.agent.displayName}](${params.agent.url})`);
|
parts.push(`Using [${params.agent.displayName}](${params.agent.url})`);
|
||||||
}
|
}
|
||||||
if (params.workflowRun) {
|
if (params.workflowRun) {
|
||||||
const { owner, repo, runId } = params.workflowRun;
|
parts.push(
|
||||||
parts.push(`[View workflow run](https://github.com/${owner}/${repo}/actions/runs/${runId})`);
|
`[View workflow run](https://github.com/${params.workflowRun.owner}/${params.workflowRun.repo}/actions/runs/${params.workflowRun.runId})`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (params.customParts) {
|
if (params.customParts) {
|
||||||
parts.push(...params.customParts);
|
parts.push(...params.customParts);
|
||||||
@@ -123147,19 +123148,19 @@ function PushBranchTool(ctx) {
|
|||||||
parameters: PushBranch,
|
parameters: PushBranch,
|
||||||
execute: execute(ctx, async ({ branchName, force }) => {
|
execute: execute(ctx, async ({ branchName, force }) => {
|
||||||
const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
|
const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
|
||||||
log.info(`Pushing branch ${branch} to ${remote}`);
|
const isUrl = remote.startsWith("https://");
|
||||||
|
const args3 = force ? ["push", "--force", ...isUrl ? [] : ["-u"], remote, branch] : ["push", ...isUrl ? [] : ["-u"], remote, branch];
|
||||||
|
log.info(`Pushing branch ${branch} to ${isUrl ? "(fork URL)" : remote}`);
|
||||||
if (force) {
|
if (force) {
|
||||||
log.warning(`Force pushing - this will overwrite remote history`);
|
log.warning(`Force pushing - this will overwrite remote history`);
|
||||||
$("git", ["push", "--force", "-u", remote, branch]);
|
|
||||||
} else {
|
|
||||||
$("git", ["push", "-u", remote, branch]);
|
|
||||||
}
|
}
|
||||||
|
$("git", args3);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
branch,
|
branch,
|
||||||
remote,
|
remote: isUrl ? "(fork URL)" : remote,
|
||||||
force,
|
force,
|
||||||
message: `Successfully pushed branch ${branch} to ${remote}`
|
message: `Successfully pushed branch ${branch}`
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -123858,10 +123859,9 @@ function setupGitConfig() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function setupGit(ctx) {
|
async function setupGit(ctx) {
|
||||||
const { githubInstallationToken: githubInstallationToken2, payload } = ctx;
|
|
||||||
const repoDir = process.cwd();
|
const repoDir = process.cwd();
|
||||||
log.info("\u{1F527} Setting up git configuration...");
|
log.info("\u{1F527} Setting up git authentication...");
|
||||||
try {
|
try {
|
||||||
execSync("git config --local --unset-all http.https://github.com/.extraheader", {
|
execSync("git config --local --unset-all http.https://github.com/.extraheader", {
|
||||||
cwd: repoDir,
|
cwd: repoDir,
|
||||||
@@ -123871,39 +123871,39 @@ function setupGit(ctx) {
|
|||||||
} catch {
|
} catch {
|
||||||
log.debug("No existing authentication headers to remove");
|
log.debug("No existing authentication headers to remove");
|
||||||
}
|
}
|
||||||
process.env.GH_TOKEN = githubInstallationToken2;
|
const originUrl = `https://x-access-token:${ctx.githubInstallationToken}@github.com/${ctx.owner}/${ctx.name}.git`;
|
||||||
$("git", ["config", "--local", "credential.helper", ""], { cwd: repoDir });
|
$("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir });
|
||||||
$("git", ["config", "--local", "--add", "credential.helper", "!gh auth git-credential"], {
|
log.info("\u2713 Updated origin URL with authentication token");
|
||||||
cwd: repoDir
|
if (ctx.payload.event.is_pr !== true || !ctx.payload.event.issue_number) {
|
||||||
});
|
|
||||||
log.info("\u2713 Configured gh as credential helper");
|
|
||||||
if (payload.event.is_pr !== true || !payload.event.issue_number) {
|
|
||||||
log.debug("Not a PR event, staying on default branch");
|
log.debug("Not a PR event, staying on default branch");
|
||||||
return { pushRemote: "origin" };
|
return { pushRemote: "origin" };
|
||||||
}
|
}
|
||||||
const prNumber = payload.event.issue_number;
|
const prNumber = ctx.payload.event.issue_number;
|
||||||
log.info(`\u{1F33F} Checking out PR #${prNumber}...`);
|
log.info(`\u{1F33F} Checking out PR #${prNumber}...`);
|
||||||
$("gh", ["pr", "checkout", prNumber.toString()], {
|
$("gh", ["pr", "checkout", prNumber.toString()], {
|
||||||
cwd: repoDir,
|
cwd: repoDir,
|
||||||
env: { GH_TOKEN: githubInstallationToken2 }
|
env: { GH_TOKEN: ctx.githubInstallationToken }
|
||||||
});
|
});
|
||||||
log.info(`\u2713 Successfully checked out PR #${prNumber}`);
|
log.info(`\u2713 Successfully checked out PR #${prNumber}`);
|
||||||
const pushRemote = detectPushRemote();
|
const pr = await ctx.octokit.rest.pulls.get({
|
||||||
if (pushRemote !== "origin") {
|
owner: ctx.owner,
|
||||||
log.info(`\u{1F374} Fork PR detected, will push to remote: ${pushRemote}`);
|
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" };
|
||||||
}
|
}
|
||||||
return { pushRemote };
|
if (!pr.data.maintainer_can_modify) {
|
||||||
}
|
log.warning(
|
||||||
function detectPushRemote() {
|
`\u26A0\uFE0F Fork PR from ${headRepo.owner.login} does not allow maintainer edits. Push may fail.`
|
||||||
try {
|
);
|
||||||
const branch = $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
|
|
||||||
const upstream = $("git", ["rev-parse", "--abbrev-ref", `${branch}@{upstream}`], {
|
|
||||||
log: false
|
|
||||||
});
|
|
||||||
return upstream.split("/")[0];
|
|
||||||
} catch {
|
|
||||||
return "origin";
|
|
||||||
}
|
}
|
||||||
|
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 };
|
||||||
}
|
}
|
||||||
|
|
||||||
// utils/timer.ts
|
// utils/timer.ts
|
||||||
@@ -123942,7 +123942,7 @@ async function main(inputs) {
|
|||||||
const partialCtx = await initializeContext(inputs, payload);
|
const partialCtx = await initializeContext(inputs, payload);
|
||||||
const ctx = partialCtx;
|
const ctx = partialCtx;
|
||||||
timer.checkpoint("initializeContext");
|
timer.checkpoint("initializeContext");
|
||||||
const { pushRemote } = setupGit(ctx);
|
const { pushRemote } = await setupGit(ctx);
|
||||||
ctx.pushRemote = pushRemote;
|
ctx.pushRemote = pushRemote;
|
||||||
timer.checkpoint("setupGit");
|
timer.checkpoint("setupGit");
|
||||||
await setupTempDirectory(ctx);
|
await setupTempDirectory(ctx);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export async function main(inputs: Inputs): Promise<MainResult> {
|
|||||||
const ctx = partialCtx as Context;
|
const ctx = partialCtx as Context;
|
||||||
timer.checkpoint("initializeContext");
|
timer.checkpoint("initializeContext");
|
||||||
|
|
||||||
const { pushRemote } = setupGit(ctx);
|
const { pushRemote } = await setupGit(ctx);
|
||||||
ctx.pushRemote = pushRemote;
|
ctx.pushRemote = pushRemote;
|
||||||
timer.checkpoint("setupGit");
|
timer.checkpoint("setupGit");
|
||||||
|
|
||||||
|
|||||||
+10
-6
@@ -152,20 +152,24 @@ export function PushBranchTool(ctx: Context) {
|
|||||||
execute: execute(ctx, async ({ branchName, force }) => {
|
execute: execute(ctx, async ({ branchName, force }) => {
|
||||||
const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
|
const branch = branchName || $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
|
||||||
|
|
||||||
log.info(`Pushing branch ${branch} to ${remote}`);
|
// skip -u flag when pushing to URL (can't set upstream without a remote name)
|
||||||
|
const isUrl = remote.startsWith("https://");
|
||||||
|
const args = force
|
||||||
|
? ["push", "--force", ...(isUrl ? [] : ["-u"]), remote, branch]
|
||||||
|
: ["push", ...(isUrl ? [] : ["-u"]), remote, branch];
|
||||||
|
|
||||||
|
log.info(`Pushing branch ${branch} to ${isUrl ? "(fork URL)" : remote}`);
|
||||||
if (force) {
|
if (force) {
|
||||||
log.warning(`Force pushing - this will overwrite remote history`);
|
log.warning(`Force pushing - this will overwrite remote history`);
|
||||||
$("git", ["push", "--force", "-u", remote, branch]);
|
|
||||||
} else {
|
|
||||||
$("git", ["push", "-u", remote, branch]);
|
|
||||||
}
|
}
|
||||||
|
$("git", args);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
branch,
|
branch,
|
||||||
remote,
|
remote: isUrl ? "(fork URL)" : remote,
|
||||||
force,
|
force,
|
||||||
message: `Successfully pushed branch ${branch} to ${remote}`,
|
message: `Successfully pushed branch ${branch}`,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@pullfrog/action",
|
"name": "@pullfrog/action",
|
||||||
"version": "0.0.141",
|
"version": "0.0.143",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"index.js",
|
"index.js",
|
||||||
|
|||||||
@@ -40,8 +40,9 @@ export function buildPullfrogFooter(params: BuildPullfrogFooterParams): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (params.workflowRun) {
|
if (params.workflowRun) {
|
||||||
const { owner, repo, runId } = params.workflowRun;
|
parts.push(
|
||||||
parts.push(`[View workflow run](https://github.com/${owner}/${repo}/actions/runs/${runId})`);
|
`[View workflow run](https://github.com/${params.workflowRun.owner}/${params.workflowRun.repo}/actions/runs/${params.workflowRun.runId})`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.customParts) {
|
if (params.customParts) {
|
||||||
|
|||||||
+38
-37
@@ -68,15 +68,13 @@ export type SetupGitResult = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Unified git setup: configures authentication and checks out PR branch if applicable.
|
* Unified git setup: configures authentication and checks out PR branch if applicable.
|
||||||
* Uses gh as credential helper so git push works with any remote (including forks).
|
* For fork PRs, returns a full URL to push to (since gh pr checkout doesn't set up remotes in Actions).
|
||||||
* For PR events, gh pr checkout sets up proper remote tracking.
|
* For same-repo PRs, returns "origin".
|
||||||
* Returns the remote to push to (detected from branch tracking after checkout).
|
|
||||||
*/
|
*/
|
||||||
export function setupGit(ctx: Context): SetupGitResult {
|
export async function setupGit(ctx: Context): Promise<SetupGitResult> {
|
||||||
const { githubInstallationToken, payload } = ctx;
|
|
||||||
const repoDir = process.cwd();
|
const repoDir = process.cwd();
|
||||||
|
|
||||||
log.info("🔧 Setting up git configuration...");
|
log.info("🔧 Setting up git authentication...");
|
||||||
|
|
||||||
// remove existing git auth headers that actions/checkout might have set
|
// remove existing git auth headers that actions/checkout might have set
|
||||||
try {
|
try {
|
||||||
@@ -89,49 +87,52 @@ export function setupGit(ctx: Context): SetupGitResult {
|
|||||||
log.debug("No existing authentication headers to remove");
|
log.debug("No existing authentication headers to remove");
|
||||||
}
|
}
|
||||||
|
|
||||||
// set GH_TOKEN in process.env so gh auth git-credential uses the installation token
|
// embed token directly in origin URL - simple and doesn't expose token in env
|
||||||
// (not the default GITHUB_TOKEN which has different permissions)
|
const originUrl = `https://x-access-token:${ctx.githubInstallationToken}@github.com/${ctx.owner}/${ctx.name}.git`;
|
||||||
process.env.GH_TOKEN = githubInstallationToken;
|
$("git", ["remote", "set-url", "origin", originUrl], { cwd: repoDir });
|
||||||
|
log.info("✓ Updated origin URL with authentication token");
|
||||||
// set up gh as credential helper - this makes git use GH_TOKEN for any remote
|
|
||||||
$("git", ["config", "--local", "credential.helper", ""], { cwd: repoDir });
|
|
||||||
$("git", ["config", "--local", "--add", "credential.helper", "!gh auth git-credential"], {
|
|
||||||
cwd: repoDir,
|
|
||||||
});
|
|
||||||
log.info("✓ Configured gh as credential helper");
|
|
||||||
|
|
||||||
// non-PR events: stay on default branch, push to origin
|
// non-PR events: stay on default branch, push to origin
|
||||||
if (payload.event.is_pr !== true || !payload.event.issue_number) {
|
if (ctx.payload.event.is_pr !== true || !ctx.payload.event.issue_number) {
|
||||||
log.debug("Not a PR event, staying on default branch");
|
log.debug("Not a PR event, staying on default branch");
|
||||||
return { pushRemote: "origin" };
|
return { pushRemote: "origin" };
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkout PR branch - gh pr checkout handles fork remotes and tracking automatically
|
// checkout PR branch
|
||||||
const prNumber = payload.event.issue_number;
|
const prNumber = ctx.payload.event.issue_number;
|
||||||
log.info(`🌿 Checking out PR #${prNumber}...`);
|
log.info(`🌿 Checking out PR #${prNumber}...`);
|
||||||
$("gh", ["pr", "checkout", prNumber.toString()], {
|
$("gh", ["pr", "checkout", prNumber.toString()], {
|
||||||
cwd: repoDir,
|
cwd: repoDir,
|
||||||
env: { GH_TOKEN: githubInstallationToken },
|
env: { GH_TOKEN: ctx.githubInstallationToken },
|
||||||
});
|
});
|
||||||
log.info(`✓ Successfully checked out PR #${prNumber}`);
|
log.info(`✓ Successfully checked out PR #${prNumber}`);
|
||||||
|
|
||||||
// detect the push remote from branch tracking (set by gh pr checkout)
|
// check if this is a fork PR - gh pr checkout in Actions doesn't set up remotes for forks
|
||||||
const pushRemote = detectPushRemote();
|
const pr = await ctx.octokit.rest.pulls.get({
|
||||||
if (pushRemote !== "origin") {
|
owner: ctx.owner,
|
||||||
log.info(`🍴 Fork PR detected, will push to remote: ${pushRemote}`);
|
repo: ctx.name,
|
||||||
}
|
pull_number: prNumber,
|
||||||
return { pushRemote };
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function detectPushRemote(): string {
|
const headRepo = pr.data.head.repo;
|
||||||
try {
|
const baseRepo = pr.data.base.repo;
|
||||||
const branch = $("git", ["rev-parse", "--abbrev-ref", "HEAD"], { log: false });
|
|
||||||
const upstream = $("git", ["rev-parse", "--abbrev-ref", `${branch}@{upstream}`], {
|
// not a fork - push to origin
|
||||||
log: false,
|
if (!headRepo || headRepo.full_name === baseRepo.full_name) {
|
||||||
});
|
return { pushRemote: "origin" };
|
||||||
// upstream is like "remote/branch", extract remote name
|
|
||||||
return upstream.split("/")[0];
|
|
||||||
} catch {
|
|
||||||
return "origin";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// use GITHUB_TOKEN for fork push - it has the right permissions in Actions
|
||||||
|
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 };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user