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:

  1. Git Trailers & GitLab Changelog API: Automatically compiles changelogs categorized by features, bug fixes, and breaking changes.
  2. 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:

  1. GitLab CI detects the new tag matching v*.*.*.
  2. The build_engine job compiles the production binary and stores the artifact.
  3. The create_release job runs release-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

  1. Leverage Git Trailers: Use Changelog: added, Changelog: fixed, and MR: 123 at the bottom of commit bodies for structured metadata.
  2. Eliminate Unnecessary Tooling: Don't install Node.js just to bump version numbers in non-JavaScript repos.
  3. Use release-cli: Native YAML release: keywords keep your CI configuration readable, maintainable, and lightning-fast.

Resources & Links

How does your team manage semantic versioning and release notes? Share your release workflows in the comments below!