mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 02:06:43 +00:00
fix(ci): bound npm resume GitHub lookups (#109982)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user