From 0757464fb63389c96138d66cb87cdaeb6584566e Mon Sep 17 00:00:00 2001 From: AgentRun Codex Date: Mon, 13 Jul 2026 02:54:31 +0000 Subject: [PATCH] fix: accept migration command arguments --- scripts/src/cicd-delivery-authority.test.ts | 11 +++++++++-- scripts/src/hwlab-node-web-sentinel-cicd.ts | 6 +++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/scripts/src/cicd-delivery-authority.test.ts b/scripts/src/cicd-delivery-authority.test.ts index 82acd31a..ba3e0ee8 100644 --- a/scripts/src/cicd-delivery-authority.test.ts +++ b/scripts/src/cicd-delivery-authority.test.ts @@ -323,11 +323,11 @@ describe("migrated CLI 执行 guard 与提示清理", () => { }); test("migration Job 只从目标容器读取多行 CLI envelope,忽略合并日志尾随结果", () => { - const verifyFailure = JSON.stringify({ ok: false, data: { ok: false, command: "web-probe sentinel migration verify", error: { code: "monitor-migration-pg-verify-mismatch", message: "verify saw {braces} and an escaped \"quote\"" } } }, null, 2); + const verifyFailure = JSON.stringify({ ok: false, command: "web-probe sentinel migration verify --node NC01 --lane v03 --json", error: { code: "monitor-migration-pg-verify-mismatch", message: "verify saw {braces} and an escaped \"quote\"" } }, null, 2); const importSuccess = JSON.stringify({ ok: true, data: { ok: true, command: "web-probe sentinel migration import" } }, null, 2); const probe = { failed: true, podPhase: "Failed", containers: [{ name: "verify", phase: "terminated", exitCode: 1, reason: "Error", message: "-" }], containerLogs: { verify: `verify prefix\n${verifyFailure}`, import: importSuccess }, logsTail: `${verifyFailure}\n${importSuccess}` }; const payload = migrationJobPayloadFromProbe(probe); - expect(payload).toMatchObject({ ok: false, command: "web-probe sentinel migration verify", error: { code: "monitor-migration-pg-verify-mismatch", message: "verify saw {braces} and an escaped \"quote\"" } }); + expect(payload).toMatchObject({ ok: false, command: "web-probe sentinel migration verify --node NC01 --lane v03 --json", error: { code: "monitor-migration-pg-verify-mismatch", message: "verify saw {braces} and an escaped \"quote\"" } }); const failure = migrationJobFailed("migration", { jobName: "job" }, "failed", payload, probe) as { projection?: Record }; expect(failure.projection?.failure).toMatchObject({ container: "verify", code: "monitor-migration-pg-verify-mismatch", message: "verify saw {braces} and an escaped \"quote\"" }); expect(failure.projection?.result).toBeUndefined(); @@ -339,6 +339,13 @@ describe("migrated CLI 执行 guard 与提示清理", () => { expect(migrationJobPayloadFromProbe({ succeeded: true, containers: [{ name: "verify", phase: "terminated", exitCode: 0 }], containerLogs: { verify: JSON.stringify({ ok: true, data: { ok: true, command: "web-probe sentinel migration verify" } }), import: importSuccess } })).toMatchObject({ ok: true, command: "web-probe sentinel migration verify" }); }); + test("migration Job 接受带参数的 direct export 包络并拒绝相似 command 前缀", () => { + const direct = (command: string) => JSON.stringify({ ok: false, command, error: { code: "monitor-migration-artifact-path-invalid", message: "export artifact is unavailable" } }, null, 2); + const failedExport = (command: string) => migrationJobPayloadFromProbe({ failed: true, containers: [{ name: "export", phase: "terminated", exitCode: 1 }], containerLogs: { export: direct(command) } }); + expect(failedExport("web-probe sentinel migration export --node NC01 --lane v03 --json")).toMatchObject({ ok: false, command: "web-probe sentinel migration export --node NC01 --lane v03 --json", error: { code: "monitor-migration-artifact-path-invalid" } }); + for (const command of ["web-probe sentinel migration export-other --json", "web-probe sentinel migration exportfoo --json", "web-probe sentinel migration import --json"]) expect(failedExport(command)).toMatchObject({ ok: false, error: { code: "monitor-migration-job-result-missing" } }); + }); + test("migration Job 业务失败、创建失败和超时均只返回安全 failure 投影", () => { const business = migrationJobFailed("migration", { jobName: "job" }, "result-failed", { ok: false, error: { message: "https://user:PW123@host failed", stack: "secret stack" } }, { podPhase: "Succeeded", logsTail: "Bearer abc" }) as { projection?: Record }; const createFailed = migrationJobFailed("migration", { jobName: "job" }, "create-failed", {}, { podPhase: "CreateFailed", logsTail: "DATABASE_URL=https://user:PW123@host/db" }) as { projection?: Record }; diff --git a/scripts/src/hwlab-node-web-sentinel-cicd.ts b/scripts/src/hwlab-node-web-sentinel-cicd.ts index 519df4ca..e8146a9b 100644 --- a/scripts/src/hwlab-node-web-sentinel-cicd.ts +++ b/scripts/src/hwlab-node-web-sentinel-cicd.ts @@ -263,10 +263,14 @@ export function migrationJobPayloadFromProbe(probe: Record): Re const expectedCommand = container === "export" ? "web-probe sentinel migration export" : container === "import" ? "web-probe sentinel migration import" : container === "verify" ? "web-probe sentinel migration verify" : null; const containerLogs = isRecord(probe.containerLogs) ? probe.containerLogs : {}; const payload = container === null ? {} : sentinelPayloadFromLogs(String(containerLogs[container] ?? "")); - if (expectedCommand !== null && payload.command === expectedCommand && (payload.ok === true || payload.ok === false)) return payload; + if (expectedCommand !== null && migrationPayloadCommandMatches(payload.command, expectedCommand) && (payload.ok === true || payload.ok === false)) return payload; return { ok: false, error: { code: "monitor-migration-job-result-missing", message: expectedCommand === null ? "migration Job failed without an identifiable terminated container result" : `migration Job ${container} container did not emit the expected structured result` }, valuesRedacted: true }; } +function migrationPayloadCommandMatches(value: unknown, expected: string): boolean { + return typeof value === "string" && (value === expected || value.startsWith(`${expected} `)); +} + function migrationFailedContainerName(probe: Record): string | null { const containers = Array.isArray(probe.containers) ? probe.containers.filter(isRecord) : []; return stringAtNullable(containers.find((container) => finiteNumberOrNull(container.exitCode) !== null && finiteNumberOrNull(container.exitCode) !== 0), "name");