CI/CD Automation Tutorial 0/46 lessons ~6 min read Lesson 11

    Semantic Versioning in CI

    Semantic versioning in CI automates version bumps from conventional commits, tags releases in git, and wires tag events to publish pipelines — connecting human-readable versions…

    Course progress0%
    Focus
    20 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Semantic versioning in CI automates version bumps from conventional commits, tags releases in git, and wires tag events to publish pipelines — connecting human-readable versions to immutable artifacts.

    The story

    Release day chaos: three engineers manually edited package.json to "2.4.0", forgot to tag, and npm publish ran from main without changelog. Customers on 2.3.x never got security patch notes. Platform introduced semantic-release: commits on main trigger analyze → bump → tag → publish; git tag v2.4.1 triggers prod container rebuild from same commit. Release notes became automatic; rollback = deploy previous tag's artifact digest.

    Understanding the topic

    Semver (MAJOR.MINOR.PATCH) signals compatibility. CI connects commit message conventions to version bumps and release automation.

    • Semver rules: MAJOR = breaking API; MINOR = backward-compatible feature; PATCH = backward-compatible fix.
    • Conventional Commits: feat: → minor, fix: → patch, feat!: or BREAKING CHANGE: footer → major.
    • Release tags: git tag v1.2.3 marks release point; CI on: push: tags: ['v*'] triggers publish/deploy.
    • Pre-release: 1.2.0-beta.1 for RC channels; separate npm dist-tag or container tag.
    • Monorepo versioning: independent per package (lerna/changesets) vs unified repo version.

    Internal architecture

    Commit → version → artifact → deploy automation flow:

    text
    Conventional commit on main
    fix(auth): handle token expiry → PATCH bump
    feat(api): add export endpoint → MINOR bump
    feat!: drop v1 endpoints → MAJOR bump
    semantic-release / changesets / release-please
    ├─ compute next version from commits since last tag
    ├─ update CHANGELOG.md + package.json / Chart.yaml
    ├─ git tag v2.4.1 + GitHub Release
    └─ trigger publish job (npm, PyPI, OCI)
    CI on tag v*
    ├─ build artifact (if not already from main merge)
    └─ deploy prod with semver tag + digest metadata
    Consumers pin ^2.4.0 or exact 2.4.1 per policy

    Visual explanation

    Two diagrams show where Semantic Versioning in CI lives in the delivery path and how teams implement it in production.

    Semantic Versioning in CI — system view
    Conventional commit
    feat/fix/!
    Version compute
    Semver bump
    Git tag v*
    Release marker
    Publish pipeline
    npm/OCI/PyPI
    Where this topic sits in the delivery path.
    Semantic Versioning in CI — execution flow
    CHANGELOG
    Auto-generated
    GitHub Release
    Notes + assets
    Registry tag
    v1.2.3
    Prod deploy
    Tag trigger
    Follow this loop when designing or reviewing pipelines.

    Step-by-step explanation

    1. Adopt Conventional Commits — enforce with commitlint in CI on PR title and commits.
    2. Choose release tool: semantic-release (JS), release-please (multi-lang), changesets (monorepo), standard-version (manual trigger).
    3. Configure bot/service account with permission to push tags and CHANGELOG to main (branch protection exception or release branch).
    4. Wire tag push workflow: build, sign, publish to registry with semver tag AND git SHA tag.
    5. Document consumer pinning policy: libraries use semver ranges; services deploy exact digests with semver labels.

    Production implementation

    semantic-release + GitHub Actions with tag-triggered container publish:

    • commitlint.config.js: extends ['@commitlint/config-conventional'].
    • For monorepos use @changesets/cli — PR adds changeset file, merge triggers version PR.
    • Avoid double-build: main merge builds artifact; tag workflow promotes existing digest with semver label.
    yaml
    # .github/workflows/release.yml
    name: Release
    on:
    push:
    branches: [main]
    jobs:
    release:
    runs-on: ubuntu-latest
    permissions:
    contents: write
    issues: write
    pull-request: write
    steps:
    - uses: actions/checkout@v4
    with: { fetch-depth: 0 }
    - uses: actions/setup-node@v4
    with: { node-version: 20 }
    - run: npm ci
    - run: npx semantic-release
    env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
    # Separate workflow — deploy on tag
    # .github/workflows/deploy-release.yml
    on:
    push:
    tags: ['v*']
    jobs:
    deploy-prod:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - run: echo "Deploy version ${GITHUB_REF_NAME} — use artifact from merge SHA"

    Execution workflow

    1Wire semver automation in CI
    1 / 5

    Enforce commit format

    commitlint on PR; squash merge uses PR title as commit.

    Document types: feat, fix, chore, docs, refactor, test, ci.

    Real-world use

    Conventional Commits spec powers semantic-release, Angular commit format, and GitHub release-please (used by Google Cloud client libraries). CalVer (calendar versioning) competes for continuously deployed SaaS — but semver dominates library ecosystems and API products.

    Enterprise use cases

    Kubernetes itself uses semver tags. Terraform providers publish semver to registry. Internal platforms often wrap release-please with approval on major bumps. Banks may require human sign-off on MAJOR — CI opens release PR, architect approves, bot tags.

    • Library teams: npm semver ranges; breaking change = major with migration guide in CHANGELOG.
    • Service teams: semver for communication; deploy unit remains image digest.
    • Mobile: store version decoupled from semver but CI tags map builds to git.

    Production case study

    An open-core company published npm SDK and Docker API. Manual versioning caused 3 semver mistakes in one quarter (duplicate 1.4.0, skipped 1.3.2 patch). Adopted changesets + release-please.

    • Process: PR must include changeset markdown; commitlint on PR title.
    • Automation: release-please opens "release: sdk v2.1.0" PR; merge creates tag.
    • CI: tag triggers npm publish and GHCR push with semver + digest.
    • Outcome: zero manual version edits; support can correlate customer report to exact tag and digest.

    Trade-offs

    • Automated semver: consistent releases; requires commit discipline and bot write access to main.
    • Manual versioning: human judgment on major; error-prone and skips changelog automation.
    • CalVer: clear time ordering; poor signal for API compatibility.
    • 0.x.y semantics: minor can break in 0.x — document policy for pre-1.0 services.

    Security implications

    Release automation bots are high-value targets — compromised token publishes malicious semver.

    • Release bot uses fine-grained PAT or GitHub App with minimal permissions (contents, pull requests only).
    • Tag protection restricts who can push v* — prefer bot-only tag creation.
    • Verify npm/PyPI publish with provenance attestations (npm trusted publishing, OIDC).
    • Audit MAJOR releases — breaking changes may remove security controls if changelog ignored.

    Scalability analysis

    High commit velocity on main can create release noise or version inflation.

    • Batch releases with changesets (daily release PR) vs per-commit semantic-release.
    • Monorepo: 50 packages × independent semver — changeset aggregation prevents 50 npm publishes per merge.
    • Changelog size grows — link to GitHub Releases API for programmatic consumption.
    • Tag explosion in git — retention policy for old pre-release tags.

    Staff engineer insights

    • Semver for libraries, digests for deploys — don't conflate the two in pipeline design.
    • If commits aren't conventional, automation fails silently or mis-bumps — invest in commitlint early.
    • Major bumps need migration docs — automation should fail if BREAKING CHANGE lacks body in CHANGELOG.
    • Release-please's release PR is the human gate for regulated orgs uncomfortable with fully bot tagging.

    Best practices

    • Squash merge PRs with conventional title — one commit per PR simplifies semver analysis.
    • Keep CHANGELOG in git; link releases to git compare view.
    • Publish pre-releases to separate npm dist-tag (beta) or container tag suffix.
    • Document 0.x breaking change policy for pre-stable APIs.

    Common mistakes

    • Running semantic-release on feature branches — accidental prerelease tags.
    • Manual tag push bypassing CI — untested artifact labeled v1.0.0.
    • Using semver ranges in prod deploy manifests instead of digests — float risk remains.

    Advanced interview questions

    Interview Prep

    Practice concise answers, then expand each card for the explanation.

    5 questions
    1AdvancedQuestionHow do conventional commits map to semver bumps in CI?+

    Answer

    fix: → PATCH, feat: → MINOR, breaking change footer or ! after type → MAJOR. CI tool parses commits since last tag. chore/docs don't bump unless configured. Enforce via commitlint before merge.

    Follow-up

    Squash merge impact?
    2AdvancedQuestionDesign release automation for monorepo with 20 npm packages.+

    Answer

    Changesets: dev adds .changeset/*.md in PR describing bump level per package. Merge triggers changesets version PR aggregating bumps. Merge version PR tags and publishes only changed packages. Independent versioning.

    Follow-up

    Internal dependency updates?
    3AdvancedQuestionTag-triggered deploy vs main-triggered deploy with semver label?+

    Answer

    Tag trigger explicit release intent — good for gated prod. Main trigger continuous deploy with semver from bot tag on same commit — faster. Both need same artifact digest; tag is metadata gate.

    Follow-up

    Hotfix patch process?
    4AdvancedQuestionCustomer reports bug on v2.3.1 — how do you locate code?+

    Answer

    git checkout v2.3.1 or resolve tag to SHA via git rev-parse. Cross-reference GitHub Release assets, container image digest with v2.3.1 label, workflow run for that SHA. Reproduce from exact tag.

    Follow-up

    Tag deleted?
    5AdvancedQuestionWhen would you choose CalVer over semver?+

    Answer

    Continuously deployed SaaS with no external API contract — 2024.03.15 clarifies staleness. Libraries and public APIs stay semver. Hybrid: CalVer display, semver for API.

    Follow-up

    Kubernetes versioning?

    Hands-on exercise

    Set up commitlint and create a conventional commit flow that would bump PATCH vs MINOR.

    • Intentionally write bad commit message — confirm CI fails.
    • Draft CHANGELOG entry format for each bump type.
    • Map tag v1.0.0 to hypothetical deploy workflow trigger.
    bash
    npm install -D @commitlint/cli @commitlint/config-conventional
    echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
    # Valid commits:
    git commit -m "fix(api): correct null pointer in handler" # PATCH
    git commit -m "feat(ui): add dark mode toggle" # MINOR
    git commit -m "feat(auth)!: remove legacy token format" # MAJOR
    npx commitlint --from HEAD~1 --to HEAD --verbose

    Summary

    You can wire semver in CI through conventional commits, automated tagging, and publish pipelines — connecting commit messages to version bumps, changelogs, and release-triggered deploys without manual package.json edits.

    Ready to mark this lesson complete?Track your journey across the entire course.