Spec: Split release-plz into separate pr / release jobs (v0.10.3)¶
- Repository:
gitlab.com/phpboyscout/cicd - Component touched:
release-plz - Release: patch —
fix:→ batched into v0.10.3
Problem¶
The release-plz component runs both release-plz subcommands as two
script lines in a single job:
script:
- release-plz release-pr --forge gitlab --git-token "$RELEASE_PLZ_TOKEN_RUNTIME"
- release-plz release --forge gitlab --git-token "$RELEASE_PLZ_TOKEN_RUNTIME"
release-plz release-pr mutates the working tree when an open
Release MR exists. In release_plz_core's release_pr command it
stashes, then reset_branch runs:
git checkout <release-mr-branch>
git reset --hard HEAD~<n>
git fetch <original-branch>
git rebase <original-branch>
After it returns, the working tree is checked out on the Release MR
branch, not the default branch. The second line, release-plz
release, then runs cargo metadata against that mutated tree. It reads
the pre-bump crate versions, concludes is_published(crate@<old>)
is true for every crate, logs <name> <version>: already published,
and exits 0 having published nothing — no crates.io upload, no
v{version} tag, no GitLab release, and so no downstream cargo-dist
tag pipeline.
Observed failure¶
On phpboyscout/rust-tool-base, Release MR !34 (unified bump to
0.5.2) was merged to main. The push pipeline's release-plz job:
- ran
release-pr, which opened a fresh (empty) follow-up Release MR !35 — proving the tree was mutated onto a release branch; - ran
releaseon that mutated tree, logged every crate as "already published", and published nothing.
0.5.2 reached main's Cargo.toml but never reached crates.io. A
local release-plz release --dry-run on a clean checkout of the same
commit correctly reported all crates as publishable — confirming the
mutated working tree, not the commit, is the cause.
Decision¶
Run the two subcommands as two separate jobs, each with its own
clean checkout of $CI_COMMIT_SHA:
release-plz:pr— runsrelease-plz release-pr. May mutate its own working tree; that tree is discarded when the job ends.release-plz:release— runsrelease-plz releaseon a pristine checkout, socargo metadatareports the merged, version-bumped tree and the publish fires.
Both inherit a hidden .release-plz-base job (extends:) carrying the
shared before_script (binstall bootstrap, branch re-attach,
authenticated origin), rules:, variables:, and dependencies: [].
This is the documented hidden-job + extends: pattern, and matches
upstream's release-plz GitLab component, which is itself split into
distinct pr / release jobs for exactly this reason.
Why not cd/git checkout - between the two lines¶
Restoring the branch after release-pr would un-mutate the tree, but
release-plz also leaves a stash and a moved HEAD; reconstructing the
exact pre-state in shell is brittle and re-couples to release-plz
internals. Separate jobs get a guaranteed-clean tree from GitLab's own
checkout, with no knowledge of what release-pr did.
Scope & non-goals¶
- In scope: the job structure of
templates/release-plz.ymland the matchingtests/release-plz/.gitlab-ci.ymlself-test override. No input shape changes — every existing input keeps its name, type, and default. - Behaviour change: the component now emits two jobs named
release-plz:prandrelease-plz:releaseinstead of one job namedrelease-plz. A consumer that referenced the old job name (e.g. inneeds:or an override) must update to the new names. Norust-tool- baseconsumer does; the only override is this repo's own self-test. - Non-goals: changing tokens, forge handling, the
extra_before_ scripthook, or the bootstrap. Not touching any other component.
Testing¶
tests/release-plz/.gitlab-ci.yml includes the component against a repo
with no Cargo.toml, so each release-plz invocation exits non-zero with
"not a Cargo workspace". The self-test override applies allow_failure:
{ exit_codes: [1, 2, 101, 128] } to both new jobs; any other exit
(broken image, bad interpolation, missing binary) still fails the
self-test. The self-test:release-plz trigger in the root pipeline runs
on any change to templates/release-plz.yml or tests/release-plz/**.
The real publish path is verified downstream: after this ships in
v0.10.3, rust-tool-base bumps its pin and re-runs the main pipeline,
which must produce the crates.io uploads, the per-crate tags, and the
GitLab releases that v0.5.2 missed.