fix/semrel-parity-and-hardening #40
No reviewers
Labels
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
webgrip/workflows!40
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/semrel-parity-and-hardening"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The previous commit pinned what every release job installs. This deletes the install: the release jobs now run IN a toolchain image, built and released in webgrip/infrastructure (ops/docker/semantic-release{,-monorepo,-rust}), each from a committed package-lock.json. semantic-release node + git + yq + semantic-release 25 + @webgrip/semantic-release-config + plugins semantic-release-monorepo FROM the base, + semantic-release-monorepo semantic-release-rust FROM the base, + cargo + semantic-release-cargo They are built THERE and not here on purpose. Building them in this repo would have meant release jobs running in an image whose build is orchestrated by this repo's own reusable workflows — a circle with no answer to which came first, where a broken toolchain image cannot be rebuilt by the pipeline that needs it, and where someone tracing a failed release ends up back where they started. Images are artifacts; infrastructure builds the estate's artifacts and consumes this library like every other repo. ADR-0005 records that as a rejected option with its reason, so the next person to propose it finds the argument. What stays here is the CONTRACT: SEMREL_PREBAKED — an installed node_modules, with PATH and NODE_PATH into it. Not a new interface; it is the one the ci-runner's undocumented /opt/semrel bake already used, except that bake is sr24 against an sr25 config and never exported NODE_PATH, so a consumer's bare require('@webgrip/semantic-release-config') could not resolve and the fast path could not have worked as written. What this removes from every release, permanently: two npm installs and ~430 packages resolved from the network on the critical path, an audit summary nobody reads, a yq download-and-checksum, a node version check plus setup-node fallback, and an npmrc holding a release token in a $HOME that outlives the job. Setup goes from ~3m45s to ~0. The @webgrip scope reads anonymously, so the image path writes no npm credential anywhere. The yq business was the clearest symptom: downloading a binary and verifying its checksum is BUILD work, and it was being done on every release in every repo. Composites, measured against origin/main: semantic-release 10 steps -> 6, semantic-release-monorepo 5 -> 2 (one run block plus a summary). What is left is release semantics only — checkout, branch-tip alignment, baseline seed, the credential the public host needs, --repository-url, the run, the summary. rust-semantic-release goes 12 -> 5 and gains lines rather than losing them, because it never had the release semantics: no baseline seed, no --repository-url (its changelog links pointed at the in-cluster host), no push credential, and a `version` output reading an output that does not exist. The image is the path, not a requirement. A job run outside one still releases: the composites fall back to an install and say so with a :⚠️: — unlocked and visible rather than fatal. Consumers needing more than the image has (a prepareCmd shelling out to helm, php, the docker CLI) build FROM it and pass toolchain-image, which puts environment needs in an image instead of in ad-hoc job steps. Tagging follows infrastructure's per-image train: it publishes webgrip/<image>:<version> (plus :latest on a final release), so the reusables pin an exact version in the toolchain-image default with a renovate annotation watching it — exactly how techdocs-runner pins techdocs-builder. A toolchain upgrade is two reviewed commits, the lockfile there and the pin here, and never moves under a repo's feet. renovate.json gains the custom manager that makes that pin visible: Renovate's workflow manager pins `uses:` refs but is blind to an image tag handed to a job through an input default, so nothing would have watched the toolchain a release actually runs in. MERGE ORDER: the infrastructure PR first, and the base image published before the two derived ones. The reusables default to these images, so a missing one fails at the container pull. Verified: all three images build and were smoke-tested — semantic-release 25.0.8 resolvable, require('@webgrip/semantic-release-config') resolving from an arbitrary cwd (i.e. NODE_PATH works), yq 4.44.3, git, semantic-release-monorepo present only in the monorepo image, cargo 1.97.1 + semantic-release-cargo 2.4.2 only in the rust image. Both toolchain branches of both composites driven against stubbed npm/git/node: image path installs nothing, fallback warns and installs, and the monorepo path cleans up its fabricated package.json and removes the credential either way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>Two defects in the image-first composites, both of which only bite outside the container. The prebake guard tested for a FILE, not a toolchain: sr="${SEMREL_PREBAKED:-/nonexistent}/.bin/semantic-release" if [[ ! -x "$sr" ]]; then ...install... fi Every pre-ADR-0005 ci-runner in the pool also exports SEMREL_PREBAKED, pointing at a hand-pinned tree that is semantic-release 24 with no @webgrip/semantic-release-config and no NODE_PATH. That file is executable, so the guard would take the fast path and die at config load with MODULE_NOT_FOUND. ploeg never saw it because ploeg runs in the image; every repo that does not would have regressed from "installs and works" to "broken". Probing what the tree actually holds — semantic-release major, the shared config, and the variant plugin — makes the rollout order-free in both directions. An old runner installs and says why; a new runner or a container goes straight to the locked binary. It also gets the rust case right for free: a ci-runner baking the monorepo toolchain has no semantic-release-cargo, so a rust release on that host correctly falls through. Verified against all four toolchain shapes: no prebake -> install; ci-runner:1.2.0 (the sr24 landmine) -> rejected loudly, install; semantic-release:0.1.2 -> fast path; a ci-runner built from the toolchain image -> fast path. Second, this branch had dropped the monorepo composite's concurrent-release retry loop. It is load-bearing: Forgejo IGNORES strategy.max-parallel — infrastructure run 177 ran three Release jobs that all started at 18:48:57 under max-parallel: 1. Without the loop the behind-remote symptom exits 0 without publishing, which is how run 141 silently dropped two releases. Restored, including the log grep, since rc alone cannot see it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>2c5c68c9betoee06ca519d