CI/CD Pipelines,  Git,  Github,  Github Actions,  Gitlab,  Gitlab Runner

Reliably Fetching Git Tags in GitLab Pipelines (and the GIT_DEPTH Pitfall)

Finding the right version string in a CI/CD pipeline can be surprisingly tricky. If you’ve been searching for a way to grab the “latest” tag in your GitLab runner, you’ve likely stumbled across this specific combination of Git commands. At least I did…

Here is a breakdown of what that command does, why it works, and the “gotchas” you need to look out for.

The Command Breakdown

The command is actually two Git commands nested together:

PowerShell
git describe --tags $(git rev-list --tags --max-count=1)

1. The Inner Command: git rev-list

The part inside the parenthesis, git rev-list --tags --max-count=1, acts as a locator.

  • --tags: Tells Git to look through the commit history specifically for commits that have tags associated with them.
  • --max-count=1: Instructs Git to stop after finding the very first (most recent) tagged commit in the current branch’s history.

2. The Outer Command: git describe

Once the inner command finds the “hash” (the unique ID) of that commit, git describe --tags turns that ID into a human-readable string (e.g., v1.2.3).

⚠️ Important: “Last Updated” vs. “Last Added”

It is a common misconception that this command always returns the highest version number or the most recently created tag.

This command returns the most recent tag reachable from your current commit history.

If you go back to an older commit (say, from six months ago) and add or move a tag there, git rev-list will see that “updated” commit as the most recent tagged point in the timeline.

Warning: If you are looking for the mathematically highest version number (like v2.0.0 over v1.9.0), this command might fail you if v1.9.0 was the last one touched or merged. For strictly sorted versions, you would typically use git tag --sort=-v:refname.

GitLab CI/CD Implementation

To use this in your GitLab pipeline, you need to ensure your runner has enough “depth” to see the tags. By default, GitLab often performs a shallow clone, which might hide the tags from Git.

Add this to your .gitlab-ci.yml:

YAML
variables:
  # Ensure the runner fetches all tags and history
  GIT_DEPTH: 0
YAML
variables:
  GIT_DEPTH: 0

get_version_job:
  stage: build
  script:
    - echo "Fetching the most recent tag..."
    - LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
    - echo "The identified tag is $LATEST_TAG"
    # Example: Save it to a file for other jobs to use
    - echo "APP_VERSION=$LATEST_TAG" >> build.env
  artifacts:
    reports:
      dotenv: build.env

Why set GIT_DEPTH: 0 ?

GitLab runners often optimize for speed by only downloading the last few commits. If the last tag was created 50 commits ago, a shallow clone won’t see it. Setting the depth to 0 tells GitLab to fetch the entire history so the command works reliably.

Github Implementation

YAML
# Example GitHub Actions workflow
on:
  push:
    tags:
      - "v*.*.*"
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Get latest tag
        run: |
          LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
          echo "Latest tag: $LATEST_TAG"

Handle Edge Cases

  • If no tags exist, consider using a fallback (e.g., 1.0.0) to avoid errors.
  • Ensure tags are properly formatted (e.g., v1.2.3) for compatibility.