From 2cbb89612c01fbd55f1d89a04c4e3ce472ab4af0 Mon Sep 17 00:00:00 2001 From: pikastech Date: Tue, 21 Jul 2026 08:32:46 +0200 Subject: [PATCH 1/4] fix(cicd): preserve heredocs in bounded build workers --- .../hwlab/runtime-pipeline-resource-governance.mjs | 4 +++- ...latform-infra-pipelines-as-code-bootstrap.test.ts | 12 ++++++++++++ scripts/src/platform-infra-pipelines-as-code.ts | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/scripts/native/hwlab/runtime-pipeline-resource-governance.mjs b/scripts/native/hwlab/runtime-pipeline-resource-governance.mjs index 01a5abc5..2a340ffd 100644 --- a/scripts/native/hwlab/runtime-pipeline-resource-governance.mjs +++ b/scripts/native/hwlab/runtime-pipeline-resource-governance.mjs @@ -49,7 +49,9 @@ function applyPipelineBuildQueue(doc) { 'build_service() {', ' service_id="$1"', ' mkdir -p "/workspace/service-work/$service_id" "/workspace/service-results-tekton/$service_id"', - workerScript.split('\n').map((line) => ` ${line}`).join('\n'), + // Keep the worker body at column zero: POSIX heredoc terminators in the + // generated publish script must not be indented by the function wrapper. + workerScript, '}', `max_parallel=${maxParallel}`, 'service_csv="$(params.services)"', diff --git a/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts b/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts index 349c3975..acf983f3 100644 --- a/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts +++ b/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts @@ -282,6 +282,18 @@ test("PaC automatic webhook disable is target-scoped and does not touch Kubernet expect(action).not.toContain("ensure_webhook"); }); +test("PaC top-level help enumerates every exposed manual-release control", async () => { + const help = await runPlatformInfraPipelinesAsCodeCommand({} as never, ["--help"]); + expect(String((help as any).command)).toContain("automatic-webhooks"); + expect(JSON.stringify(help)).toContain("automatic-webhooks disable --target [--confirm]"); +}); + +test("bounded HWLAB build worker wrapper preserves heredoc terminators", () => { + const source = readFileSync(resolve(import.meta.dir, "../native/hwlab/runtime-pipeline-resource-governance.mjs"), "utf8"); + expect(source).toContain("workerScript,"); + expect(source).not.toContain("workerScript.split('\\n').map((line) => ` ${line}`).join('\\n')"); +}); + test("L2/L3 release exposes plan first and trigger only sends the PaC webhook", async () => { await expect(runPlatformInfraPipelinesAsCodeCommand({} as never, [ "release", "trigger", "--target", "NC01", "--consumer", "hwlab-nc01-v03", diff --git a/scripts/src/platform-infra-pipelines-as-code.ts b/scripts/src/platform-infra-pipelines-as-code.ts index 43df3ba4..af37b0e0 100644 --- a/scripts/src/platform-infra-pipelines-as-code.ts +++ b/scripts/src/platform-infra-pipelines-as-code.ts @@ -617,7 +617,7 @@ function help(scope: string | null): Record { }; } return { - command: "platform-infra pipelines-as-code plan|release|status|history|diagnose-regression|delivery-timing|delivery-observe|debug-step|source-artifact", + command: "platform-infra pipelines-as-code plan|release|automatic-webhooks|status|history|diagnose-regression|delivery-timing|delivery-observe|debug-step|source-artifact", configTruth: configLabel, usage: [ "bun scripts/cli.ts platform-infra pipelines-as-code plan --target JD01", From fbbc7b74ea370f7564d884bdc4ae95a0cbfc9b6a Mon Sep 17 00:00:00 2001 From: pikastech Date: Tue, 21 Jul 2026 08:44:52 +0200 Subject: [PATCH 2/4] docs: require L1 source provenance evidence --- .agents/skills/unidesk-devlevel/SKILL.md | 2 ++ docs/reference/dev-environment.md | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/.agents/skills/unidesk-devlevel/SKILL.md b/.agents/skills/unidesk-devlevel/SKILL.md index 7a883d81..a9cb7a6a 100644 --- a/.agents/skills/unidesk-devlevel/SKILL.md +++ b/.agents/skills/unidesk-devlevel/SKILL.md @@ -90,6 +90,8 @@ description: >- - 禁止随机端口、临时 URL、命令行覆盖和代码默认值。 - L1 验收以 native 服务为准: - lifecycle status 必须证明 API、Worker 和 Web 进程仍在运行且 health ready; + - lifecycle status 必须披露运行进程选中的源码 workspace 和 commit,并与本次修改所在的受控 workspace 一致; + - 源码 provenance 缺失或不一致时不得用页面结果判定修复通过,应先修复受控 CLI 可见性或按 owning YAML 重新选择源码; - CLI 必须通过 native `--over-api` 完成真实业务操作; - Web 必须通过 native Web 完成受影响页面和交互; - localhost 或 probe host 只要来自 owning YAML,就可以作为同 host L1 证据。 diff --git a/docs/reference/dev-environment.md b/docs/reference/dev-environment.md index a09afc19..dc3ac052 100644 --- a/docs/reference/dev-environment.md +++ b/docs/reference/dev-environment.md @@ -117,6 +117,11 @@ trans D601:/home/ubuntu/workspace/unidesk-dev git remote -v - 同 host 可使用 YAML probe host,跨 host 使用 YAML 固定 native host; - 公共入口状态独立记录,不能成为 L1 业务门禁; - L1 任务不进入 CI/CD、Kubernetes 和 public-edge 调查或操作。 +- L1 源码 provenance 是 lifecycle 状态的一部分: + - 聚合 `status` 应同时披露 owning YAML 固定 repo、实际选中的 source workspace、commit 和进程工作目录一致性; + - source workspace 可以是固定 workspace,也可以是 owning YAML 受控选择且属于固定 repo 的任务 worktree; + - 浏览器、CLI 或 smoke 命中其他 workspace 的旧代码时,结果只能证明旧运行面,不得作为当前修改的验收证据; + - 状态缺少这些字段属于受控 CLI 可见性缺陷,应在当前 L1 任务内修复或登记到既有 source-worktree 改进任务,禁止用裸进程探测固化第二套验收流程。 ## L1 受控 CLI 即时修复 From ab726ad5830d276f256095978d1f04a7aea8dd56 Mon Sep 17 00:00:00 2001 From: pikastech Date: Tue, 21 Jul 2026 08:47:28 +0200 Subject: [PATCH 3/4] chore: tighten NC01 registry retention --- config/unidesk-cli.yaml | 4 ++-- docs/reference/gc.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/config/unidesk-cli.yaml b/config/unidesk-cli.yaml index d559e21f..931c9af9 100644 --- a/config/unidesk-cli.yaml +++ b/config/unidesk-cli.yaml @@ -174,8 +174,8 @@ gc: - node22-alpine-bun-v1 - sidecar deletableTagPattern: ^[0-9a-f]{7,64}$ - keepPerRepo: 20 - minAgeHours: 48 + keepPerRepo: 5 + minAgeHours: 24 previewLimit: 20 estimateBlobLimit: 5000 automatic: diff --git a/docs/reference/gc.md b/docs/reference/gc.md index f5609142..d5e34624 100644 --- a/docs/reference/gc.md +++ b/docs/reference/gc.md @@ -58,6 +58,7 @@ - 无论成功或失败都恢复原副本数,并复核 Deployment readiness 与 `/v2/` endpoint。 - writer idle guard 只阻塞 YAML grace 窗口内的新任务,或仍有 `Pending`/`Running` Pod 的 Tekton run;超过 grace 且没有活动 Pod 的残留 run 只报告为 stale,不得永久阻塞维护。 - 每批 manifest 上限由 YAML `automatic.maxDeleteManifestsPerRun` 控制;即使自动策略关闭,人工确认态也复用该批量边界并在每批前重新计算保护闭包。 + - `keepPerRepo` 与 `minAgeHours` 只控制历史版本冗余;运行 workload refs、保护 tag/仓库和 digest closure 始终独立保护,不得因容量目标跳过。 - `--include-tool-caches`:本机和远端都必须是显式 opt-in,只清理固定 allowlist 的可重建 npm/npx/Bun 缓存;不得扩大成清理 `~/.npm`、`~/.bun`、`node_modules`、auth/config 或运行面依赖。 所有成功和失败输出都必须是 JSON。`plan` 必须标记 `dryRun=true`、`mutation=false`;`run` 必须要求 `--confirm` 并报告 `diskBefore`、`diskAfter`、`summary`、`results` 和 `protected`。远端 GC 可用 `--target-use-percent N` 显式表达目标根盘水位;`summary.target` 必须给出目标所需释放量、候选估算、预计水位、缺口和 `safeStop` 决策,避免靠人工心算判断是否应该继续扩大清理范围。 From 0c3efee932f91d8980242bf1bc405a87f415be08 Mon Sep 17 00:00:00 2001 From: pikastech Date: Tue, 21 Jul 2026 08:53:31 +0200 Subject: [PATCH 4/4] fix(cicd): isolate bounded worker result paths --- .../native/hwlab/runtime-pipeline-resource-governance.mjs | 7 ++++++- .../src/platform-infra-pipelines-as-code-bootstrap.test.ts | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/native/hwlab/runtime-pipeline-resource-governance.mjs b/scripts/native/hwlab/runtime-pipeline-resource-governance.mjs index 2a340ffd..a9561e80 100644 --- a/scripts/native/hwlab/runtime-pipeline-resource-governance.mjs +++ b/scripts/native/hwlab/runtime-pipeline-resource-governance.mjs @@ -43,12 +43,17 @@ function applyPipelineBuildQueue(doc) { let workerScript = publish.script .replaceAll('$(params.service-id)', '$service_id') .replaceAll('/workspace/service-work', '/workspace/service-work/$service_id') - .replaceAll('/tekton/results', '/workspace/service-results-tekton/$service_id'); + .replaceAll('"/tekton/results/" + name', 'process.env.HWLAB_TEKTON_RESULTS_DIR + "/" + name') + .replaceAll('--tekton-results-dir /tekton/results', '--tekton-results-dir "$HWLAB_TEKTON_RESULTS_DIR"'); + if (workerScript.includes('/tekton/results')) { + throw new Error(`CI resource governance task ${governance.matrixTaskName} contains an unsupported Tekton result path context`); + } publish.script = [ 'set -eu', 'build_service() {', ' service_id="$1"', ' mkdir -p "/workspace/service-work/$service_id" "/workspace/service-results-tekton/$service_id"', + ' export HWLAB_TEKTON_RESULTS_DIR="/workspace/service-results-tekton/$service_id"', // Keep the worker body at column zero: POSIX heredoc terminators in the // generated publish script must not be indented by the function wrapper. workerScript, diff --git a/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts b/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts index acf983f3..ee5cde54 100644 --- a/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts +++ b/scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts @@ -292,6 +292,10 @@ test("bounded HWLAB build worker wrapper preserves heredoc terminators", () => { const source = readFileSync(resolve(import.meta.dir, "../native/hwlab/runtime-pipeline-resource-governance.mjs"), "utf8"); expect(source).toContain("workerScript,"); expect(source).not.toContain("workerScript.split('\\n').map((line) => ` ${line}`).join('\\n')"); + expect(source).toContain('export HWLAB_TEKTON_RESULTS_DIR="/workspace/service-results-tekton/$service_id"'); + expect(source).toContain('process.env.HWLAB_TEKTON_RESULTS_DIR + "/" + name'); + expect(source).toContain('--tekton-results-dir "$HWLAB_TEKTON_RESULTS_DIR"'); + expect(source).toContain("contains an unsupported Tekton result path context"); }); test("L2/L3 release exposes plan first and trigger only sends the PaC webhook", async () => {