Retire the Crucible container image
build-binaries / build-binaries (pull_request) Successful in 2m6s
test / test (pull_request) Successful in 1m21s

Nothing consumes registry.westgate.pw/deployment/crucible: prod.yml no
longer reserves a slot for it and no runtime or recovery path pulls it.
Binaries, wrappers, and the Nix input are the supported ways to run
Crucible. Delete the image workflows (build/publish/test), the
Dockerfile, and the make image target; update docs to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 08:48:23 +02:00
co-authored by Claude Fable 5
parent 4ee28fee4f
commit fab911d97a
8 changed files with 10 additions and 179 deletions
-52
View File
@@ -1,52 +0,0 @@
# Publish the immutable crucible:<sha> image on version tags.
# PR/main test builds live in test-image.yml and never publish, so registry
# packages are only generated for releases we intend to use (not on every merge).
#
# Daemonless: the host-mode runner has no container runtime, so the image is
# built by Nix (`nix build .#image`, see flake.nix) and pushed with skopeo
# straight from the OCI tarball. No `docker build`/`docker login` involved.
name: build-image
on:
push:
tags: ['v*']
env:
REGISTRY: registry.westgate.pw
IMAGE: deployment/crucible
jobs:
publish:
runs-on: nix-docker
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Resolve tag
id: tag
run: echo "sha=$(git rev-parse --short=12 HEAD)" >> "$GITHUB_OUTPUT"
- name: Build OCI image (daemonless)
run: nix build .#image
# This workflow only runs on v* tags, so every run is a release publish.
- name: Publish image
env:
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
run: |
nix shell nixpkgs#skopeo -c skopeo copy \
--dest-creds "${REGISTRY_USER}:${REGISTRY_PASSWORD}" \
docker-archive:result \
"docker://${REGISTRY}/${IMAGE}:${{ steps.tag.outputs.sha }}"
- name: Emit release fragment
run: |
FRAG_REPO=sow-tools \
FRAG_SHA=${{ steps.tag.outputs.sha }} \
FRAG_ARTIFACT="${REGISTRY}/${IMAGE}:${{ steps.tag.outputs.sha }}" \
FRAG_URL="${REGISTRY}/${IMAGE}" \
FRAG_RUN_ID=${{ gitea.run_id }} \
bash scripts/emit-release-fragment.sh
- uses: actions/upload-artifact@v3
with: { name: release-fragment, path: release-fragment.json }
-49
View File
@@ -1,49 +0,0 @@
# Manual compatibility publisher for the Crucible image.
# Normal release publishing happens in build-image.yml on v* tags; this is the
# break-glass / re-publish path, triggered by hand.
#
# Daemonless: built by Nix (`nix build .#image`) and pushed with skopeo from the
# OCI tarball. No `docker build`/`docker login` involved.
name: publish-image
on:
workflow_dispatch:
env:
REGISTRY: registry.westgate.pw
IMAGE: deployment/crucible
jobs:
publish:
runs-on: nix-docker
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Resolve tag
id: tag
run: echo "sha=$(git rev-parse --short=12 HEAD)" >> "$GITHUB_OUTPUT"
- name: Build OCI image (daemonless)
run: nix build .#image
- name: Publish image
env:
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
run: |
nix shell nixpkgs#skopeo -c skopeo copy \
--dest-creds "${REGISTRY_USER}:${REGISTRY_PASSWORD}" \
docker-archive:result \
"docker://${REGISTRY}/${IMAGE}:${{ steps.tag.outputs.sha }}"
- name: Emit release fragment
run: |
FRAG_REPO=sow-tools \
FRAG_SHA=${{ steps.tag.outputs.sha }} \
FRAG_ARTIFACT="${REGISTRY}/${IMAGE}:${{ steps.tag.outputs.sha }}" \
FRAG_URL="${REGISTRY}/${IMAGE}" \
FRAG_RUN_ID=${{ gitea.run_id }} \
bash scripts/emit-release-fragment.sh
- uses: actions/upload-artifact@v3
with: { name: release-fragment, path: release-fragment.json }
-19
View File
@@ -1,19 +0,0 @@
# Test-build the Crucible image on PRs. Proves `nix build .#image` still works
# (daemonless, Nix-built OCI tarball) but does NOT publish — release publishing
# happens in build-image.yml on v* tags. PR-only: with up-to-date-before-merge
# protection, main == the tested PR head, so a throwaway post-merge rebuild that
# publishes nothing is pure waste.
name: test-image
on:
pull_request:
jobs:
build-image:
runs-on: nix-docker
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Build OCI image (daemonless, no publish)
run: nix build .#image
+4 -4
View File
@@ -41,13 +41,14 @@ section and `.gitea/workflows/`.
- Dev loop: `nix develop`, then `make check` / `make build` / `make smoke` - Dev loop: `nix develop`, then `make check` / `make build` / `make smoke`
(see Commands below). (see Commands below).
- Release: push a `v*` tag. CI uploads cross-built binaries + wrappers to the - Release: push a `v*` tag. CI uploads cross-built binaries + wrappers to the
Gitea release and publishes the `crucible` container image. Gitea release. There is no container image; Crucible ships as binaries,
wrappers, and the Nix input.
- Consumers (they download released binaries via the wrapper; they never - Consumers (they download released binaries via the wrapper; they never
vendor a toolkit): vendor a toolkit):
- sow-module — https://git.westgate.pw/ShadowsOverWestgate/sow-module - sow-module — https://git.westgate.pw/ShadowsOverWestgate/sow-module
- sow-topdata — https://git.westgate.pw/ShadowsOverWestgate/sow-topdata - sow-topdata — https://git.westgate.pw/ShadowsOverWestgate/sow-topdata
- sow-assets-manifest — https://git.westgate.pw/ShadowsOverWestgate/sow-assets-manifest - sow-assets-manifest — https://git.westgate.pw/ShadowsOverWestgate/sow-assets-manifest
- sow-platform (deploys the released image/pins) — - sow-platform (infra/deploy authority) —
https://git.westgate.pw/ShadowsOverWestgate/sow-platform https://git.westgate.pw/ShadowsOverWestgate/sow-platform
## What this repo owns / does not own ## What this repo owns / does not own
@@ -62,7 +63,7 @@ is `sow-platform`).
1. **Fail closed, never fake.** A builder with no migrated logic yet (`depot`) 1. **Fail closed, never fake.** A builder with no migrated logic yet (`depot`)
exits `70`. Do not stub a builder to emit a placeholder artifact. exits `70`. Do not stub a builder to emit a placeholder artifact.
2. **Binaries are not committed.** They are CI artifacts / image layers. 2. **Binaries are not committed.** They are CI artifacts.
`/bin/`, `*.exe`, `nwn-tool`, `sow-toolkit` are gitignored. `/bin/`, `*.exe`, `nwn-tool`, `sow-toolkit` are gitignored.
3. **The registry is the command surface.** `internal/dispatch.Registry` is the 3. **The registry is the command surface.** `internal/dispatch.Registry` is the
single source of truth; keep it in sync with `cmd/` and single source of truth; keep it in sync with `cmd/` and
@@ -81,7 +82,6 @@ is `sow-platform`).
nix develop && make check # vet + test + shellcheck + yamllint nix develop && make check # vet + test + shellcheck + yamllint
make build # cmd/* -> ./bin make build # cmd/* -> ./bin
make smoke # assert fail-closed contract make smoke # assert fail-closed contract
make image # crucible:<sha>
``` ```
## Tests ## Tests
+1 -7
View File
@@ -1,6 +1,6 @@
SHELL := bash SHELL := bash
.ONESHELL: .ONESHELL:
.PHONY: check test vet build smoke fmt image .PHONY: check test vet build smoke fmt
# Lint + unit tests. Mirrors the `test` CI job; runs green inside `nix develop`. # Lint + unit tests. Mirrors the `test` CI job; runs green inside `nix develop`.
check: vet test check: vet test
@@ -22,9 +22,3 @@ smoke: build
fmt: fmt:
gofmt -l -w cmd internal gofmt -l -w cmd internal
# Build the Crucible image locally. Requires docker; mirrors build-image CI.
IMAGE ?= crucible
GIT_SHA ?= $(shell git rev-parse --short=12 HEAD 2>/dev/null || echo unknown)
image:
docker build --build-arg GIT_SHA=$(GIT_SHA) -f docker/Dockerfile -t $(IMAGE):$(GIT_SHA) .
+1 -6
View File
@@ -71,10 +71,9 @@ nix develop # Go + shellcheck + yamllint + make
make check # go vet + go test + shellcheck + yamllint make check # go vet + go test + shellcheck + yamllint
make build # build every cmd/* into ./bin (gitignored) make build # build every cmd/* into ./bin (gitignored)
make smoke # build + assert the fail-closed contract make smoke # build + assert the fail-closed contract
make image # docker build -> crucible:<sha>
``` ```
Binaries are **never committed** — they are CI artifacts / image layers (D19). Binaries are **never committed** — they are CI artifacts (D19).
This retires the old habit of checking in `nwn-tool` / `sow-toolkit`. This retires the old habit of checking in `nwn-tool` / `sow-toolkit`.
## CI ## CI
@@ -84,12 +83,8 @@ publish event is a `v*` tag (see `runbooks/ci-trigger-standard.md` in sow-docs,
https://git.westgate.pw/ShadowsOverWestgate/sow-docs). https://git.westgate.pw/ShadowsOverWestgate/sow-docs).
- `test.yml` — vet, test, shellcheck, yamllint, binary smoke (PR + main). - `test.yml` — vet, test, shellcheck, yamllint, binary smoke (PR + main).
- `test-image.yml` — build the OCI image to prove it compiles (PR + main, no push).
- `build-binaries.yml` — cross-build all targets (PR + main); on a `v*` tag, upload - `build-binaries.yml` — cross-build all targets (PR + main); on a `v*` tag, upload
the binaries, `SHA256SUMS`, and the wrappers to the Gitea release. the binaries, `SHA256SUMS`, and the wrappers to the Gitea release.
- `build-image.yml` — on a `v*` tag, build and publish
`registry.westgate.pw/deployment/crucible:<sha>`.
- `publish-image.yml` — manual `workflow_dispatch` break-glass republish.
- `sync-wrappers.yml` — on a `main` push that touches `wrappers/`, auto-PR the - `sync-wrappers.yml` — on a `main` push that touches `wrappers/`, auto-PR the
canonical wrappers to the consumer repos in `wrappers/consumers.txt`. canonical wrappers to the consumer repos in `wrappers/consumers.txt`.
Consumer drift checks run after those PRs merge to `main`, not on the PRs Consumer drift checks run after those PRs merge to `main`, not on the PRs
-38
View File
@@ -1,38 +0,0 @@
# syntax=docker/dockerfile:1
#
# Crucible toolchain image: registry.westgate.pw/deployment/crucible:<git-sha>
#
# Reproducible multi-stage build, no on-VPS build. Produces every cmd/* binary
# and ships them on a static base. The `crucible` dispatcher is the entrypoint;
# consumer CI can also call the standalone crucible-<name> binaries by path.
#
FROM golang:1.26-alpine AS build
WORKDIR /src
RUN apk add --no-cache git
# go.sum is committed now that the migrated packages pull golang.org/x/text and
# gopkg.in/yaml.v3; the glob keeps the build working if it is ever absent.
COPY go.mod go.sum* ./
RUN go mod download
COPY . .
ARG GIT_SHA=unknown
ENV CGO_ENABLED=0
RUN set -eux; \
mkdir -p /out; \
for dir in ./cmd/*/; do \
name="$(basename "${dir}")"; \
go build -trimpath \
-ldflags "-s -w -X git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/buildinfo.Version=${GIT_SHA}" \
-o "/out/${name}" "${dir}"; \
done
FROM debian:12-slim
# ca-certificates: builders fetch published manifests over HTTPS.
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates; \
rm -rf /var/lib/apt/lists/*; \
useradd --system --create-home --uid 65532 nonroot
COPY --from=build /out/ /usr/local/bin/
USER nonroot
ENTRYPOINT ["/usr/local/bin/crucible"]
CMD ["help"]
+4 -4
View File
@@ -49,10 +49,10 @@ by `flake.lock`. CI runs the _same_ `crucible <build>` as local dev, inside the
nix devshell (binary-cache fast). No build container, no token, no CI-only nix devshell (binary-cache fast). No build container, no token, no CI-only
pre-steps. pre-steps.
The `registry.westgate.pw/deployment/crucible:<sha>` image is **deployment-only**: The `registry.westgate.pw/deployment/crucible:<sha>` image is **retired**
`prod.yml` pins it so tools travel with the runtime host (`nwn.enable`), a (2026-07-17): nothing consumed it, `prod.yml` no longer reserves a slot for
disabled placeholder until the NWN stack lands. It is **not** a build tool and no it, and CI no longer builds or publishes it. Binaries, wrappers, and the Nix
build/CI job consumes it. input are the only supported ways to run Crucible.
## Determinism ## Determinism