Release pipeline silently clobbers the release branch when a published version is re-dispatched
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 970
- Forks
- 486
- Avg merge
- 3d 33m
- Merged PRs (30d)
- 170
Description
What happened
Release 26.09.02-01 shipped successfully on 2026-09-02 (run 33659151567) — tag v26.09.02-01 @ 7fe2ff5, GitHub release published, dotcms/dotcms:26.09.02-01 pushed to Docker Hub.
On 2026-09-03 the same version string was dispatched again (run 33780197124), this time against a main that was 54 commits ahead. The run got 24 minutes in — through Prepare and a full 15-minute artifact build — before being cancelled by hand at the Docker deploy step.
By then Prepare had already force-recreated release-26.09.02-01 at 81563d9, a commit 54 commits newer than the tag the branch is supposed to match. Had the run not been cancelled, it would have published Docker and Maven artifacts for 26.09.02-01 containing entirely different code than what already shipped under that version.
Root cause
.github/workflows/cicd_comp_release-prepare-phase.yml has two "already exists" guards, and they don't agree:
# line 148 — tag guard, checked against the LOCAL clone
if git rev-parse "${release_tag}" >/dev/null 2>&1; then
echo "Tag ${release_tag} exists, removing it"
git push origin :refs/tags/${release_tag}
fi
# line 165 — branch guard, checked against the REMOTE
remote=$(git ls-remote --heads https://github.com/dotCMS/core.git "${release_branch}" | wc -l | tr -d '[:space:]')
if [[ "${remote}" == '1' ]]; then
echo "Release branch ${release_branch} already exists, removing it"
git push origin :${release_branch}
fi
The checkout at line 103 is fetch-depth: 1 and pulls no tags, so git rev-parse "${release_tag}" is always false — the tag guard is dead code that can never fire. The branch guard queries the remote and works correctly.
That asymmetry is the whole bug: the branch was deleted and recreated, the tag was left alone. It broke in the safe direction purely by accident. If that rev-parse ever did resolve, the workflow would silently delete a published release tag — which is worse.
Neither guard is the right behavior anyway. Deleting and recreating a published release's branch/tag is never correct; the run should refuse to start.
Impact
release-26.09.02-01misrepresented what shipped for ~2 hours (manually reset to7fe2ff5).- 24 minutes of runner time and a full artifact build burned before a human caught it.
- Nothing was published — QEMU setup was cancelled, so Docker build/push and the Release / Release Notes / Changelog jobs never ran.
- Changelog generation was not affected:
gather-release-dataresolves its range from the releases API (findPreviousTagin.github/scripts/gather-release-data/src/github.ts), never from release branches.
Proposed fix
Fail fast in Prepare, immediately after Set Version Variables and before anything is mutated: if a GitHub Release already exists for release_tag, error out and tell the operator to bump the counter. Delete the dead git rev-parse tag block.
No force escape hatch. Bumping the counter on a failed attempt is already the established convention — 26.08.19-01, -02 and -03 all left behind empty-bodied releases before -04 shipped, and findPreviousTag already skips undocumented releases so the changelog range stays correct.
Acceptance criteria
- Dispatching a
release_versionwhosev<version>release already exists fails within the first minute with an actionable error. - The failure happens before the release branch is touched.
- Dispatching a fresh version is unaffected.
- The dead
git rev-parsetag-deletion block is gone.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in .github/workflows/cicd_comp_release-prepare-phase.yml, immediately after Set Version Variables, and inspect the existing tag and branch guards. Check the release-dispatch path and confirm the change preserves fresh-version runs while stopping before branch mutation when the GitHub Release already exists; verify the dead git rev-parse block is removed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, github-actions, shell
- Domain
- ci-cd, devops, release
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100