DEV Community

Pavan Kumar Sadashiv
Pavan Kumar Sadashiv

Posted on

Automated Releases Without Node.js: Using GitLab's Native Release CLI & Git Trailers

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
Enter fullscreen mode Exit fullscreen mode
fix(db): prevent deadlocks on high concurrency transactions

Adds row-level locking during account balance updates.

Changelog: fixed
MR: 43
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)