GitLab releases are more than just version tags; they’re first-class citizens that capture the state of your project at a specific point in time, linking code, issues, and deployments.
Let’s see a release in action. Imagine we’ve just merged a new feature and are ready to tag it for an upcoming release.
# First, create a Git tag
git tag v1.2.0
git push origin v1.2.0
# Now, navigate to your GitLab project's dashboard
# Go to: Deploy -> Releases
# Click: "New Release"
On the "New Release" page, you’ll see fields for:
- Tag: This is where you’ll select the Git tag you just created (
v1.2.0). GitLab automatically populates information based on the tag. - Release title: A human-readable name for the release (e.g., "Version 1.2.0 - Feature Freeze").
- Description: This is where the magic happens. You can write release notes here, but more powerfully, GitLab can dynamically generate release notes by referencing issues and merge requests closed since the previous release. We’ll cover this in detail.
- Milestones: If you’re using GitLab milestones to group work, you can associate this release with one or more milestones.
- Release date: Defaults to the current date but can be set to a future date for scheduled releases.
You can also associate assets with your release, such as compiled binaries or documentation links.
The core problem GitLab releases solve is bringing order to the chaos of software delivery. Traditionally, a "release" was just a tag in Git. But what is that tag? What code is in it? What problems does it fix? What features does it introduce? GitLab releases tie all this together.
Internally, when you create a release, GitLab associates it with the specified Git tag. It then looks at the commit history between the tag of the previous release and the tag of the current release. For each commit in that range, it identifies associated merge requests. If those merge requests have closed issues linked to them, GitLab can pull that information into the release notes.
Here’s a sample gitlab-ci.yml snippet that automates release creation after a tag is pushed:
stages:
- build
- deploy
- release
build_app:
stage: build
script:
- echo "Building the application..."
# Your build commands here
artifacts:
paths:
- build/
deploy_to_staging:
stage: deploy
script:
- echo "Deploying to staging..."
# Your staging deployment commands
environment: staging
create_release:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:latest
script:
- |
release-cli create --name "v$CI_COMMIT_TAG" --tag-name "$CI_COMMIT_TAG" --description "Release notes generated from closed issues and MRs."
only:
- tags
This CI job uses the release-cli tool. When a tag is pushed (e.g., v1.2.0), this job runs. The release-cli create command takes arguments like --name and --tag-name to create the release in GitLab, automatically linking it to the pushed Git tag. The description text here is a placeholder; GitLab will populate the actual release notes based on the issues and MRs closed between the previous tag and this one.
The most surprising true thing about GitLab releases is that their descriptions can be fully templated using data from all closed issues and merge requests within a given range, not just those directly linked to the release itself. This means you can generate comprehensive release notes that detail every user-facing change without manual intervention.
This dynamic generation is configured in the release description field. You can use Markdown and GitLab’s templating syntax. For example, to list all closed issues in a milestone associated with the release, you might use something like:
## What's New
### Features
* {{ #each closed_major_issues }}* {{ title }} (#{{ iid }})
{{ /each }}
### Bug Fixes
* {{ #each closed_bug_issues }}* {{ title }} (#{{ iid }})
{{ /each }}
## Changes
{{ #each merge_requests }}* {{ title }} ([!{{ iid }}])({{ web_url }})
{{ /each }}
GitLab then interpolates this template with the actual data when the release is created.
The next concept you’ll likely encounter is automating release note generation for every release using this templating mechanism, even without explicit CI/CD pipeline steps for it, by leveraging GitLab’s API and webhooks.