fix(ci): bound npm resume GitHub lookups (#109982)

This commit is contained in:
Alix-007
2026-07-18 09:29:34 +01:00
committed by GitHub
parent 9a7d615dfc
commit ccf55a43cd
3 changed files with 60 additions and 3 deletions
+16
View File
@@ -43,6 +43,22 @@ export function validateOpenClawNpmResumeRun(
input: OpenClawNpmResumeValidationInput,
): OpenClawNpmResumeIdentity;
export function runOpenClawNpmResumeGh(
args: string[],
params?: {
execFileSyncImpl?: (
command: string,
args: string[],
options: {
encoding: "utf8";
killSignal: "SIGKILL";
maxBuffer: number;
timeout: number;
},
) => string;
},
): string;
export function resolveOpenClawNpmResumeRun(options: {
repo: string;
runId: string;
+9 -3
View File
@@ -5,6 +5,9 @@ import { fileURLToPath } from "node:url";
const SHA_PATTERN = /^[a-f0-9]{40}$/u;
const RELEASE_PUBLISH_REF_PATTERN = /^release-publish\/([a-f0-9]{12})-([1-9][0-9]*)$/u;
const WORKFLOW_PATH = ".github/workflows/openclaw-npm-release.yml";
// Resume checks run during release recovery, so keep enough headroom for GitHub
// latency while preventing one stalled read from consuming the workflow budget.
const GH_COMMAND_TIMEOUT_MS = 60_000;
function fail(message) {
throw new Error(message);
@@ -100,14 +103,17 @@ export function validateOpenClawNpmResumeRun({
};
}
function defaultRunGh(args) {
return execFileSync("gh", args, {
export function runOpenClawNpmResumeGh(args, params = {}) {
const execFileSyncImpl = params.execFileSyncImpl ?? execFileSync;
return execFileSyncImpl("gh", args, {
encoding: "utf8",
killSignal: "SIGKILL",
maxBuffer: 32 * 1024 * 1024,
timeout: GH_COMMAND_TIMEOUT_MS,
});
}
export function resolveOpenClawNpmResumeRun({ repo, runId, runGh = defaultRunGh }) {
export function resolveOpenClawNpmResumeRun({ repo, runId, runGh = runOpenClawNpmResumeGh }) {
if (!/^[1-9][0-9]*$/u.test(runId)) {
fail("OpenClaw npm resume run id must be a positive integer.");
}
@@ -1,6 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import {
resolveOpenClawNpmResumeRun,
runOpenClawNpmResumeGh,
validateOpenClawNpmResumeRun,
} from "../../scripts/openclaw-npm-resume-run.mjs";
import type { OpenClawNpmResumeValidationInput } from "../../scripts/openclaw-npm-resume-run.mjs";
@@ -36,6 +37,40 @@ function fixture(
}
describe("openclaw npm resume run identity", () => {
it("bounds each GitHub lookup", () => {
const execFileSyncImpl = vi.fn(() => "result");
expect(
runOpenClawNpmResumeGh(["api", "repos/openclaw/openclaw/actions/runs/456"], {
execFileSyncImpl,
}),
).toBe("result");
expect(execFileSyncImpl).toHaveBeenCalledWith(
"gh",
["api", "repos/openclaw/openclaw/actions/runs/456"],
{
encoding: "utf8",
killSignal: "SIGKILL",
maxBuffer: 32 * 1024 * 1024,
timeout: 60_000,
},
);
});
it("propagates GitHub lookup timeouts", () => {
const timeoutError = Object.assign(new Error("spawnSync gh ETIMEDOUT"), {
code: "ETIMEDOUT",
});
expect(() =>
runOpenClawNpmResumeGh(["api", "repos/openclaw/openclaw/actions/runs/456"], {
execFileSyncImpl: () => {
throw timeoutError;
},
}),
).toThrow(timeoutError);
});
it("accepts a successful run bound to a signed main-reachable tooling tag", () => {
expect(validateOpenClawNpmResumeRun(fixture())).toEqual({
tagObjectSha: TAG_OBJECT_SHA,