diff --git a/docs/runbooks/production-ci-cd.md b/docs/runbooks/production-ci-cd.md index ad30679..794170f 100644 --- a/docs/runbooks/production-ci-cd.md +++ b/docs/runbooks/production-ci-cd.md @@ -16,7 +16,7 @@ 生产 CD 由部署仓的 root-owned dispatcher 执行。它在相同源码 SHA 上同时等待 `ci / verify (push)` 与本次新产生的 `release-ci / verify-tag (push)` 成功,然后只运行部署仓中固定的 BuildKit 构建、Trivy 镜像扫描、digest 发布、备份、迁移、健康检查和回滚命令;不得执行 Tag checkout 中的 `pnpm`、测试、shell 脚本或应用程序。 -provision 安装 checksum-pinned 的官方静态 ShellCheck `0.11.0`、Docker Compose `5.3.1`,并从固定 digest 的 Runner 镜像提取 Docker CLI `29.6.0` 后校验二进制 SHA-256;不会依赖宿主是否预装这些命令。所有工具都在精确 Node Job 镜像内实际执行版本检查。 +provision 安装 checksum-pinned 的官方静态 ShellCheck `0.11.0`、Docker Compose `5.3.1`,并从固定 digest 的 Runner 镜像提取 Docker CLI `29.6.0` 后校验二进制 SHA-256;不会依赖宿主是否预装这些命令。固定 digest 的 Runner 镜像已存在时可离线复用,缺失时才访问镜像仓库。所有工具都在精确 Node Job 镜像内实际执行版本检查。 ## 首次安装或修复 CI Runner diff --git a/scripts/provision-ci-runner.sh b/scripts/provision-ci-runner.sh index 8cf4848..eae66c7 100755 --- a/scripts/provision-ci-runner.sh +++ b/scripts/provision-ci-runner.sh @@ -192,7 +192,9 @@ GOBIN="$PREFIX/bin" GOPROXY="${GO_MODULE_PROXY:-https://goproxy.cn,direct}" \ "$PREFIX/toolchains/go/bin/go" install golang.org/x/vuln/cmd/govulncheck@v1.6.0 require_free_space "$MIN_FREE_GIB" "runner image pull" -docker pull "$RUNNER_IMAGE_SOURCE" +if ! docker image inspect "$RUNNER_IMAGE_SOURCE" >/dev/null 2>&1; then + docker pull "$RUNNER_IMAGE_SOURCE" +fi runner_runtime_image=$(docker image inspect --format '{{.Id}}' "$RUNNER_IMAGE_SOURCE") printf '%s\n' "$runner_runtime_image" | grep -Eq '^sha256:[0-9a-f]{64}$' || \ fail "pinned runner image did not resolve to an immutable local image ID" diff --git a/tests/ci/pipeline-test.sh b/tests/ci/pipeline-test.sh index a6edbe1..7a609fb 100755 --- a/tests/ci/pipeline-test.sh +++ b/tests/ci/pipeline-test.sh @@ -272,6 +272,7 @@ grep -Fq "RUNNER_IMAGE='$runner_image'" "$provision" grep -Fq 'RUNNER_IMAGE_SOURCE=${CI_RUNNER_IMAGE_SOURCE:-$RUNNER_IMAGE}' "$provision" grep -Fq 'runner_manifest_digest=${RUNNER_IMAGE##*@}' "$provision" grep -Fq 'CI_RUNNER_IMAGE_SOURCE must use the pinned runner manifest digest' "$provision" +grep -Fq 'if ! docker image inspect "$RUNNER_IMAGE_SOURCE"' "$provision" grep -Fq 'runner_runtime_image=$(docker image inspect' "$provision" grep -Fq 'RUNNER_IMAGE_REF="%s"' "$provision" grep -Fq "JOB_IMAGE='$job_image'" "$provision"