ci: CI-resolve mempalace-toolkit to a pinned SHA
mempalace-toolkit is the only dependency cloned in Dockerfile.base (all others live in the variant), so it bypassed the resolve-versions -> build-arg plumbing and its ref stayed a literal `main`. Because the base only rebuilds on a content hash, a toolkit-only fix would silently fail to land unless Dockerfile.base itself changed. Mirrors pi-devbox commit 4744f05, adapted to this repo: - resolve-versions: new mempalace_toolkit_ref output via the gitea commits API (first gitea call in this repo's CI; works unauthenticated, no secret). - base-decide: needs resolve-versions; fold the SHA into the base-tag hash so a moved toolkit forces a base rebuild (they no longer run in parallel). - build-base: needs resolve-versions; pass --build-arg MEMPALACE_TOOLKIT_REF. - Dockerfile.base: clone switched to SHA-capable git fetch + checkout FETCH_HEAD (git clone --branch <SHA> would fail). - docs lockstep: .gitea/README.md Step 1 (no longer "in parallel"), AGENTS.md Critical conventions, CHANGELOG Unreleased. base_tag now reflects a live gitea lookup; on API blip it falls back to `main`, triggering one extra rebuild, never a missed one. No new tag — lands on the next release or workflow_dispatch.
This commit is contained in:
+10
-1
@@ -75,7 +75,13 @@ The split-base architecture is what the `docker-publish-split.yml` workflow exer
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
### Step 1: `base-decide` (and `resolve-versions` in parallel)
|
||||
### Step 1: `resolve-versions`, then `base-decide`
|
||||
|
||||
**`resolve-versions`** resolves floating refs to concrete values: `omos_version`
|
||||
(npm `latest`) and `mempalace_toolkit_ref` (the `mempalace-toolkit` `main` HEAD
|
||||
resolved to a commit SHA via the gitea commits API). **`base-decide`** now
|
||||
**depends on `resolve-versions`** (they no longer run in parallel) because it
|
||||
folds `mempalace_toolkit_ref` into the base hash — see below.
|
||||
|
||||
**`base-decide`** computes a SHA-256 hash over the inputs that determine
|
||||
the base image's content:
|
||||
@@ -90,6 +96,9 @@ the base image's content:
|
||||
! -name '._*' \
|
||||
-print0 | sort -z | xargs -0 cat
|
||||
cat entrypoint.sh entrypoint-user.sh
|
||||
echo "$mempalace_toolkit_ref" # CI-resolved SHA; mempalace-toolkit is
|
||||
# cloned in Dockerfile.base, so a moved
|
||||
# toolkit must force a base rebuild
|
||||
} | sha256sum | cut -c1-12
|
||||
```
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ env:
|
||||
jobs:
|
||||
# ── Phase 1: decide whether base needs rebuilding ──────────────────
|
||||
base-decide:
|
||||
needs: [resolve-versions]
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
@@ -77,6 +78,10 @@ jobs:
|
||||
! -name '._*' \
|
||||
-print0 2>/dev/null | sort -z | xargs -0 cat 2>/dev/null
|
||||
cat entrypoint.sh entrypoint-user.sh
|
||||
# mempalace-toolkit is cloned in Dockerfile.base at a ref CI
|
||||
# resolves to a SHA; fold it in so base_tag changes when the
|
||||
# toolkit moves (otherwise a toolkit-only fix never lands).
|
||||
echo "${{ needs.resolve-versions.outputs.mempalace_toolkit_ref }}"
|
||||
} | sha256sum | cut -c1-12
|
||||
)
|
||||
BASE_TAG="base-${HASH}"
|
||||
@@ -121,6 +126,7 @@ jobs:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
outputs:
|
||||
omos_version: ${{ steps.resolve.outputs.omos_version }}
|
||||
mempalace_toolkit_ref: ${{ steps.resolve.outputs.mempalace_toolkit_ref }}
|
||||
steps:
|
||||
- name: Resolve omos version from npm registry
|
||||
id: resolve
|
||||
@@ -134,10 +140,24 @@ jobs:
|
||||
OMOS_VERSION=$(curl -sf "https://registry.npmjs.org/oh-my-opencode-slim/latest" | jq -r '.version')
|
||||
echo "omos_version=${OMOS_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "Resolved OMOS_VERSION=${OMOS_VERSION}"
|
||||
# Resolve mempalace-toolkit main HEAD to a commit SHA. Unlike omos
|
||||
# (an npm pkg baked into the VARIANT), mempalace-toolkit is cloned
|
||||
# in Dockerfile.base, so this SHA is ALSO folded into the
|
||||
# base-decide hash to force a base rebuild when the toolkit moves
|
||||
# (without it, a toolkit-only fix silently fails to land unless
|
||||
# Dockerfile.base itself changes). gitea allows unauthenticated
|
||||
# public-repo commit listing; the token header is harmless if the
|
||||
# env vars are unset (degrades to anon, still HTTP 200).
|
||||
MEMPALACE_TOOLKIT_REF=$(curl -sf -H "Authorization: token ${GITEA_BUILD_TOKEN:-${GITHUB_TOKEN:-}}" \
|
||||
"https://gitea.jordbo.se/api/v1/repos/joakimp/mempalace-toolkit/commits?limit=1&sha=main" \
|
||||
| jq -r '.[0].sha // "main"' 2>/dev/null || echo "main")
|
||||
[ -n "$MEMPALACE_TOOLKIT_REF" ] || MEMPALACE_TOOLKIT_REF=main
|
||||
echo "mempalace_toolkit_ref=${MEMPALACE_TOOLKIT_REF}" >> "$GITHUB_OUTPUT"
|
||||
echo "Resolved MEMPALACE_TOOLKIT_REF=${MEMPALACE_TOOLKIT_REF}"
|
||||
|
||||
# ── Phase 2: build & push base (multi-arch), only when needed ──────
|
||||
build-base:
|
||||
needs: [base-decide]
|
||||
needs: [base-decide, resolve-versions]
|
||||
if: needs.base-decide.outputs.need_build == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
@@ -185,6 +205,7 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
BASE_TAG_FULL: ${{ env.IMAGE }}:${{ needs.base-decide.outputs.base_tag }}
|
||||
MEMPALACE_TOOLKIT_REF: ${{ needs.resolve-versions.outputs.mempalace_toolkit_ref }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# 3-attempt retry around `docker buildx build --push` for transient
|
||||
@@ -205,6 +226,7 @@ jobs:
|
||||
if docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--file Dockerfile.base \
|
||||
--build-arg MEMPALACE_TOOLKIT_REF="${MEMPALACE_TOOLKIT_REF}" \
|
||||
--push \
|
||||
--tag "${BASE_TAG_FULL}" \
|
||||
.; then
|
||||
|
||||
Reference in New Issue
Block a user