Fix github_actions iss

This commit is contained in:
Colin McDonnell
2026-01-08 11:54:19 -08:00
parent d30532979a
commit 9291ee5952
3 changed files with 1264 additions and 24 deletions
+1213 -6
View File
File diff suppressed because it is too large Load Diff
+35 -10
View File
@@ -49,11 +49,11 @@ function filterEnv(): Record<string, string> {
*/
function spawnSandboxed(
command: string,
options: { env: Record<string, string>; cwd: string },
options: { env: Record<string, string>; cwd: string }
): ChildProcess {
const stdio: ["ignore", "pipe", "pipe"] = ["ignore", "pipe", "pipe"];
const spawnOpts = { env: options.env, cwd: options.cwd, stdio, detached: true };
return process.env.GITHUB_ACTIONS === "true"
return process.env.CI === "true" // requires linux runner or this will fail.
? spawn("unshare", ["--pid", "--fork", "--mount-proc", "bash", "-c", command], spawnOpts)
: spawn("bash", ["-c", command], spawnOpts);
}
@@ -66,7 +66,11 @@ async function killProcessGroup(proc: ChildProcess): Promise<void> {
await new Promise((r) => setTimeout(r, 200));
process.kill(-proc.pid, "SIGKILL");
} catch {
try { proc.kill("SIGKILL"); } catch { /* already dead */ }
try {
proc.kill("SIGKILL");
} catch {
/* already dead */
}
}
}
@@ -88,24 +92,45 @@ The command runs in a bash shell with a filtered environment that excludes sensi
const cwd = params.working_directory ?? process.cwd();
const proc = spawnSandboxed(params.command, { env: filterEnv(), cwd });
let stdout = "", stderr = "", timedOut = false, exited = false;
proc.stdout?.on("data", (chunk: Buffer) => { stdout += chunk.toString(); });
proc.stderr?.on("data", (chunk: Buffer) => { stderr += chunk.toString(); });
let stdout = "",
stderr = "",
timedOut = false,
exited = false;
proc.stdout?.on("data", (chunk: Buffer) => {
stdout += chunk.toString();
});
proc.stderr?.on("data", (chunk: Buffer) => {
stderr += chunk.toString();
});
const timeoutId = setTimeout(async () => {
if (!exited) { timedOut = true; await killProcessGroup(proc); }
if (!exited) {
timedOut = true;
await killProcessGroup(proc);
}
}, timeout);
const exitCode = await new Promise<number | null>((resolve) => {
const done = (code: number | null) => { exited = true; clearTimeout(timeoutId); resolve(code); };
const done = (code: number | null) => {
exited = true;
clearTimeout(timeoutId);
resolve(code);
};
proc.on("exit", done);
proc.on("error", () => done(null));
});
let output = stderr ? (stdout ? `${stdout}\n${stderr}` : stderr) : stdout;
if (timedOut) output = output ? `${output}\n[timed out after ${timeout}ms]` : `[timed out after ${timeout}ms]`;
if (timedOut)
output = output
? `${output}\n[timed out after ${timeout}ms]`
: `[timed out after ${timeout}ms]`;
return { output: output.trim(), exit_code: exitCode ?? (timedOut ? 124 : -1), timed_out: timedOut };
return {
output: output.trim(),
exit_code: exitCode ?? (timedOut ? 124 : -1),
timed_out: timedOut,
};
}),
});
}
+16 -8
View File
@@ -1,8 +1,8 @@
import assert from "node:assert/strict";
import { createSign } from "node:crypto";
import * as core from "@actions/core";
import { throttling } from "@octokit/plugin-throttling";
import { Octokit } from "@octokit/rest";
import assert from "node:assert/strict";
import { createSign } from "node:crypto";
import { log } from "./cli.ts";
import { retry } from "./retry.ts";
@@ -47,8 +47,11 @@ interface RepositoriesResponse {
repositories: Repository[];
}
function isGitHubActionsEnvironment(): boolean {
return Boolean(process.env.GITHUB_ACTIONS);
function isOIDCAvailable(): boolean {
// OIDC requires both env vars to be set (only in real GitHub Actions with id-token permission)
return Boolean(
process.env.ACTIONS_ID_TOKEN_REQUEST_URL && process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN
);
}
async function acquireTokenViaOIDC(): Promise<string> {
@@ -236,7 +239,7 @@ async function acquireTokenViaGitHubApp(): Promise<string> {
}
async function acquireNewToken(): Promise<string> {
if (isGitHubActionsEnvironment()) {
if (isOIDCAvailable()) {
return await retry(() => acquireTokenViaOIDC(), { label: "token exchange" });
} else {
return await acquireTokenViaGitHubApp();
@@ -267,7 +270,10 @@ export async function setupGitHubInstallationToken() {
* Get the GitHub installation token from memory
*/
export function getGitHubInstallationToken(): string {
assert(githubInstallationToken, "GitHub installation token not set. Call setupGitHubInstallationToken first.");
assert(
githubInstallationToken,
"GitHub installation token not set. Call setupGitHubInstallationToken first."
);
return githubInstallationToken;
}
@@ -313,7 +319,9 @@ export function parseRepoContext(): RepoContext {
return { owner, name };
}
export type OctokitWithPlugins = InstanceType<ReturnType<typeof Octokit.plugin<typeof Octokit, [typeof throttling]>>>
export type OctokitWithPlugins = InstanceType<
ReturnType<typeof Octokit.plugin<typeof Octokit, [typeof throttling]>>
>;
export function createOctokit(token: string): OctokitWithPlugins {
// `OctokitWithPlugins` initialization based on https://github.com/actions/toolkit/blob/2506e78e82fbd2f9e94d63e75f5309118c8de1b1/packages/github/src/github.ts#L15-L22
@@ -328,6 +336,6 @@ export function createOctokit(token: string): OctokitWithPlugins {
onSecondaryRateLimit: (retryAfter, options, octokit, retryCount) => {
return retryCount <= 2;
},
}
},
});
}