






















@@ -2,6 +2,53 @@
2233import { execFileSync } from "node:child_process";
445+const DEFAULT_GITHUB_REPOSITORY = "openclaw/openclaw";
6+const RUN_JOBS_PAGE_SIZE = 20;
7+const RUN_JOBS_MAX_PAGES = 25;
8+const GH_JSON_RETRY_DELAYS_MS = [1_000, 3_000, 6_000];
9+10+function sleepSync(ms) {
11+Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
12+}
13+14+function parseJsonCommand(command, args, options = {}) {
15+let lastError;
16+for (let attempt = 0; attempt <= GH_JSON_RETRY_DELAYS_MS.length; attempt += 1) {
17+try {
18+return JSON.parse(
19+execFileSync(command, args, {
20+encoding: "utf8",
21+ ...options,
22+}),
23+);
24+} catch (error) {
25+lastError = error;
26+const message = error instanceof Error ? error.message : String(error);
27+const retryable = /HTTP 5\d\d|Server Error|ETIMEDOUT|ECONNRESET|EAI_AGAIN/u.test(message);
28+if (!retryable || attempt === GH_JSON_RETRY_DELAYS_MS.length) {
29+throw error;
30+}
31+sleepSync(GH_JSON_RETRY_DELAYS_MS[attempt]);
32+}
33+}
34+throw lastError;
35+}
36+37+function normalizeRunJob(job) {
38+return {
39+completedAt: job.completedAt ?? job.completed_at ?? null,
40+conclusion: job.conclusion ?? "",
41+databaseId: job.databaseId ?? job.id,
42+name: job.name,
43+startedAt: job.startedAt ?? job.started_at ?? null,
44+status: job.status ?? "",
45+};
46+}
47+48+export function collectRunJobsFromPages(pages) {
49+return pages.flatMap((page) => (Array.isArray(page.jobs) ? page.jobs.map(normalizeRunJob) : []));
50+}
51+552function parseTime(value) {
653if (!value || value === "0001-01-01T00:00:00Z") {
754return null;
@@ -216,15 +263,37 @@ function listRecentSuccessfulCiRuns(limit) {
216263}
217264218265function loadRun(runId) {
219-return JSON.parse(
220-execFileSync(
221-"gh",
222-["run", "view", runId, "--json", "status,conclusion,createdAt,updatedAt,jobs"],
223-{
224-encoding: "utf8",
225-},
226-),
227-);
266+const run = parseJsonCommand("gh", [
267+"run",
268+"view",
269+runId,
270+"--json",
271+"status,conclusion,createdAt,updatedAt",
272+]);
273+const repository = process.env.GITHUB_REPOSITORY || DEFAULT_GITHUB_REPOSITORY;
274+const pages = [];
275+let totalCount = null;
276+for (let page = 1; page <= RUN_JOBS_MAX_PAGES; page += 1) {
277+const payload = parseJsonCommand("gh", [
278+"api",
279+"-X",
280+"GET",
281+`repos/${repository}/actions/runs/${runId}/jobs?per_page=${RUN_JOBS_PAGE_SIZE}&page=${page}`,
282+]);
283+pages.push(payload);
284+const jobs = Array.isArray(payload.jobs) ? payload.jobs : [];
285+totalCount = typeof payload.total_count === "number" ? payload.total_count : totalCount;
286+if (
287+jobs.length === 0 ||
288+(totalCount !== null && collectRunJobsFromPages(pages).length >= totalCount)
289+) {
290+break;
291+}
292+}
293+return {
294+ ...run,
295+jobs: collectRunJobsFromPages(pages),
296+};
228297}
229298230299function summarizeJobs(run) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。