All code used in this write-up lives on GitHub.
Branching strategy#
This setup uses three core branches:
develop: active development branch. Feature branches start here. Merging back triggers a snapshot build.release/*: cut fromdevelopwhen a release is ready for QA. Every push produces a release candidate build.main: latest stable release. Production builds and release tags live here. Hotfixes branch from and merge back tomain.
I also use feature/*, bugfix/*, and hotfix/* branches. This is standard GitFlow with a bit of CI automation layered on top.
Reference: GitFlow
Semantic versioning#
Version format is MAJOR.MINOR.PATCH with branch-specific suffixes.
develop:<MAJOR>.<MINOR>.<PATCH>-SNAPSHOTrelease/*:<MAJOR>.<MINOR>.<PATCH>-rcNmain:<MAJOR>.<MINOR>.<PATCH>
This keeps branch intent obvious and prevents people from guessing which build is safe to deploy.
Reference: Semantic Versioning
Branch pipelines#
Each branch has its own pipeline behavior.
The main pipeline deploys what lands in main. It runs version.sh to strip prerelease suffixes, syncs develop after merges via release.sh and hotfix.sh, tags the release, then builds the Docker image.
File: semver_ci/bitbucket-pipelines.yml
release/* pipeline#
When a release/* branch is created, the pipeline turns -SNAPSHOT into -rc1. Every next push increments the RC number and builds again. That gives QA a clear sequence of candidates.
develop pipeline#
Merging feature/* or main into develop triggers the snapshot flow. version.sh ensures the suffix is -SNAPSHOT, then the pipeline builds the Docker image.
Custom pipelines#
version-bump pipeline#
version-bump is a manual helper for develop. You pick major, minor, or patch, and version.sh updates the version.
initiate-release-branch pipeline#
initiate-release-branch creates a new release/* branch from develop, commits an empty change to trigger CI, and pushes it.
Versioning scripts#
version.sh#
version.sh is the core versioning script. It accepts two argument groups.
Release mode (snapshot, rc, main)#
snapshot: sets the version like2.0.0-SNAPSHOTondeveloprc: increments release candidate versions like2.0.0-rc1,2.0.0-rc2main: removes prerelease suffixes onmainand creates a Git tag
SemVer segment (major, minor, patch)#
Increments the selected segment.
File: semver_ci/version.sh
release.sh#
release.sh runs in the main pipeline. It compares release/* and develop versions, then updates develop when needed.
File: semver_ci/release.sh
hotfix.sh#
hotfix.sh increments patch versions for hotfixes, syncs develop if required, and commits the result.
File: semver_ci/hotfix.sh
initiate-release-branch.sh#
Creates a release branch from develop.
File: semver_ci/initiate-release-branch.sh
Alternative: GitFlow CLI by NVIE#
- GitFlow adds higher-level commands for Vincent Driessen’s branching model.
- If you do not need SemVer automation, this tool is a solid way to keep GitFlow consistent.
Conclusion#
The goal here is simple: make release versions predictable without manual edits.
This setup gave me repeatable releases, clearer RC tracking, and less pipeline guesswork.