From c2fd4cb3f2a3fb3163c4d846e975625295f687c6 Mon Sep 17 00:00:00 2001 From: pikastech Date: Mon, 20 Jul 2026 19:09:25 +0200 Subject: [PATCH] fix: include kubectl in CI tools images --- .agents/skills/unidesk-cicd/SKILL.md | 7 ++++++- .../unidesk-cicd/references/platform-ops.md | 7 +++++++ config/hwlab-node-control-plane.yaml | 17 +++++++++++++---- docs/reference/cli.md | 7 ++++++- ...1060308-cicd-yaml-first-target-governance.md | 12 ++++++++++++ scripts/src/platform-infra-gitea.ts | 3 ++- 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/.agents/skills/unidesk-cicd/SKILL.md b/.agents/skills/unidesk-cicd/SKILL.md index c0f4711e..10e62b95 100644 --- a/.agents/skills/unidesk-cicd/SKILL.md +++ b/.agents/skills/unidesk-cicd/SKILL.md @@ -362,7 +362,12 @@ bun scripts/cli.ts hwlab nodes control-plane legacy-cicd --help - 自动链单步测不通时,应在 Git-backed YAML、controller 或源码中修复根因,并由修复 PR 合并产生的新正常 webhook/PaC 事件复测;不得用 branch-follower 写状态或补跑当前交付。 - CI/CD 排障中再次踩到已暴露的运行面坑、工具误用、镜像假设或状态可见性缺口时,必须先把长期规则写入本 skill/reference,再继续只读定位。 - CI/CD 运行面验收必须在目标 NODE/k8s 内计算短摘要;本机只用于源码阅读、编辑和静态合同测试,不能替代正常 PR merge 自动事件产生的目标运行面证据。 -- in-cluster/controller/native helper 不能假设镜像内存在 `kubectl` binary。目标 Pod/Job 内读取或写入 Kubernetes 对象必须走 serviceaccount token + Kubernetes HTTPS API 或已封装的 native helper;`kubectl` 只允许在 operator 侧受控 CLI/trans 边界作为 transport/debug 包装,不得进入正式 controller 状态读写链路。 +- CI tools image 默认必须包含 `kubectl`: + - `kubectl` 与其他构建工具统一由 owning YAML 的 tools image Dockerfile 声明并随镜像交付; + - 镜像构建必须执行 `kubectl version --client` 自检; + - 禁止在 PipelineRun、Pod 或 Job 启动后临时安装 `kubectl`; + - 使用 tools image 的有界 CI helper、cleanup 或 bootstrap 可以直接调用 `kubectl`; + - 产品业务镜像和长期 controller 不得仅因本规则新增对 `kubectl` 的运行时依赖。 - 一旦发现 CI/CD CLI 被误用且可能写入错误状态、产生伪证据或绕过目标运行面,必须立刻先把用法改成更符合直觉的公开入口并更新本 skill/reference,再继续验证或交付;不要只靠口头记忆、隐藏 flag、手动约定或后续小心来避免复发。内部 in-cluster 模式必须只由目标 k8s Job/Pod 调用,操作者从本机只能用公开入口提交目标侧 Job 或读取目标侧摘要。 - PaC migrated consumer 的旧手动 publish/debug 命令不得作为正式 closeout 引导;调查命令默认只能指向只读的 `platform-infra pipelines-as-code status|history`。`closeout` 仅保留只读兼容能力,不得作为主代理、子代理或操作者的 PR 合并后步骤。需要保留的旧 mutation 命令只能属于 YAML 明确的 legacy authority 或独立平台维护,不得标记或引导 manual recovery;单步确认的自动链缺陷必须回到 YAML/controller/源码修复。 - Secret 只通过 YAML sourceRef/targetKey 和受控 CLI 下发;输出只披露 presence/fingerprint。 diff --git a/.agents/skills/unidesk-cicd/references/platform-ops.md b/.agents/skills/unidesk-cicd/references/platform-ops.md index 45dd30b0..1bca3e21 100644 --- a/.agents/skills/unidesk-cicd/references/platform-ops.md +++ b/.agents/skills/unidesk-cicd/references/platform-ops.md @@ -88,6 +88,13 @@ bun scripts/cli.ts hwlab g14 tools-image build \ tools image 构建只在目标 host 和本地 registry 的受控路径中执行,不把 master server 当通用构建机。 +- 默认工具集: + - CI tools image 必须随镜像提供 `kubectl`; + - `kubectl` 必须由 owning YAML 的内联 Dockerfile 构建,不得在 PipelineRun、Pod 或 Job 启动后安装; + - Dockerfile 构建自检必须包含 `kubectl version --client`; + - 有界 CI helper、cleanup 和 bootstrap 可以直接调用镜像内的 `kubectl`; + - 产品业务镜像和长期 controller 不继承该运行时依赖。 + ## PipelineRun 清理 ```bash diff --git a/config/hwlab-node-control-plane.yaml b/config/hwlab-node-control-plane.yaml index 0d8d933b..d16d6386 100644 --- a/config/hwlab-node-control-plane.yaml +++ b/config/hwlab-node-control-plane.yaml @@ -523,6 +523,8 @@ targets: filename: hwlab-ci-node-tools.public.Dockerfile lines: - FROM docker.io/library/golang:1.24-bookworm AS golang-toolchain + - FROM docker.io/docker:29-cli AS docker-cli + - RUN apk add --no-cache kubectl - FROM 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1 - ARG HTTP_PROXY - ARG HTTPS_PROXY @@ -533,11 +535,12 @@ targets: - ARG all_proxy - ARG no_proxy - COPY --from=golang-toolchain /usr/local/go /usr/local/go + - COPY --from=docker-cli /usr/bin/kubectl /usr/local/bin/kubectl - ENV PATH=/usr/local/go/bin:$PATH - RUN ln -sf /usr/local/bin/bun /usr/local/bin/bunx - ENV HWLAB_CI_NODE_DEPS=/opt/hwlab-ci-node-deps/node_modules - RUN set -eu; export HTTP_PROXY="${HTTP_PROXY:-${http_proxy:-}}"; export HTTPS_PROXY="${HTTPS_PROXY:-${https_proxy:-$HTTP_PROXY}}"; export ALL_PROXY="${ALL_PROXY:-${all_proxy:-}}"; export NO_PROXY="${NO_PROXY:-${no_proxy:-}}"; export http_proxy="$HTTP_PROXY"; export https_proxy="$HTTPS_PROXY"; export all_proxy="$ALL_PROXY"; export no_proxy="$NO_PROXY"; export npm_config_registry="https://registry.npmmirror.com/"; export BUN_CONFIG_REGISTRY="https://registry.npmmirror.com/"; export npm_config_noproxy="$NO_PROXY"; if [ -n "$HTTP_PROXY" ]; then export npm_config_proxy="$HTTP_PROXY"; fi; if [ -n "$HTTPS_PROXY" ]; then export npm_config_https_proxy="$HTTPS_PROXY"; fi; export npm_config_fetch_retries=2; export npm_config_fetch_retry_mintimeout=2000; export npm_config_fetch_retry_maxtimeout=16000; export npm_config_fetch_timeout=120000; proxy_label="${HTTP_PROXY:+HTTP_PROXY}"; proxy_label="${proxy_label:-none}"; mkdir -p /opt/hwlab-ci-node-deps; cd /opt/hwlab-ci-node-deps; printf '{"private":true,"dependencies":{}}\n' > package.json; ok=0; delay=2; for attempt in 1 2 3 4 5; do echo "{\"event\":\"tools-yaml-node-npm-install\",\"attempt\":\"$attempt/5\",\"registry\":\"$npm_config_registry\",\"proxy\":\"$proxy_label\"}" >&2; if timeout 180s npm install --package-lock=false --no-save --ignore-scripts --no-audit --no-fund --omit=dev yaml@2.8.3; then ok=1; break; fi; if [ "$attempt" = 5 ]; then break; fi; echo "{\"event\":\"tools-yaml-node-npm-install\",\"status\":\"retrying\",\"attempt\":\"$attempt/5\",\"sleepSeconds\":$delay}" >&2; sleep "$delay"; delay=$((delay * 2)); done; test "$ok" = 1; node --input-type=module -e 'import("/opt/hwlab-ci-node-deps/node_modules/yaml/browser/dist/index.js").then((yaml)=>console.log("yaml-ok", typeof yaml.parse))' - - RUN node --version && npm --version && bun --version && git --version && python3 --version && docker --version && ssh -V && go version + - RUN node --version && npm --version && bun --version && git --version && python3 --version && docker --version && kubectl version --client=true && ssh -V && go version buildArgs: {} buildNetwork: host publicBaseImages: @@ -729,6 +732,7 @@ targets: lines: - FROM docker.io/oven/bun:1.3.13 AS bun-runtime - FROM docker.io/docker:29-cli AS docker-cli + - RUN apk add --no-cache kubectl - FROM docker.io/library/golang:1.24-bookworm AS golang-toolchain - FROM docker.io/library/node:22-bookworm-slim AS node-runtime - FROM docker.io/library/python:3.12-bookworm @@ -745,12 +749,13 @@ targets: - COPY --from=node-runtime /usr/local/lib/node_modules /usr/local/lib/node_modules - COPY --from=bun-runtime /usr/local/bin/bun /usr/local/bin/bun - COPY --from=docker-cli /usr/local/bin/docker /usr/local/bin/docker + - COPY --from=docker-cli /usr/bin/kubectl /usr/local/bin/kubectl - ENV PATH=/usr/local/go/bin:$PATH - RUN ln -sf /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && ln -sf /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx - RUN ln -sf /usr/local/bin/bun /usr/local/bin/bunx - ENV HWLAB_CI_NODE_DEPS=/opt/hwlab-ci-node-deps/node_modules - RUN set -eu; export HTTP_PROXY="${HTTP_PROXY:-${http_proxy:-}}"; export HTTPS_PROXY="${HTTPS_PROXY:-${https_proxy:-$HTTP_PROXY}}"; export ALL_PROXY="${ALL_PROXY:-${all_proxy:-}}"; export NO_PROXY="${NO_PROXY:-${no_proxy:-}}"; export http_proxy="$HTTP_PROXY"; export https_proxy="$HTTPS_PROXY"; export all_proxy="$ALL_PROXY"; export no_proxy="$NO_PROXY"; export npm_config_registry="https://registry.npmmirror.com/"; export BUN_CONFIG_REGISTRY="https://registry.npmmirror.com/"; export npm_config_noproxy="$NO_PROXY"; if [ -n "$HTTP_PROXY" ]; then export npm_config_proxy="$HTTP_PROXY"; fi; if [ -n "$HTTPS_PROXY" ]; then export npm_config_https_proxy="$HTTPS_PROXY"; fi; export npm_config_fetch_retries=2; export npm_config_fetch_retry_mintimeout=2000; export npm_config_fetch_retry_maxtimeout=16000; export npm_config_fetch_timeout=120000; proxy_label="${HTTP_PROXY:+HTTP_PROXY}"; proxy_label="${proxy_label:-none}"; mkdir -p /opt/hwlab-ci-node-deps; cd /opt/hwlab-ci-node-deps; printf '{"private":true,"dependencies":{}}\n' > package.json; ok=0; delay=2; for attempt in 1 2 3 4 5; do echo "{\"event\":\"tools-yaml-node-npm-install\",\"attempt\":\"$attempt/5\",\"registry\":\"$npm_config_registry\",\"proxy\":\"$proxy_label\"}" >&2; if timeout 180s npm install --package-lock=false --no-save --ignore-scripts --no-audit --no-fund --omit=dev yaml@2.8.3; then ok=1; break; fi; if [ "$attempt" = 5 ]; then break; fi; echo "{\"event\":\"tools-yaml-node-npm-install\",\"status\":\"retrying\",\"attempt\":\"$attempt/5\",\"sleepSeconds\":$delay}" >&2; sleep "$delay"; delay=$((delay * 2)); done; test "$ok" = 1; node --input-type=module -e 'import("/opt/hwlab-ci-node-deps/node_modules/yaml/browser/dist/index.js").then((yaml)=>console.log("yaml-ok", typeof yaml.parse))' - - RUN node --version && npm --version && bun --version && git --version && python3 --version && docker --version && ssh -V && go version + - RUN node --version && npm --version && bun --version && git --version && python3 --version && docker --version && kubectl version --client=true && ssh -V && go version buildArgs: {} buildNetwork: host publicBaseImages: @@ -947,6 +952,7 @@ targets: lines: - FROM docker.io/oven/bun:1.3.13 AS bun-runtime - FROM docker.io/docker:29-cli AS docker-cli + - RUN apk add --no-cache kubectl - FROM docker.io/library/golang:1.24-bookworm AS golang-toolchain - FROM docker.io/library/node:22-bookworm-slim AS node-runtime - FROM docker.io/library/python:3.12-bookworm @@ -963,12 +969,13 @@ targets: - COPY --from=node-runtime /usr/local/lib/node_modules /usr/local/lib/node_modules - COPY --from=bun-runtime /usr/local/bin/bun /usr/local/bin/bun - COPY --from=docker-cli /usr/local/bin/docker /usr/local/bin/docker + - COPY --from=docker-cli /usr/bin/kubectl /usr/local/bin/kubectl - ENV PATH=/usr/local/go/bin:$PATH - RUN ln -sf /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && ln -sf /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx - RUN ln -sf /usr/local/bin/bun /usr/local/bin/bunx - ENV HWLAB_CI_NODE_DEPS=/opt/hwlab-ci-node-deps/node_modules - RUN set -eu; export HTTP_PROXY="${HTTP_PROXY:-${http_proxy:-}}"; export HTTPS_PROXY="${HTTPS_PROXY:-${https_proxy:-$HTTP_PROXY}}"; export ALL_PROXY="${ALL_PROXY:-${all_proxy:-}}"; export NO_PROXY="${NO_PROXY:-${no_proxy:-}}"; export http_proxy="$HTTP_PROXY"; export https_proxy="$HTTPS_PROXY"; export all_proxy="$ALL_PROXY"; export no_proxy="$NO_PROXY"; export npm_config_registry="https://registry.npmmirror.com/"; export BUN_CONFIG_REGISTRY="https://registry.npmmirror.com/"; export npm_config_noproxy="$NO_PROXY"; if [ -n "$HTTP_PROXY" ]; then export npm_config_proxy="$HTTP_PROXY"; fi; if [ -n "$HTTPS_PROXY" ]; then export npm_config_https_proxy="$HTTPS_PROXY"; fi; export npm_config_fetch_retries=2; export npm_config_fetch_retry_mintimeout=2000; export npm_config_fetch_retry_maxtimeout=16000; export npm_config_fetch_timeout=120000; proxy_label="${HTTP_PROXY:+HTTP_PROXY}"; proxy_label="${proxy_label:-none}"; mkdir -p /opt/hwlab-ci-node-deps; cd /opt/hwlab-ci-node-deps; printf '{"private":true,"dependencies":{}}\n' > package.json; ok=0; delay=2; for attempt in 1 2 3 4 5; do echo "{\"event\":\"tools-yaml-node-npm-install\",\"attempt\":\"$attempt/5\",\"registry\":\"$npm_config_registry\",\"proxy\":\"$proxy_label\"}" >&2; if timeout 180s npm install --package-lock=false --no-save --ignore-scripts --no-audit --no-fund --omit=dev yaml@2.8.3; then ok=1; break; fi; if [ "$attempt" = 5 ]; then break; fi; echo "{\"event\":\"tools-yaml-node-npm-install\",\"status\":\"retrying\",\"attempt\":\"$attempt/5\",\"sleepSeconds\":$delay}" >&2; sleep "$delay"; delay=$((delay * 2)); done; test "$ok" = 1; node --input-type=module -e 'import("/opt/hwlab-ci-node-deps/node_modules/yaml/browser/dist/index.js").then((yaml)=>console.log("yaml-ok", typeof yaml.parse))' - - RUN node --version && npm --version && bun --version && git --version && python3 --version && docker --version && ssh -V && go version + - RUN node --version && npm --version && bun --version && git --version && python3 --version && docker --version && kubectl version --client=true && ssh -V && go version buildArgs: {} buildNetwork: host publicBaseImages: @@ -1206,6 +1213,7 @@ targets: lines: - FROM docker.io/oven/bun:1.3.13 AS bun-runtime - FROM docker.io/docker:29-cli AS docker-cli + - RUN apk add --no-cache kubectl - FROM docker.io/library/golang:1.24-bookworm AS golang-toolchain - FROM docker.io/library/node:22-bookworm-slim AS node-runtime - FROM docker.io/library/python:3.12-bookworm @@ -1222,12 +1230,13 @@ targets: - COPY --from=node-runtime /usr/local/lib/node_modules /usr/local/lib/node_modules - COPY --from=bun-runtime /usr/local/bin/bun /usr/local/bin/bun - COPY --from=docker-cli /usr/local/bin/docker /usr/local/bin/docker + - COPY --from=docker-cli /usr/bin/kubectl /usr/local/bin/kubectl - ENV PATH=/usr/local/go/bin:$PATH - RUN ln -sf /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && ln -sf /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx - RUN ln -sf /usr/local/bin/bun /usr/local/bin/bunx - ENV HWLAB_CI_NODE_DEPS=/opt/hwlab-ci-node-deps/node_modules - RUN set -eu; unset HTTP_PROXY HTTPS_PROXY ALL_PROXY http_proxy https_proxy all_proxy; export npm_config_registry="https://registry.npmmirror.com/"; export BUN_CONFIG_REGISTRY="https://registry.npmmirror.com/"; export npm_config_fetch_retries=2; export npm_config_fetch_retry_mintimeout=2000; export npm_config_fetch_retry_maxtimeout=16000; export npm_config_fetch_timeout=120000; mkdir -p /opt/hwlab-ci-node-deps; cd /opt/hwlab-ci-node-deps; printf '{"private":true,"dependencies":{}}\n' > package.json; ok=0; delay=2; for attempt in 1 2 3 4 5; do echo "{\"event\":\"tools-yaml-node-npm-install\",\"attempt\":\"$attempt/5\",\"registry\":\"$npm_config_registry\",\"proxy\":\"none\"}" >&2; if timeout 180s npm install --package-lock=false --no-save --ignore-scripts --no-audit --no-fund --omit=dev yaml@2.8.3; then ok=1; break; fi; if [ "$attempt" = 5 ]; then break; fi; echo "{\"event\":\"tools-yaml-node-npm-install\",\"status\":\"retrying\",\"attempt\":\"$attempt/5\",\"sleepSeconds\":$delay}" >&2; sleep "$delay"; delay=$((delay * 2)); done; test "$ok" = 1; node --input-type=module -e 'import("/opt/hwlab-ci-node-deps/node_modules/yaml/browser/dist/index.js").then((yaml)=>console.log("yaml-ok", typeof yaml.parse))' - - RUN node --version && npm --version && bun --version && git --version && python3 --version && docker --version && ssh -V && go version + - RUN node --version && npm --version && bun --version && git --version && python3 --version && docker --version && kubectl version --client=true && ssh -V && go version buildArgs: {} buildNetwork: host publicBaseImages: diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 7c9174ea..a441a96a 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -191,7 +191,12 @@ PipelineRun 失败或长时间未完成时,先按定点 `control-plane status - `platform-infra observability plan|apply|status|validate|trace --target ` 是 `platform-infra` 内 OTel Collector 和 Tempo trace backend 的受控入口,`--target` 必须选择 trace 所属运行面节点;具体 trace 查询、噪声压制、Code Agent/AgentRun 排障和“先改进 OTel”的操作面统一见 `$unidesk-otel`(`.agents/skills/unidesk-otel/SKILL.md`)。 - `secrets plan|sync|status --config config/secrets-distribution.yaml --scope platform-infra` 是平台服务本地 Secret sourceRef 到 Kubernetes Secret key 的受控下发入口。`plan` 只读展示 sourceRef、必需 key、目标 Secret/key、missingKeys 和 fingerprint;`sync --confirm` 只按 YAML 声明创建允许生成的本地 key 并下发到声明的目标 Secret;`status` 只验证 live Secret key presence。该入口禁止从 live pod 或 Kubernetes Secret 反推密码、API key、JWT secret、n8n encryption key 或 `DATABASE_URL`,输出也不得打印 base64、解码值或远端 raw transcript;即使显式 `--raw` 也只返回脱敏 summary 和 raw omission 标记。LangBot/n8n Secret 轮换和缺 key 修复规则见 `docs/reference/platform-infra.md#secret-distribution-boundary`。 - `hwlab g14 observability status|apply|query|targets|boundary|closeout [--lane v02] [--promql ] [--expect-count N] [--expect-value V] [--dry-run|--confirm]` 是 G14 `devops-infra` 共享监控基础设施和 HWLAB v0.2 监控 closeout 的受控入口。`apply` 固定安装 Prometheus Operator `v0.91.0`、Prometheus `v3.12.0`、Prometheus 发现 RBAC、`devops-infra` 内 Prometheus 实例和 ClusterIP query Service,并给被允许发现的 workload namespace 打低风险 label;它不把 Prometheus、Grafana 或 Alertmanager 部署到 `hwlab-v02`,也不接管 HWLAB runtime Deployment/Service。`status` 只读汇总 CRD、operator Deployment、Prometheus CR/pod/service、`hwlab-v02` ServiceMonitor/PrometheusRule 和 bounded `up` 查询;`query` 只通过 Kubernetes service proxy 查询 Prometheus,支持 `--expect-count` / `--expect-value` 输出 `assertion`、bad values 和 missing/extra series;`targets` 汇总 ServiceMonitor/PrometheusRule、metrics sidecar readiness/restart、三层指标值和 `metrics.k8s.io` 当前 CPU/内存资源快照;`boundary` 验证 workload namespace 没有 Prometheus/Alertmanager,并对 `19666/19667` 公网 `/metrics` 做负向验证;`closeout` 聚合平台 ready、scrape reachable、sidecar serving、business health probe、resource snapshot、namespace boundary 和 public metrics exposure 语义结论。长期边界见 `docs/reference/g14-observability-infra.md`。 -- `hwlab g14 tools-image status|build --name ci-node-tools --tag [--dockerfile deploy/ci/hwlab-ci-node-tools.Dockerfile] [--dry-run|--confirm]` 是 G14 固定 HWLAB CI tools image 的受控 host build/push 入口;构建和 push 只发生在 G14 host 与本地 registry,不在 master server 构建,也不把 `apk add`/runtime install 塞进 Tekton PipelineRun。 +- CI tools image: + - legacy G14 入口为 `hwlab g14 tools-image status|build --name ci-node-tools --tag [--dockerfile deploy/ci/hwlab-ci-node-tools.Dockerfile] [--dry-run|--confirm]`; + - node-scoped 入口为 `hwlab nodes control-plane infra tools-image status|build|logs --node --lane `; + - 构建和 push 只发生在目标 host 与 node-local registry,不在 master server 构建; + - 镜像默认提供 `kubectl`,并在 Dockerfile 构建阶段执行 `kubectl version --client`; + - 禁止在 Tekton PipelineRun、Pod 或 Job 启动后临时安装工具。 - `trans gh:/owner/repo ...` 把 GitHub issue/PR 映射成只读、受控写入的虚拟文本目录: - `ls` 展示 PR / issue 目录和有界条目摘要;`cat` 直接读取一楼正文;`rg` 在正文内检索。 - `apply-patch` 使用默认 v2 虚拟文件 executor,把正文一楼映射为 `body.md`,写回仍走 `bun scripts/cli.ts gh issue/pr update` 的 guard/concurrency 规则。 diff --git a/project-management/PJ2026-01/specs/PJ2026-01060308-cicd-yaml-first-target-governance.md b/project-management/PJ2026-01/specs/PJ2026-01060308-cicd-yaml-first-target-governance.md index 0dfbcea2..552a204b 100644 --- a/project-management/PJ2026-01/specs/PJ2026-01060308-cicd-yaml-first-target-governance.md +++ b/project-management/PJ2026-01/specs/PJ2026-01060308-cicd-yaml-first-target-governance.md @@ -263,6 +263,18 @@ freshness 预判阻断 YAML-first 变更。入口应在停止服务后备份派 节点就绪或期望状态收敛失败才可使 apply 失败。不得重新引入凭据新旧 preflight、CI gate 或人工确认门禁。 +### 6.9 OPS-TARGET-REQ-009 CI 工具镜像默认能力 + +| 编号 | 短名 | 主责模块 | 关联模块 | +| --- | --- | --- | --- | +| OPS-TARGET-REQ-009 | 工具镜像能力 | PJ2026-01060308 YAML目标治理 | [发布流水](PJ2026-010601-controlled-release.md)、[平台运维](PJ2026-0106-platform-ops.md) | + +- CI tools image 必须默认包含 `kubectl`; +- `kubectl` 必须由 owning YAML 声明的 Dockerfile 在镜像构建阶段安装和自检; +- 使用 tools image 的有界 CI helper、cleanup 和 bootstrap 可以调用镜像内的 `kubectl`; +- 禁止在 PipelineRun、Pod 或 Job 启动后临时安装 `kubectl`; +- 该默认能力只属于 CI tools image,不得隐式扩大为产品业务镜像或长期 controller 的运行时依赖。 + ## 7. 过程控制 本规格的执行 issue 为 [#911](https://github.com/pikasTech/unidesk/issues/911)。源码文件头部应标注 `SPEC: PJ2026-01060308 cicd-yaml-targets draft-2026-06-25-cicd-yaml-targets`;自动生成文件、纯配置、锁文件和无法承载注释头的二进制产物可例外。 diff --git a/scripts/src/platform-infra-gitea.ts b/scripts/src/platform-infra-gitea.ts index 66b3dafe..b8cf2253 100644 --- a/scripts/src/platform-infra-gitea.ts +++ b/scripts/src/platform-infra-gitea.ts @@ -1217,7 +1217,7 @@ ${indentBlock(labels, 8)} containers: - name: cleanup image: ${cleanup.image} - imagePullPolicy: IfNotPresent + imagePullPolicy: Always command: ["kubectl"] args: - -n @@ -1226,6 +1226,7 @@ ${indentBlock(labels, 8)} - deployment/${cleanup.deploymentName} - secret/${cleanup.secretName} - --ignore-not-found + - --wait=false `; }