From 5224490d93214c31ec604f26a56663bc7442fe82 Mon Sep 17 00:00:00 2001 From: AgentRun Codex Date: Mon, 13 Jul 2026 04:17:21 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=9D=E7=95=99=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=20envelope=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/src/cicd-delivery-authority.test.ts | 10 +++++++++ .../src/hwlab-node-web-sentinel-cicd-jobs.ts | 21 ++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/scripts/src/cicd-delivery-authority.test.ts b/scripts/src/cicd-delivery-authority.test.ts index ba3e0ee8..62ca14f3 100644 --- a/scripts/src/cicd-delivery-authority.test.ts +++ b/scripts/src/cicd-delivery-authority.test.ts @@ -333,6 +333,16 @@ describe("migrated CLI 执行 guard 与提示清理", () => { expect(failure.projection?.result).toBeUndefined(); }); + test("migration Job 解包 pretty JSON outer command 和 nested data.error", () => { + const importFailure = JSON.stringify({ ok: false, command: "web-probe sentinel migration import --node NC01 --lane v03 --json", data: { ok: false, error: { code: "payload_hash_conflict", message: "payload hash conflicts with the existing import" } } }, null, 2); + const probe = { failed: true, podPhase: "Failed", containers: [{ name: "import", phase: "terminated", exitCode: 1, reason: "Error", message: "-" }], containerLogs: { import: `import prefix\n${importFailure}` }, logsTail: importFailure }; + const payload = migrationJobPayloadFromProbe(probe); + expect(payload).toMatchObject({ ok: false, command: "web-probe sentinel migration import --node NC01 --lane v03 --json", error: { code: "payload_hash_conflict", message: "payload hash conflicts with the existing import" } }); + const failure = migrationJobFailed("migration", { jobName: "job" }, "failed", payload, probe) as { projection?: Record }; + expect(failure.projection?.failure).toMatchObject({ container: "import", code: "payload_hash_conflict" }); + expect(String((failure.projection?.failure as Record)?.message)).toStartWith("payload hash conflicts with the existin"); + }); + test("migration Job 成功固定读取 verify,缺结构化 verify 结果显式失败", () => { const importSuccess = JSON.stringify({ ok: true, data: { ok: true, command: "web-probe sentinel migration import" } }, null, 2); expect(migrationJobPayloadFromProbe({ succeeded: true, containers: [{ name: "verify", phase: "terminated", exitCode: 0 }], containerLogs: { import: importSuccess }, logsTail: importSuccess })).toMatchObject({ ok: false, error: { code: "monitor-migration-job-result-missing" } }); diff --git a/scripts/src/hwlab-node-web-sentinel-cicd-jobs.ts b/scripts/src/hwlab-node-web-sentinel-cicd-jobs.ts index a19484ad..06d2ed56 100644 --- a/scripts/src/hwlab-node-web-sentinel-cicd-jobs.ts +++ b/scripts/src/hwlab-node-web-sentinel-cicd-jobs.ts @@ -983,19 +983,27 @@ export function sentinelPayloadFromLogs(logsTail: string): Record | null { let last: Record | null = null; - for (let start = text.indexOf("{"); start >= 0; start = text.indexOf("{", start + 1)) { + for (let start = text.indexOf("{"); start >= 0;) { const candidate = jsonObjectAt(text, start); - if (candidate !== null && (candidate.ok === true || candidate.ok === false)) last = candidate; + if (candidate === null) { + start = text.indexOf("{", start + 1); + continue; + } + if (candidate.value.ok === true || candidate.value.ok === false) last = candidate.value; + start = text.indexOf("{", candidate.end + 1); } return last; } -function jsonObjectAt(text: string, start: number): Record | null { +function jsonObjectAt(text: string, start: number): { value: Record; end: number } | null { let depth = 0; let inString = false; let escaped = false; @@ -1009,7 +1017,10 @@ function jsonObjectAt(text: string, start: number): Record | nu } if (char === "\"") { inString = true; continue; } if (char === "{") { depth += 1; continue; } - if (char === "}" && --depth === 0) return parseJsonObject(text.slice(start, index + 1)); + if (char === "}" && --depth === 0) { + const value = parseJsonObject(text.slice(start, index + 1)); + return value === null ? null : { value, end: index }; + } } return null; }