mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-12 04:02:53 +00:00
fix(release): bound candidate GitHub requests
This commit is contained in:
@@ -51,6 +51,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Release/CI/E2E: print heartbeat progress during centralized Docker builds while keeping successful build logs quiet.
|
||||
- Release/CI/E2E: avoid heartbeat-tail delays in Docker E2E log wrappers while reporting captured log bytes during long runs.
|
||||
- Release/CI/E2E: keep release user-journey logs and temporary plugin fixtures under per-run scratch roots so parallel runs cannot collide or leak artifacts.
|
||||
- Release/CI/E2E: bound release candidate GitHub API calls so stalled network requests cannot wedge workflow and artifact polling.
|
||||
- Control UI: lazy-load the usage view so the initial app bundle stays below the chunk warning threshold.
|
||||
- Build: keep Baileys optional image backends external so source builds do not warn about missing `jimp` or `sharp`.
|
||||
- Build: render independent CLI startup metadata help snapshots concurrently to cut cold build-all metadata time.
|
||||
|
||||
@@ -12,6 +12,7 @@ const DEFAULT_RELEASE_PROFILE = "beta";
|
||||
const DEFAULT_NPM_DIST_TAG = "beta";
|
||||
const DEFAULT_PLUGIN_SCOPE = "all-publishable";
|
||||
const DEFAULT_TELEGRAM_PROVIDER_MODE = "mock-openai";
|
||||
const DEFAULT_GITHUB_API_TIMEOUT_MS = 30_000;
|
||||
|
||||
function usage() {
|
||||
return `Usage: pnpm release:candidate -- --tag vYYYY.M.D-beta.N [options]
|
||||
@@ -182,15 +183,43 @@ function readJson(path, label) {
|
||||
}
|
||||
}
|
||||
|
||||
async function githubApi(path) {
|
||||
const token = run("gh", ["auth", "token"], { capture: true }).trim();
|
||||
const response = await fetch(`https://api.github.com/${path}`, {
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
},
|
||||
});
|
||||
function githubApiTimeoutMs() {
|
||||
const raw = process.env.OPENCLAW_RELEASE_CANDIDATE_GITHUB_API_TIMEOUT_MS;
|
||||
if (!raw) {
|
||||
return DEFAULT_GITHUB_API_TIMEOUT_MS;
|
||||
}
|
||||
const value = Number(raw);
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
throw new Error("OPENCLAW_RELEASE_CANDIDATE_GITHUB_API_TIMEOUT_MS must be a positive number");
|
||||
}
|
||||
return Math.trunc(value);
|
||||
}
|
||||
|
||||
function githubApiTimedOut(error) {
|
||||
return (
|
||||
error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")
|
||||
);
|
||||
}
|
||||
|
||||
export async function githubApi(path, options = {}) {
|
||||
const token = options.token ?? run("gh", ["auth", "token"], { capture: true }).trim();
|
||||
const timeoutMs = options.timeoutMs ?? githubApiTimeoutMs();
|
||||
let response;
|
||||
try {
|
||||
response = await (options.fetchImpl ?? fetch)(`https://api.github.com/${path}`, {
|
||||
signal: AbortSignal.timeout(timeoutMs),
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (githubApiTimedOut(error)) {
|
||||
throw new Error(`GitHub API ${path} timed out after ${timeoutMs}ms`, { cause: error });
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub API ${path} failed with ${response.status}: ${await response.text()}`);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
buildPublishCommand,
|
||||
githubApi,
|
||||
parseArgs,
|
||||
parseRunIdFromDispatchOutput,
|
||||
resolveArtifactName,
|
||||
@@ -125,4 +126,49 @@ describe("release candidate checklist", () => {
|
||||
),
|
||||
).toBe("openclaw-npm-preflight-dba00");
|
||||
});
|
||||
|
||||
it("bounds GitHub API requests with a timeout signal", async () => {
|
||||
const fetchImpl = vi.fn(async (_url: string, init?: RequestInit) => {
|
||||
expect(init?.signal).toBeInstanceOf(AbortSignal);
|
||||
expect(init?.headers).toMatchObject({
|
||||
Accept: "application/vnd.github+json",
|
||||
Authorization: "Bearer test-token",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
});
|
||||
return {
|
||||
ok: true,
|
||||
json: async () => ({ workflow_runs: [] }),
|
||||
};
|
||||
});
|
||||
|
||||
await expect(
|
||||
githubApi("repos/openclaw/openclaw/actions/runs", {
|
||||
fetchImpl,
|
||||
timeoutMs: 1234,
|
||||
token: "test-token",
|
||||
}),
|
||||
).resolves.toEqual({ workflow_runs: [] });
|
||||
expect(fetchImpl).toHaveBeenCalledWith(
|
||||
"https://api.github.com/repos/openclaw/openclaw/actions/runs",
|
||||
expect.objectContaining({
|
||||
signal: expect.any(AbortSignal),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("includes the GitHub API path when a request times out", async () => {
|
||||
const fetchImpl = vi.fn(async () => {
|
||||
throw new DOMException("request timed out", "TimeoutError");
|
||||
});
|
||||
|
||||
await expect(
|
||||
githubApi("repos/openclaw/openclaw/actions/runs/123/jobs", {
|
||||
fetchImpl,
|
||||
timeoutMs: 5,
|
||||
token: "test-token",
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
"GitHub API repos/openclaw/openclaw/actions/runs/123/jobs timed out after 5ms",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user