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!