From 923667cc67e948eae552d70fe20e949542d11b25 Mon Sep 17 00:00:00 2001 From: ops-ci-01 Date: Wed, 19 Aug 2026 18:25:12 -0500 Subject: [PATCH] ci: pin ci-base to immutable lock-9cb7ffcd8828 (#1328) Every pipeline step ran from the mutable tag ci-base:latest, which the k8s backend resolves per-pod at pull time: the same tree and config could execute different images across runs, and no run recorded which image it ran. That is the uncontrolled variable left standing after #1324's measurement excluded agent version and concurrency (all 48 pipelines that day ran on agent 45). Pin to lock-9cb7ffcd8828, the immutable tag ci-image.yml pushes atomically with :latest. Byte-identical to what runs today by construction: last ci-image run was main 712c770 (2026-07-26), main's recipe files unchanged since, no later rebuild. Zero behavior change at pin time; drift becomes a deliberate, reviewed commit instead of a race. A wrong or missing tag fails loudly at image pull, which is the guard enforcing itself. Bump procedure and known limitations (lock-tag addresses the lockfile only; recipe changes on next do not rebuild the base until they reach main) are documented in the ci.yml header comment and issue #1328. --- .woodpecker/ci.yml | 16 +++++++++++++++- .woodpecker/publish.yml | 7 ++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.woodpecker/ci.yml b/.woodpecker/ci.yml index 35acff9e..d06f9903 100644 --- a/.woodpecker/ci.yml +++ b/.woodpecker/ci.yml @@ -2,8 +2,22 @@ # node:24-alpine + python3/make/g++/postgresql-client + pnpm + a warm pnpm # store. The install step resolves from the baked store (--prefer-offline) # instead of paying a ~731s cold fetch + native compile every run. +# +# PINNED to an immutable lock-tag (#1328, brain D27): ci-image.yml pushes +# lock- atomically with :latest, so the two are +# byte-identical at push time. A mutable :latest resolves per-pod at pull time +# on the k8s backend, which made CI verdicts non-reproducible (same tree, same +# config, different images across runs; see #1324 comment 23382/23386). The pin +# changes ONLY through reviewed commits; a wrong tag fails loudly at image pull. +# +# Bump procedure: when a recipe change (pnpm-lock.yaml / Dockerfile.ci) lands on +# main, ci-image.yml pushes lock-; a follow-up PR updates this anchor. +# Until then pipelines keep the old pin: reproducible, with the documented +# network-fallback lag (frozen-lockfile resolves missing packages from network). +# Known limitation: lock- addresses the lockfile only, so a Dockerfile-only +# change re-pushes the same tag with new content (#1328 follow-up: recipe-hash). variables: - - &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:latest' + - &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:lock-9cb7ffcd8828' - &enable_pnpm 'corepack enable' when: diff --git a/.woodpecker/publish.yml b/.woodpecker/publish.yml index 2249b1c1..418ab21f 100644 --- a/.woodpecker/publish.yml +++ b/.woodpecker/publish.yml @@ -18,7 +18,12 @@ variables: # Pre-baked CI base (see .woodpecker/ci-image.yml): node:24-alpine + # toolchain + warm pnpm store. Kills the second cold install publish pays. - - &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:latest' + # PINNED to the immutable lock-tag, not :latest (#1328, brain D27): a mutable + # tag resolves per-pod at pull time on the k8s backend and made CI verdicts + # non-reproducible (#1324). Byte-identical to :latest at pin time (pushed + # atomically by the same kaniko run, main 712c770, 2026-07-26). Bump only via + # reviewed PR, per the procedure in .woodpecker/ci.yml's header comment. + - &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:lock-9cb7ffcd8828' - &enable_pnpm 'corepack enable' # Heavy kaniko image builds (~25 min) — gate them so a merge that only touches # the npm-only CLI (@mosaicstack/mosaic) or docs does NOT rebuild the platform