Chapter 72: DevOps Projects Part 2: Git Branching, Hooks, Conflicts, Semver, Monorepo and Gitea
72.4 Project 14: Semantic versioning and auto-tagged releases
Friends, version numbers must mean something. Today we follow semver: MAJOR.MINOR.PATCH. We tag releases from conventional commit messages and print a short changelog.
मित्रांनो, version numbers ला अर्थ असावा. आज semver: MAJOR.MINOR.PATCH. Conventional commit messages वरून tags, आणि छोटा changelog.
दोस्तों, version numbers का मतलब होना चाहिए. आज semver: MAJOR.MINOR.PATCH. Conventional commit messages से tags, और छोटा changelog.
What you will build in this project
- A repo that uses feat/fix/BREAKING commits.
- A tag script that bumps semver.
- Annotated tags pushed to the remote.
- A changelog snippet from git log.
1. Start from a real company story
At Cognizant in Chennai, every zip file was named final2_really_final.zip. Support never knew which build a customer ran.
One wrong rollback wiped a week of fixes. Cost: about ₹6 lakh in re-work and an angry client call.
Isha introduced semver tags and a tiny script that reads the latest commit prefix and bumps MAJOR, MINOR or PATCH.
Today we build that tagging habit on a practice remote.
Ravindra Bagale's Tip
Many students only move the PATCH number even for big breaks. That lies to the next team. Match the bump to the change. Keep that habit.
खूप students मोठ्या break साठीही फक्त PATCH हलवतात. ते पुढच्या team ला खोटे ठरते. Change नुसार bump करा. सवय ठेवा.
बहुत students बड़े break के लिए भी सिर्फ PATCH बढ़ाते हैं. वह अगली team से झूठ है. Change के हिसाब से bump करें. आदत रखिए.
2. Start and prepare
Step 1: Init and first tag
Why: Need v0.1.0 start. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: git init tag. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 1: init
Type this:
rm -rf /tmp/semver /tmp/semver.git && git init --bare /tmp/semver.git && git clone /tmp/semver.git /tmp/semver && cd /tmp/semver && git config user.email 'isha@cognizant.example' && git config user.name 'Isha' && printf '%s\n' 'api' > README.md && git add README.md && git commit -m 'chore: start' && git branch -M main && git tag -a v0.1.0 -m 'start' && git push -u origin main --tags && echo started
You will see:
started
What it means: Bare remote, clone, and v0.1.0 tag.
Example 2: tags
Type this:
cd /tmp/semver && git tag -l
You will see:
v0.1.0
What it means: Baseline tag.
Example 3: describe
Type this:
cd /tmp/semver && git describe --tags
You will see:
v0.1.0
What it means: HEAD matches tag.
Step 2: Write bump script
Why: Script reads latest tag. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: printf script. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 4: script
Type this:
cd /tmp/semver && printf '%s\n' '#!/bin/sh' 'set -e' 'MSG=${1:?usage: bump.sh <feat|fix|breaking>}' 'CUR=$(git describe --tags --abbrev=0)' 'VER=${CUR#v}' 'MAJOR=${VER%%.*}; REST=${VER#*.}; MINOR=${REST%%.*}; PATCH=${REST#*.}' 'case "$MSG" in' ' feat) MINOR=$((MINOR+1)); PATCH=0 ;;' ' fix) PATCH=$((PATCH+1)) ;;' ' breaking) MAJOR=$((MAJOR+1)); MINOR=0; PATCH=0 ;;' ' *) echo "unknown $MSG" >&2; exit 1 ;;' 'esac' 'NEW=v${MAJOR}.${MINOR}.${PATCH}' 'git tag -a "$NEW" -m "$MSG bump to $NEW"' 'echo "$NEW"' > bump.sh && chmod 755 bump.sh && cat bump.sh | head -5
You will see:
#!/bin/sh
set -e
MSG=...
CUR=...
VER=...
What it means: Bump script ready.
Example 5: help fail
Type this:
cd /tmp/semver && ./bump.sh 2>&1 | head -1 || true
You will see:
usage: bump.sh <feat|fix|breaking>
What it means: Usage guard works.
Example 6: mode
Type this:
cd /tmp/semver && stat -c '%a' bump.sh
You will see:
755
What it means: Executable.
Step 3: feat commit bumps MINOR
Why: feat -> MINOR. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: commit feat. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 7: feat work
Type this:
cd /tmp/semver && printf '%s\n' 'health' > health.txt && git add health.txt && git commit -m 'feat: add health endpoint file' && ./bump.sh feat && git tag -l
You will see:
v0.1.0
v0.2.0
What it means: MINOR bumped to 0.2.0.
Example 8: show
Type this:
cd /tmp/semver && git show v0.2.0 --no-patch | head -4
You will see:
tag v0.2.0
Tagger: Isha
feat bump to v0.2.0
What it means: Annotated message.
Example 9: describe
Type this:
cd /tmp/semver && git describe --tags
You will see:
v0.2.0
What it means: Current.
3. Build the main pieces
Step 4: fix commit bumps PATCH
Why: fix -> PATCH. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: commit fix. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 10: fix work
Type this:
cd /tmp/semver && printf '%s\n' 'health-ok' > health.txt && git add health.txt && git commit -m 'fix: health text' && ./bump.sh fix && git tag -l
You will see:
v0.1.0
v0.2.0
v0.2.1
What it means: PATCH bumped to 0.2.1.
Example 11: describe
Type this:
cd /tmp/semver && git describe --tags
You will see:
v0.2.1
What it means: Current patch.
Example 12: log
Type this:
cd /tmp/semver && git log --oneline v0.2.0..v0.2.1
You will see:
f01 fix: health text
What it means: Range shows the fix.
Step 5: BREAKING bumps MAJOR
Why: BREAKING CHANGE -> MAJOR. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: commit breaking. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 13: breaking
Type this:
cd /tmp/semver && printf '%s\n' 'v2-api' > README.md && git add README.md && git commit -m 'feat!: rename api folder' -m 'BREAKING CHANGE: clients must call /v2' && ./bump.sh breaking && git tag -l
You will see:
v0.1.0
v0.2.0
v0.2.1
v1.0.0
What it means: MAJOR moved to 1.0.0.
Example 14: show major
Type this:
cd /tmp/semver && git show v1.0.0 --no-patch | head -5
You will see:
tag v1.0.0
Tagger: Isha
breaking bump to v1.0.0
What it means: Major tag message.
Example 15: describe
Type this:
cd /tmp/semver && git describe --tags
You will see:
v1.0.0
What it means: On major.
Step 6: Show all tags
Why: tag -l lists. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: git tag -l. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 16: list
Type this:
cd /tmp/semver && git tag -l 'v*'
You will see:
v0.1.0
v0.2.0
v0.2.1
v1.0.0
What it means: Four tags.
Example 17: sorted
Type this:
cd /tmp/semver && git tag -l --sort=v:refname
You will see:
v0.1.0
v0.2.0
v0.2.1
v1.0.0
What it means: Version sort.
Example 18: count
Type this:
cd /tmp/semver && git tag -l | wc -l
You will see:
4
What it means: Four.
4. Automate and verify
Step 7: Build changelog
Why: log between tags. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: git log range. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 19: changelog
Type this:
cd /tmp/semver && printf '%s\n' '## v1.0.0' && git log --oneline v0.2.1..v1.0.0 && printf '%s\n' '## v0.2.1' && git log --oneline v0.2.0..v0.2.1
You will see:
## v1.0.0
feat!: rename...
## v0.2.1
fix: health text
What it means: Simple changelog from ranges.
Example 20: write file
Type this:
cd /tmp/semver && { echo '## Changelog'; git tag -l --sort=-v:refname | while read t; do echo; echo ### $t; git log --oneline $(git describe --tags --abbrev=0 $t^ 2>/dev/null)..$t 2>/dev/null || git log --oneline -1 $t; done; } > CHANGELOG.md && head -20 CHANGELOG.md
You will see:
## Changelog
### v1.0.0
...
What it means: CHANGELOG.md started.
Example 21: add changelog
Type this:
cd /tmp/semver && git add CHANGELOG.md bump.sh && git commit -m 'chore: add changelog and bump script' && echo committed
You will see:
committed
What it means: Script tracked.
Step 8: Push tags to remote
Why: ls-remote shows. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: git push --tags. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 22: push tags
Type this:
cd /tmp/semver && git push origin main --tags && git ls-remote --tags origin | awk '{print $2}'
You will see:
refs/tags/v0.1.0
refs/tags/v0.2.0
refs/tags/v0.2.1
refs/tags/v1.0.0
What it means: Remote has all tags.
Example 23: ls heads
Type this:
cd /tmp/semver && git ls-remote --heads origin
You will see:
... refs/heads/main
What it means: main on remote.
Example 24: fetch proof
Type this:
cd /tmp/semver && git fetch origin --tags && echo fetched
You will see:
fetched
What it means: In sync.
Step 9: Describe HEAD
Why: describe finds nearest. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: git describe. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 25: describe dirty
Type this:
cd /tmp/semver && git describe --tags
You will see:
v1.0.0
What it means: Clean describe when HEAD tagged; after chore commit may show v1.0.0-1-g... which is also fine.
Example 26: show patch msg
Type this:
cd /tmp/semver && git show v0.2.1 --no-patch | grep -i bump
You will see:
fix bump to v0.2.1
What it means: Message kept.
Example 27: show minor msg
Type this:
cd /tmp/semver && git show v0.2.0 --no-patch | grep -i bump
You will see:
feat bump to v0.2.0
What it means: Message kept.
5. Harden and accept
Step 10: Verify tag messages
Why: show reads message. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: git show. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 28: bad bump
Type this:
cd /tmp/semver && ./bump.sh weird 2>&1 | head -1 || true
You will see:
unknown weird
What it means: Rejects bad kind.
Example 29: tags unchanged
Type this:
cd /tmp/semver && git tag -l | wc -l
You will see:
4
What it means: Still four after bad call.
Example 30: script still 755
Type this:
cd /tmp/semver && stat -c '%a' bump.sh
You will see:
755
What it means: Mode ok.
Step 11: Refuse bad version file
Why: Guard bad input. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: reject. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 31: between tags
Type this:
cd /tmp/semver && git log --oneline v0.1.0..v0.2.0
You will see:
b01 feat: add health endpoint file
What it means: Range for the MINOR bump.
Example 32: tagger
Type this:
cd /tmp/semver && git for-each-ref refs/tags/v0.2.0 --format='%(taggername)'
You will see:
Isha
What it means: Annotated tagger.
Example 33: ahead
Type this:
cd /tmp/semver && git rev-list --count v0.1.0..HEAD
You will see:
2
What it means: Commits since first tag.
Step 12: Acceptance checklist
Why: Isha signs off when these pass. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: All green. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 34: semver rule
Type this:
printf '%s\n' 'MAJOR breaking, MINOR feat, PATCH fix' > /tmp/semver-rule.txt && cat /tmp/semver-rule.txt
You will see:
MAJOR breaking, MINOR feat, PATCH fix
What it means: Rule card.
Example 35: latest
Type this:
cd /tmp/semver && git tag -l --sort=-v:refname | head -1
You will see:
v1.0.0
What it means: Latest by version sort.
Example 36: Sign-off
Type this:
echo 'Isha: PASS'
You will see:
Isha: PASS
What it means: Done.
Check yourself
Q1. Who set semver at Cognizant?
Isha
Q2. Three semver parts?
MAJOR.MINOR.PATCH
Q3. Prefix for a new feature?
feat
Q4. Prefix for a bug fix?
fix
Q5. Command to list tags?
git tag -l
Q6. Annotated tag flag?
-a
Friends, you tagged releases with semver and built a tiny bump script. Did you understand? Let us try it once more. Add one BREAKING commit and prove MAJOR moves.
मित्रांनो, तुम्ही semver tags आणि bump script केली. एक BREAKING commit टाकून MAJOR हलवा.
दोस्तों, आपने semver tags और bump script बनाई. एक BREAKING commit डालकर MAJOR बढ़ाएँ
Lab: try at home
- Init repo and baseline tag.
- Add feat and fix commits.
- Run the bump script.
- Write a changelog snippet.
- Push tags to the bare remote.
Recap
- MAJOR = breaking.
- MINOR = new feature.
- PATCH = bug fix.
- Annotated tags carry a message.
- Conventional commits drive the bump.