Versioning and Releases¶
Welcome to the Intermediate track! 🚀
So far your pipeline publishes an image tagged latest. But latest is a moving target: it changes meaning every time you push, so nobody can tell which version they are actually running. Real projects publish versioned releases instead, marked with tags like v1.0.0. This lesson makes your pipeline do that.
What we will do (in very simple steps)¶
- Learn how version numbers work
- Learn what a Git tag is
- Make the pipeline publish a versioned image when you push a tag
Semantic versioning¶
Version numbers are not random. The common scheme, semantic versioning, looks like v1.4.2 and reads as vMAJOR.MINOR.PATCH:
- MAJOR (
1): goes up on a breaking change - MINOR (
4): goes up when you add a feature that does not break anything - PATCH (
2): goes up for a small bug fix
So v1.0.0 is your first release, v1.1.0 adds a feature, and v1.1.1 fixes a bug.
Git tags¶
A tag is a label you attach to a specific commit to mark it as a release. Unlike a branch, a tag does not move. v1.0.0 always points at the exact code that was released as v1.0.0.
You create and push a tag like this (do not run it yet):
Step 1: Make the pipeline react to tags¶
Open .github/workflows/ci.yml and update the trigger and the image tag. The full file becomes:
name: CI
on:
push:
branches:
- main
tags:
- 'v*'
jobs:
build-and-test:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Check out the code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install -r requirements.txt pytest
- name: Run tests
run: pytest
- name: Log in to the registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push the image
run: |
docker build -t ghcr.io/${{ github.repository }}:${{ github.ref_name }} .
docker push ghcr.io/${{ github.repository }}:${{ github.ref_name }}
Two things changed:
- The trigger now also fires on tags matching
v*, so any version tag starts the pipeline. - The image is now tagged with
${{ github.ref_name }}, which is the name of whatever triggered the run. On a push tomainit ismain; on the tagv1.0.0it becomesv1.0.0.
Step 2: Cut your first release¶
Commit the workflow change first:
Now create and push a version tag:
In the Actions tab you will see a run triggered by the tag. When it finishes, check Packages: your image is now published as snapshot-app:v1.0.0, a real, named release. 🎉
✅ Checkpoint¶
You are ready for the next lesson if:
- You understand what MAJOR, MINOR, and PATCH mean
- Pushing the tag
v1.0.0triggered a pipeline run - The registry shows an image tagged
v1.0.0, not justlatest
🩹 Common hiccups¶
- The tag did not trigger anything: you must push the tag itself with
git push origin v1.0.0. A normalgit pushdoes not send tags. - "tag already exists": you already made that tag. Use the next number, such as
v1.0.1. - Wrong image tag published: check the build step uses
${{ github.ref_name }}, not a hard-coded value.
Next up: Secrets Done Right, where you store credentials safely so the pipeline can log in to places beyond GitHub.