fix: preserve empty prepublication inputs (#3139)

This commit is contained in:
Patrick Erichsen
2026-07-16 22:14:21 -07:00
committed by GitHub
parent 709a4b1dc5
commit 67e6413cf5
4 changed files with 82 additions and 24 deletions
@@ -95,11 +95,4 @@ jobs:
SECURITY_SCAN_WORKER_TOKEN: ${{ secrets.SECURITY_SCAN_WORKER_TOKEN }}
VIRUSTOTAL_API_KEY: ${{ secrets.VT_API_KEY }}
run: |
bun run publish:prepublication-worker -- \
--batch-limit "$PREPUBLICATION_CHECK_LIMIT" \
--max-jobs "$PREPUBLICATION_CHECK_MAX_JOBS" \
--max-runtime-minutes "$PREPUBLICATION_CHECK_MAX_RUNTIME_MINUTES" \
--attempt-id "$PREPUBLICATION_CHECK_ATTEMPT_ID" \
--kind "$PREPUBLICATION_CHECK_KIND" \
--slug "$PREPUBLICATION_CHECK_SLUG" \
--version "$PREPUBLICATION_CHECK_VERSION"
bun run publish:prepublication-worker
@@ -119,10 +119,11 @@ describe("pre-publication publish worker workflow", () => {
const runStep = steps.find((step) => step.name === "Run pre-publication publish worker");
expect(runStep?.run).toContain("bun run publish:prepublication-worker");
expect(runStep?.run).toContain('--attempt-id "$PREPUBLICATION_CHECK_ATTEMPT_ID"');
expect(runStep?.run).toContain('--kind "$PREPUBLICATION_CHECK_KIND"');
expect(runStep?.run).toContain('--slug "$PREPUBLICATION_CHECK_SLUG"');
expect(runStep?.run).toContain('--version "$PREPUBLICATION_CHECK_VERSION"');
expect(runStep?.run).not.toContain("--attempt-id");
expect(runStep?.run).not.toContain("--kind");
expect(runStep?.run).not.toContain("--slug");
expect(runStep?.run).not.toContain("--version");
expect(runStep?.run).not.toContain("--max-jobs");
expect(steps.find((step) => step.name === "Install ClawScan CLI")?.run).toContain(
"npm install -g @openclaw/clawscan@0.1.4",
);
@@ -8,6 +8,7 @@ import {
claimBatchDrainedQueue,
claimPrePublicationAttempt,
claimPrePublicationBatch,
parseArgs,
processPrePublicationBatch,
processPrePublicationAttempt,
runNativeClawScan,
@@ -50,6 +51,69 @@ const attempt = {
};
describe("pre-publication worker", () => {
it("treats empty scheduled recovery flags as absent", () => {
expect(
parseArgs(
[
"--batch-limit",
"2",
"--max-jobs",
"--max-runtime-minutes",
"8",
"--attempt-id",
"--kind",
"--slug",
"--version",
],
{},
),
).toEqual({
batchLimit: 2,
maxJobs: undefined,
maxRuntimeMs: 8 * 60 * 1000,
claimFilters: {
attemptId: undefined,
kind: undefined,
slug: undefined,
version: undefined,
},
});
});
it("parses populated targeted recovery inputs", () => {
expect(
parseArgs(
[
"--batch-limit",
"1",
"--max-jobs",
"1",
"--max-runtime-minutes",
"12",
"--attempt-id",
"publishAttempts:driver",
"--kind",
"skill",
"--slug",
"driver",
"--version",
"0.8.3",
],
{},
),
).toEqual({
batchLimit: 1,
maxJobs: 1,
maxRuntimeMs: 12 * 60 * 1000,
claimFilters: {
attemptId: "publishAttempts:driver",
kind: "skill",
slug: "driver",
version: "0.8.3",
},
});
});
it("forwards targeted recovery filters when claiming an attempt", async () => {
const client = {
action: vi.fn().mockResolvedValue(attempt),
+12 -12
View File
@@ -92,11 +92,11 @@ const MAX_TRUFFLEHOG_FINDINGS = 10;
const MAX_PUBLIC_SUMMARY_CHARS = 600;
const logger = createWorkerLogger({ name: "prepublication-worker" });
function parseArgs() {
const args = process.argv.slice(2);
export function parseArgs(args = process.argv.slice(2), env: NodeJS.ProcessEnv = process.env) {
const get = (name: string) => {
const index = args.indexOf(name);
return index === -1 ? undefined : args[index + 1];
const value = index === -1 ? undefined : args[index + 1];
return value && !value.startsWith("--") ? value : undefined;
};
const numberFrom = (value: string | undefined, fallback: number) => {
const parsed = Number(value);
@@ -107,28 +107,28 @@ function parseArgs() {
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
};
const optionalStringFrom = (value: string | undefined) => value?.trim() || undefined;
const kind = optionalStringFrom(get("--kind") ?? process.env.PREPUBLICATION_CHECK_KIND);
const kind = optionalStringFrom(get("--kind") ?? env.PREPUBLICATION_CHECK_KIND);
if (kind && kind !== "skill" && kind !== "package") {
throw new Error("--kind must be skill or package");
}
return {
batchLimit: numberFrom(
get("--batch-limit") ?? process.env.PREPUBLICATION_CHECK_LIMIT,
get("--batch-limit") ?? env.PREPUBLICATION_CHECK_LIMIT,
DEFAULT_BATCH_LIMIT,
),
maxJobs: optionalNumberFrom(get("--max-jobs") ?? process.env.PREPUBLICATION_CHECK_MAX_JOBS),
maxJobs: optionalNumberFrom(get("--max-jobs") ?? env.PREPUBLICATION_CHECK_MAX_JOBS),
maxRuntimeMs:
numberFrom(
get("--max-runtime-minutes") ?? process.env.PREPUBLICATION_CHECK_MAX_RUNTIME_MINUTES,
get("--max-runtime-minutes") ?? env.PREPUBLICATION_CHECK_MAX_RUNTIME_MINUTES,
DEFAULT_MAX_RUNTIME_MS / 60_000,
) * 60_000,
claimFilters: {
attemptId: optionalStringFrom(
get("--attempt-id") ?? process.env.PREPUBLICATION_CHECK_ATTEMPT_ID,
) as Id<"publishAttempts"> | undefined,
attemptId: optionalStringFrom(get("--attempt-id") ?? env.PREPUBLICATION_CHECK_ATTEMPT_ID) as
| Id<"publishAttempts">
| undefined,
kind: kind as "skill" | "package" | undefined,
slug: optionalStringFrom(get("--slug") ?? process.env.PREPUBLICATION_CHECK_SLUG),
version: optionalStringFrom(get("--version") ?? process.env.PREPUBLICATION_CHECK_VERSION),
slug: optionalStringFrom(get("--slug") ?? env.PREPUBLICATION_CHECK_SLUG),
version: optionalStringFrom(get("--version") ?? env.PREPUBLICATION_CHECK_VERSION),
} satisfies PrePublicationClaimFilters,
};
}