fix(scripts): bound sync-labels GitHub CLI operations (#110463)

* fix(scripts): bound sync-labels GitHub CLI operations

* fix(scripts): bound sync-labels GitHub CLI operations

* fix(scripts): bound sync-labels GitHub CLI operations

* fix(scripts): bound label discovery requests

Co-authored-by: wanyongstar <wan.yong@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
wanyongstar
2026-07-18 08:39:21 +01:00
committed by GitHub
co-authored by Peter Steinberger
parent ff54749b1b
commit c20ece85f4
2 changed files with 56 additions and 1 deletions
+9 -1
View File
@@ -5,6 +5,8 @@ import { resolve } from "node:path";
import { parse } from "yaml";
import { resolveGitHubRepoFromOrigin } from "./lib/github-repo.ts";
const SYNC_LABELS_TIMEOUT_MS = 120_000;
type RepoLabel = {
name: string;
color?: string;
@@ -77,7 +79,11 @@ for (const label of missing) {
if (metadata.description) {
args.push("-f", `description=${metadata.description}`);
}
execFileSync("gh", args, { stdio: "inherit" });
execFileSync("gh", args, {
stdio: "inherit",
timeout: SYNC_LABELS_TIMEOUT_MS,
killSignal: "SIGKILL",
});
console.log(`Created label: ${label}`);
}
@@ -93,6 +99,8 @@ function resolveLabelMetadata(label: string): { color: string; description?: str
function fetchExistingLabels(repoLocal: string): Map<string, RepoLabel> {
const raw = execFileSync("gh", ["api", `repos/${repoLocal}/labels?per_page=100`, "--paginate"], {
encoding: "utf8",
timeout: SYNC_LABELS_TIMEOUT_MS,
killSignal: "SIGKILL",
});
const labels = JSON.parse(raw) as RepoLabel[];
return new Map(labels.map((label) => [label.name, label]));
+47
View File
@@ -0,0 +1,47 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const { execFileSyncMock } = vi.hoisted(() => ({
execFileSyncMock: vi.fn(),
}));
vi.mock("node:child_process", async () => {
const actual = await vi.importActual<typeof import("node:child_process")>("node:child_process");
return {
...actual,
execFileSync: execFileSyncMock,
};
});
describe("sync-labels", () => {
beforeEach(() => {
vi.resetModules();
execFileSyncMock.mockReset();
execFileSyncMock.mockImplementation((command: string, args?: readonly string[]) => {
if (command === "git") {
return "https://github.com/openclaw/openclaw.git\n";
}
if (command === "gh" && args?.some((arg) => arg.includes("/labels?"))) {
return "[]";
}
return "";
});
vi.spyOn(console, "log").mockImplementation(() => undefined);
});
afterEach(() => {
vi.restoreAllMocks();
});
it("bounds every GitHub CLI operation", async () => {
await import("../../scripts/sync-labels.ts");
const ghCalls = execFileSyncMock.mock.calls.filter(([command]) => command === "gh");
expect(ghCalls.length).toBeGreaterThan(1);
for (const [, , options] of ghCalls) {
expect(options).toMatchObject({
timeout: 120_000,
killSignal: "SIGKILL",
});
}
});
});