Version collisions. Website-Backend #65/#66 (0.1.24) and again #80/#79 (0.1.32, today 18:42–18:50Z) merged minutes apart and both pipelines computed the same GitVersion version. The first pipeline rolled its image out. The second overwrote the image tag in GHCR. github-release used --target main, so the tag landed on whatever main pointed at by then. The GitOps bump was a no-op. Result: prod ran 2d7cb7a under v0.1.32 -> d212ec5, and GHCR 0.1.32 now holds a different image than prod.
cd_path check failure. The generator always rendered cd_path: into update_cd_repo. Repos whose .munitor.yml pins an older orb_version (Website-Backend/Frontend develop at 0.2, Concilium 0.2, makers4 0.1) fail with Unexpected argument(s): cd_path.
Fix
allocate_version (new, in mvn/gradle/node build_and_test on main): reserves vX.Y.Z by POST /git/refs. GitHub creates the ref only if it is absent, so concurrent pipelines serialize on it:
created: this commit owns the version
exists on this commit: a rerun, so reuse it
exists on another commit: take the next patch and retry
Existing tags are never moved. This runs before the version is baked into the jar or image.
guard_image_tags (new, before build/push in docker_build_push): every image now carries the org.opencontainers.image.revision label. The guard refuses to push a main or supplemental version tag that already exists for a different commit or without the label, and fails closed on registry errors.
update_cd_repo: on main, verifies vX.Y.Z points at CIRCLE_SHA1 before bumping GitOps, and fails loudly otherwise. The source commit is recorded in the GitOps commit body.
github_release: targets CIRCLE_SHA1 (not main), and refuses if the tag points elsewhere.
Generator: cd_path is wrapped in ##IF_CD_EXPLICIT_PATH##, so it is rendered only when cd.path is set. Packed scripts are regenerated.
No new orb parameters, so generated pipelines stay compatible.
Tests
tests/test_allocate_version.sh (12 tests): free version, rerun reuse, the #65/#66 collision gets the next patch, tags never move, annotated tags, multiple taken versions skipped, non-main/pre-release skipped, API errors and missing token fail. It also runs 8 concurrent allocators against a fake refs API with atomic create-if-absent (hard-link) semantics, which must yield 8 distinct contiguous versions, each tag owned by its committer, and concurrent reruns that create no extra tags. Mutation check: treating "already exists" as success reproduces the incident (all 8 get 0.1.24, 4 tests fail).
tests/test_release_guards.sh (13 tests): the registry guard with a fake docker (free, rerun, collision, supplemental collision, unlabeled, registry error), step ordering in jobs, release target, CD ownership check, and cd_path omitted without cd.path but kept with it.
## Problem
**Version collisions.** Website-Backend #65/#66 (0.1.24) and again #80/#79 (0.1.32, today 18:42–18:50Z) merged minutes apart and both pipelines computed the same GitVersion version. The first pipeline rolled its image out. The second overwrote the image tag in GHCR. `github-release` used `--target main`, so the tag landed on whatever `main` pointed at by then. The GitOps bump was a no-op. Result: prod ran `2d7cb7a` under `v0.1.32 -> d212ec5`, and GHCR `0.1.32` now holds a different image than prod.
**`cd_path` check failure.** The generator always rendered `cd_path:` into `update_cd_repo`. Repos whose `.munitor.yml` pins an older `orb_version` (Website-Backend/Frontend `develop` at 0.2, Concilium 0.2, makers4 0.1) fail with `Unexpected argument(s): cd_path`.
## Fix
- **`allocate_version`** (new, in mvn/gradle/node `build_and_test` on `main`): reserves `vX.Y.Z` by `POST /git/refs`. GitHub creates the ref only if it is absent, so concurrent pipelines serialize on it:
- created: this commit owns the version
- exists on this commit: a rerun, so reuse it
- exists on another commit: take the next patch and retry
Existing tags are never moved. This runs before the version is baked into the jar or image.
- **`guard_image_tags`** (new, before build/push in `docker_build_push`): every image now carries the `org.opencontainers.image.revision` label. The guard refuses to push a main or supplemental version tag that already exists for a different commit or without the label, and fails closed on registry errors.
- **`update_cd_repo`**: on `main`, verifies `vX.Y.Z` points at `CIRCLE_SHA1` before bumping GitOps, and fails loudly otherwise. The source commit is recorded in the GitOps commit body.
- **`github_release`**: targets `CIRCLE_SHA1` (not `main`), and refuses if the tag points elsewhere.
- **Generator**: `cd_path` is wrapped in `##IF_CD_EXPLICIT_PATH##`, so it is rendered only when `cd.path` is set. Packed scripts are regenerated.
No new orb parameters, so generated pipelines stay compatible.
## Tests
- `tests/test_allocate_version.sh` (12 tests): free version, rerun reuse, the #65/#66 collision gets the next patch, tags never move, annotated tags, multiple taken versions skipped, non-main/pre-release skipped, API errors and missing token fail. It also runs **8 concurrent allocators** against a fake refs API with atomic create-if-absent (hard-link) semantics, which must yield 8 distinct contiguous versions, each tag owned by its committer, and concurrent reruns that create no extra tags. Mutation check: treating "already exists" as success reproduces the incident (all 8 get 0.1.24, 4 tests fail).
- `tests/test_release_guards.sh` (13 tests): the registry guard with a fake `docker` (free, rerun, collision, supplemental collision, unlabeled, registry error), step ordering in jobs, release target, CD ownership check, and `cd_path` omitted without `cd.path` but kept with it.
- shellcheck clean on all touched scripts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
Version collisions. Website-Backend #65/#66 (0.1.24) and again #80/#79 (0.1.32, today 18:42–18:50Z) merged minutes apart and both pipelines computed the same GitVersion version. The first pipeline rolled its image out. The second overwrote the image tag in GHCR.
github-releaseused--target main, so the tag landed on whatevermainpointed at by then. The GitOps bump was a no-op. Result: prod ran2d7cb7aunderv0.1.32 -> d212ec5, and GHCR0.1.32now holds a different image than prod.cd_pathcheck failure. The generator always renderedcd_path:intoupdate_cd_repo. Repos whose.munitor.ymlpins an olderorb_version(Website-Backend/Frontenddevelopat 0.2, Concilium 0.2, makers4 0.1) fail withUnexpected argument(s): cd_path.Fix
allocate_version(new, in mvn/gradle/nodebuild_and_testonmain): reservesvX.Y.ZbyPOST /git/refs. GitHub creates the ref only if it is absent, so concurrent pipelines serialize on it:Existing tags are never moved. This runs before the version is baked into the jar or image.
guard_image_tags(new, before build/push indocker_build_push): every image now carries theorg.opencontainers.image.revisionlabel. The guard refuses to push a main or supplemental version tag that already exists for a different commit or without the label, and fails closed on registry errors.update_cd_repo: onmain, verifiesvX.Y.Zpoints atCIRCLE_SHA1before bumping GitOps, and fails loudly otherwise. The source commit is recorded in the GitOps commit body.github_release: targetsCIRCLE_SHA1(notmain), and refuses if the tag points elsewhere.Generator:
cd_pathis wrapped in##IF_CD_EXPLICIT_PATH##, so it is rendered only whencd.pathis set. Packed scripts are regenerated.No new orb parameters, so generated pipelines stay compatible.
Tests
tests/test_allocate_version.sh(12 tests): free version, rerun reuse, the #65/#66 collision gets the next patch, tags never move, annotated tags, multiple taken versions skipped, non-main/pre-release skipped, API errors and missing token fail. It also runs 8 concurrent allocators against a fake refs API with atomic create-if-absent (hard-link) semantics, which must yield 8 distinct contiguous versions, each tag owned by its committer, and concurrent reruns that create no extra tags. Mutation check: treating "already exists" as success reproduces the incident (all 8 get 0.1.24, 4 tests fail).tests/test_release_guards.sh(13 tests): the registry guard with a fakedocker(free, rerun, collision, supplemental collision, unlabeled, registry error), step ordering in jobs, release target, CD ownership check, andcd_pathomitted withoutcd.pathbut kept with it.🤖 Generated with Claude Code