diff --git a/.woodpecker/ci.yml b/.woodpecker/ci.yml index f5fc7729..5f021318 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 diff --git a/packages/mosaic/framework/skills/mosaic-gitea/SKILL.md b/packages/mosaic/framework/skills/mosaic-gitea/SKILL.md index fd1a8818..9ae65e62 100644 --- a/packages/mosaic/framework/skills/mosaic-gitea/SKILL.md +++ b/packages/mosaic/framework/skills/mosaic-gitea/SKILL.md @@ -11,13 +11,8 @@ Git operations via Mosaic wrapper scripts. Platform-aware (Gitea or GitHub). Scripts auto-detect platform from git remote. Run from inside the repo directory. -For force-merge (branch protection bypass): - -```bash -GITEA_TOKEN=$(cat ~/.config/mosaic/credentials/gitea.env | grep TOKEN | cut -d= -f2) -``` - -Or use the credentials loader: +Credentials come from the framework credentials loader (never from a shared env +file): ```bash source ~/.config/mosaic/tools/_lib/credentials.sh @@ -86,14 +81,10 @@ cd ~/src/ ~/.config/mosaic/tools/git/pr-merge.sh -n -d ``` -**Force-merge bypassing branch protection:** - -```bash -GITEA_TOKEN=$(cat ~/.config/mosaic/credentials/gitea.env | grep TOKEN | cut -d= -f2) -curl -X POST "https://git.mosaicstack.dev/api/v1/repos///pulls//merge" \ - -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" \ - -d '{"Do":"squash","force_merge":true}' -``` +Branch protection is a gate, not an obstacle: if it blocks a merge, fix the cause — +a failing check, a moved head, or a missing review. Never bypass it with a raw +API call, a shared credential, or `force_merge`. Exceptional cases go to the +operator or the coordinating seat, still merged through the wrapper. ## Notes