Automated Releases Without Node.js: Using GitLab's Native Release CLI & Git Trailers
In many modern software projects, release automation is unnecessarily bloated.
Even in simple Go, Python, C++, or Rust projects, developers frequently pull in bloated npm dependencies like semantic-release or complex third-party GitHub Actions just to parse commit messages, increment version tags, and draft changelogs.
If you use GitLab, you don't need any external npm packages or third-party bots.
GitLab provides two native platform capabilities that handle the entire release lifecycle natively:
- Git Trailers & GitLab Changelog API: Automatically compiles changelogs categorized by features, bug fixes, and breaking changes.
-
GitLab
release-cli: Generates tagged releases, attaches compiled build artifacts, and updates release notes directly from your.gitlab-ci.yml.
In this practical tutorial, I will show you how to set up a zero-dependency, native release pipeline in GitLab CI/CD.
1. How Git Trailers Structure Commits
Git trailers are key-value pairs appended to the bottom of a Git commit message (standardized by git interpret-trailers).
GitLab natively parses Git trailers to categorize entries in your automated changelog.
Example Commit Messages:
feat(auth): add OAuth2 PKCE login support
Implements authorization code flow with PKCE for single-page apps.
Changelog: added
MR: 42
fix(db): prevent deadlocks on high concurrency transactions
Adds row-level locking during account balance updates.
Changelog: fixed
MR: 43
By adding Changelog: added or Changelog: fixed, GitLab automatically knows how to organize your release notes without requiring rigid conventional-commit regex parsers.
2. Generating the Changelog via GitLab API
GitLab provides a dedicated REST endpoint to generate or commit a changelog based on trailers since the last release tag:
# Preview changelog for the upcoming release v1.4.0
curl -s --header "PRIVATE-TOKEN: $GITLAB_PAT" \
"https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/repository/changelog?version=v1.4.0" | jq -r '.notes'
Generated Output:
## v1.4.0 (2026-10-16)
### Added (1 change)
- feat(auth): add OAuth2 PKCE login support (!42)
### Fixed (1 change)
- fix(db): prevent deadlocks on high concurrency transactions (!43)
No npm packages, no heavy Docker images, and zero custom parsing code.
3. Configuring the Native Release Pipeline (.gitlab-ci.yml)
GitLab’s official Docker image registry.gitlab.com/gitlab-org/release-cli:latest provides a lightweight binary specifically designed to publish releases.
Here is the complete, self-contained release job:
stages:
- build
- test
- release
# 1. Compile your binary (example: Go / C++ / Rust)
build_engine:
stage: build
image: golang:1.23-alpine
script:
- mkdir -p bin
- go build -ldflags="-s -w" -o bin/engine ./cmd/main.go
artifacts:
paths:
- bin/engine
expire_in: 1 month
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/ # Only run on semantic version tags
# 2. Publish the Official GitLab Release
create_release:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:latest
needs:
- job: build_engine
artifacts: true
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
script:
- echo "Publishing official release for tag $CI_COMMIT_TAG..."
release:
name: "Release $CI_COMMIT_TAG"
tag_name: "$CI_COMMIT_TAG"
description: "Release notes automatically compiled from Git trailers."
assets:
links:
- name: "Compiled Engine Binary (Linux x86_64)"
url: "${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/bin/engine"
link_type: "package"
4. How to Trigger a Zero-Fuss Release
To deploy a new release, developers simply create and push a git tag:
git tag -a v1.4.0 -m "Release v1.4.0"
git push origin v1.4.0
What Happens Automatically:
- GitLab CI detects the new tag matching
v*.*.*. - The
build_enginejob compiles the production binary and stores the artifact. - The
create_releasejob runsrelease-cli, drafts the release page, embeds the changelog, and links the compiled binary asset directly under the project's Deploy $\rightarrow$ Releases tab.
Reviewers, users, and DevOps pipelines can immediately download the verified binary package without manual intervention.
5. Why Native Tooling Wins
| Dimension | npm / Third-Party Bots | Native GitLab release-cli
|
|---|---|---|
| Dependencies | Requires node, npm, 50+ packages |
0 dependencies (native Go binary) |
| Execution Time | 45–90 seconds (npm install) |
< 3 seconds |
| Security Footprint | Vulnerable to supply chain attacks in npm tree | Officially signed by GitLab |
| Integration | Requires custom API tokens with broad permissions | Uses native CI_JOB_TOKEN
|
6. Key Takeaways
-
Leverage Git Trailers: Use
Changelog: added,Changelog: fixed, andMR: 123at the bottom of commit bodies for structured metadata. - Eliminate Unnecessary Tooling: Don't install Node.js just to bump version numbers in non-JavaScript repos.
-
Use
release-cli: Native YAMLrelease:keywords keep your CI configuration readable, maintainable, and lightning-fast.
Resources & Links
- GitLab Profile: gitlab.com/hrlpavan
- GitLab Contributor Profile: contributors.gitlab.com/users/hrlpavan
- GitHub: @hrlpavan
How does your team manage semantic versioning and release notes? Share your release workflows in the comments below!
Top comments (0)