Skip to content

fix(release): stop the workflow re-releasing its own version bump - #532

Merged
rubenvdlinde merged 1 commit into
mainfrom
ci/release-self-retrigger-guard
Aug 21, 2026
Merged

fix(release): stop the workflow re-releasing its own version bump#532
rubenvdlinde merged 1 commit into
mainfrom
ci/release-self-retrigger-guard

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

The workflow releases its own release

This workflow ends by opening a pull request that brings the integration
branch up to the version it just tagged. Merging that pull request is a push
to the integration branch
, which triggers this workflow again, which bumps
the version again, which opens another pull request.

The bump commit carries [skip ci] — but the merge commit that actually
lands on the branch does not, so the marker never gets a chance to work.

Measured

opencatalogi, 2026-08-20, last 100 development commits:

kind count
chore(release): version bumps 45
merges of release/* branches 46
actual work 9

91% of the branch's history was the workflow reacting to itself, roughly
one cycle every ten minutes — and the base version never advanced. Every one
of those releases was 1.0.9-unstable.<timestamp>.

Why it hides

Each individual run is a legitimate release of a real commit. The only
symptom is that the branch never settles, which reads as "someone is
committing a lot" rather than as a defect.

The damage lands somewhere else entirely: a development branch that moves
every ten minutes cancels its own in-flight CI by concurrency.
opencatalogi's dev→beta PR had its E2E and Hydra Gates jobs cancelled
repeatedly for exactly this reason — and a cancelled job yields no verdict,
so the promotion could not be judged at all.

The guard

Skip the job when the triggering push is the bump landing, in either merge
style — a merge commit names the release/v* head branch, a squash produces
chore(release): <version> (#123).

Nothing is lost: those pushes contain only appinfo/info.xml (and
openapi.json where present), so re-releasing them builds a byte-identical
artefact under a new number.

workflow_dispatch is never skipped, so a human can always force a
release by hand — including of a commit this guard would refuse.

Verification

Truth table over real commit messages from opencatalogi's history,
asserting both directions:

message releases?
Merge pull request #955 from ConductionNL/release/v1.0.9-unstable… no
chore(release): 1.0.9-unstable.… [skip ci] no
chore(release): 1.0.9-unstable.… (#955) (squash) no
Merge pull request #928 from ConductionNL/feat/rotterdam-demo-rig yes
test(federation): prove the SSRF allowance… yes
Merge pull request #194 from ConductionNL/chore/nc-vue-2.8.2 yes
workflow_dispatch yes

All 7 correct. A guard that only proved it can skip would be
indistinguishable from one that skips everything, so the must-still-release
rows are the ones that matter.

This workflow ends by opening a pull request that brings the integration
branch up to the version it just tagged. Merging that pull request is a push
to the integration branch, which triggers this workflow again, which bumps
the version again, which opens another pull request.

The bump commit carries `[skip ci]`, but the MERGE COMMIT that actually lands
on the branch does not — so the marker never gets a chance to work.

Measured on opencatalogi, 2026-08-20, over its last 100 development commits:

    45  chore(release): version bumps
    46  merges of release/* branches
     9  actual work

91% of the branch's history was the workflow reacting to itself, one cycle
about every ten minutes, and the base version never advanced: every one of
those releases was 1.0.9-unstable.<timestamp>.

It hides well. Each run is a legitimate release of a real commit, so the only
symptom is that the branch never settles — which reads as "someone is
committing a lot" rather than as a defect. The damage shows up elsewhere: a
development branch that moves every ten minutes cancels its own in-flight CI
by concurrency, and opencatalogi's dev→beta pull request had its E2E and
Hydra Gates jobs cancelled repeatedly for exactly that reason. A cancelled
job yields no verdict, so the promotion could not be judged at all.

The guard skips the job when the triggering push IS the bump landing, in
either merge style: a merge commit names the `release/v*` head branch, and a
squash produces `chore(release): <version> (#123)`. Nothing is lost — those
pushes contain only appinfo/info.xml (and openapi.json where present), so
re-releasing them would build a byte-identical artefact under a new number.

`workflow_dispatch` is never skipped, so a human can still force a release by
hand, including of a commit this guard would refuse.

Verified with a truth table over REAL commit messages from opencatalogi's
history, asserting both directions rather than only the skip: the two bump
shapes and the squash shape skip, while `feat/rotterdam-demo-rig`, a plain
`test(federation):` commit and a `chore/nc-vue-2.8.2` dependency bump all
still release. A guard that only proved it can skip would be indisttinguishable
from one that skips everything.
@rubenvdlinde
rubenvdlinde merged commit 6877af2 into main Aug 21, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant