Release Automation: Tagging, Changelogs, and Release Notes
Turn releases into a scripted habit with semantic tags, automated changelogs, and release notes assembled from your merged pull requests, in one command.
Netbay Engineering
Netbay Engineering
On this page
A release is a contract with three clauses: a version everyone agrees to call the software, a note describing what changed since the last version, and a stable reference — the tag — that never moves. Teams that do these three by hand stay in a weird place: releases get skipped when someone is busy, notes get written from memory, and tags get retagged when they are wrong. All three clauses are automatable, so the ritual becomes one command. The whole workload is a script, a tag, and a notes file — no release engineer required, just a rule nobody has to remember.
The contract, spelled out
Decide the rules before the tooling:
- Versions are semantic — "v1.4.0" means a feature release, "v1.4.1" a patch.
- Tags, once pushed, are immutable. A wrong release means a new release with a bumped number, never an edited one.
- The changelog is derived from merged pull requests and conventional-commit messages, never reconstructed from memory.
With those rules, creating a release is a small, boring script:
# tag, push, and publish in one breath
git tag -a v1.4.0 -m "release v1.4.0"
git push origin v1.4.0
gh release create v1.4.0 --generate-notes --latest --title "Release v1.4.0"The "--generate-notes" flag has GitHub assemble the note from every PR merged since the previous tag — which is exactly right, because those PRs were the change. No one has to remember anything.
Steering generated notes
Generated notes are only as good as the labels behind them. Release notes can be organized with a small config file that maps PR labels to sections:
# .github/release.yml
changelog:
categories:
- title: Features
labels: [enhancement]
- title: Bug fixes
labels: [bug]
- title: Dependencies
labels: [dependencies]
- title: Other changes
labels: ["*"]This is why the auto-labeling approach from the PR labeller post pays off twice: the same "enhancement", "bug", and "dependencies" labels that triage your review queue become the section headers in your release notes. Labels are metadata, and metadata that does double duty is the entire game.
Honest changelogs from commit history
If you prefer changelogs generated from the commit stream, conventional commits give you filters that you can read like a table of contents:
# slices of the changelog by conventional-commit type
git log v1.3.0..HEAD --grep='^feat' --pretty='- %s'
git log v1.3.0..HEAD --grep='^fix' --pretty='- %s'
git log v1.3.0..HEAD --grep='^docs' --pretty='- %s'The pattern — one commit, one kind, one line — is what makes both approaches work. Find the one your team already writes; the tooling just reads it. The two sources agree only when the history is disciplined: if you write conventional commits, the git log slice is a faithful changelog with no label ceremony at all, while PR-centric teams get honest notes from the labels the reviewers themselves applied. Mixing the two produces duplicate sections, so pick the one that matches how your merges actually happen and treat the other as a cross-check rather than a second changelog.
Branch targets and backports
Not every project releases from main. For a maintained product, a "release/1.4.x" branch is where patch fixes land, v1.4.1 tags are cut, and the fix gets cherry-picked back to main. The automation stays identical, just pointed at a different default. The one hard rule survives all of it: never move a tag. If "v1.4.0" points somewhere wrong, cut "v1.4.1" and apologize to nobody in code.
Promoting a release
The ritual usually ends with making the release visible: "gh release upload" to attach build artifacts, "--latest" set so the newest is obvious, and — if your flow drives deployments from tags — nothing more, because the tag itself is the event your deploy tooling already watches. Keep notes note-sized; link to the full changelog rather than pasting the diff. Validate the tag as part of the ritual: confirm the tag name matches the version string in your package manifest, so the release and the source never disagree about what shipped. A mismatched pair is the classic one-line incident that automation exists to prevent.
Takeaway: semantic immutable tags, notes derived from merged PRs, and a one-command release ritual turn shipping from a chore into a habit. When you script that loop, run it against a dependable box — a Lucknow DC01 VPS from Netbay deploys in under 60 seconds at netbayhosts.in.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance