Skip to content

Instantly share code, notes, and snippets.

@jonlabelle
Last active February 2, 2026 17:21
Show Gist options
  • Select an option

  • Save jonlabelle/2e091fa88baf70c3b8d6aa36d43c4ee8 to your computer and use it in GitHub Desktop.

Select an option

Save jonlabelle/2e091fa88baf70c3b8d6aa36d43c4ee8 to your computer and use it in GitHub Desktop.

Get Latest Stable GitHub Tag

Retrieve the latest stable tag from a GitHub repository using the GitHub API. This guide shows you how to fetch tags and filter for stable releases in both Bash and PowerShell.

Note

This strategy assumes the repository tags follow the Semantic Versioning format.

Table of Contents

The Challenge

The GitHub API's /repos/OWNER/REPO/tags endpoint returns all tags, including pre-releases (alpha, beta, rc, etc.). Additionally, the API doesn't guarantee tags are returned in any particular order---neither chronological nor semantic. This means we need to:

  1. Filter for stable version tags (using a whitelist approach)
  2. Sort them semantically to find the true latest version
  3. Handle edge cases like pagination and rate limits

The Solution

We'll use a whitelist regex pattern that matches only clean semantic version tags like 1.2.3 or v1.2.3, automatically excluding anything with extensions (e.g., 1.2.3-beta, v2.0.0-rc1).

Understanding the API

The GitHub API returns 30 tags by default. To increase our chances of finding the latest stable release, we'll request 100 tags using ?per_page=100. For repositories with many tags, you may need to implement pagination.

GitHub rate limits unauthenticated requests to 60 per hour. Authenticated requests (using a GitHub token) increase this to 5,000 per hour.

Bash Implementation

Basic Command

curl --silent "https://api.github.com/repos/OWNER/REPO/tags?per_page=100" |
  sed -n 's/.*"name": "\([^"]*\)".*/\1/p' |
  grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' |
  sort -V |
  tail -n 1

How it works:

  • curl --silent fetches the JSON response without progress output
  • sed extracts tag names from the JSON
  • grep -E filters for clean semantic versions (whitelist approach)
  • sort -V performs version-aware sorting
  • tail -n 1 selects the highest version

Important

On macOS, sort -V may not be available in the default BSD utilities. Install GNU coreutils (brew install coreutils) and use gsort -V instead.

macOS Alternative (Without GNU Coreutils)

If you don't want to install additional tools, use this version that sorts with awk:

OWNER="GoogleCloudPlatform"
REPO="cloud-sql-proxy"

latest_tag=$(curl --silent --fail "https://api.github.com/repos/$OWNER/$REPO/tags?per_page=100" |
  sed -n 's/.*"name": "\([^"]*\)".*/\1/p' |
  grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' |
  awk -F'[.v]' '{
    v = ($1 == "" ? $2 : $1);
    print v*1000000 + $(NF-1)*1000 + $NF "\t" $0
  }' |
  sort -n |
  tail -n 1 |
  cut -f2)

if [ -z "$latest_tag" ]; then
  echo "Error: No stable tag found or API request failed"
  exit 1
fi

echo "Latest stable tag: $latest_tag"

This approach converts versions to numeric values for sorting (e.g., 1.2.3 becomes 1002003), then extracts the original tag name.

Complete Example with Error Handling

OWNER="GoogleCloudPlatform"
REPO="cloud-sql-proxy"

latest_tag=$(curl --silent --fail "https://api.github.com/repos/$OWNER/$REPO/tags?per_page=100" |
  sed -n 's/.*"name": "\([^"]*\)".*/\1/p' |
  grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' |
  sort -V |
  tail -n 1)

if [ -z "$latest_tag" ]; then
  echo "Error: No stable tag found or API request failed"
  exit 1
fi

echo "Latest stable tag: $latest_tag"

With Authentication

For higher rate limits, authenticate with a GitHub token:

OWNER="GoogleCloudPlatform"
REPO="cloud-sql-proxy"
GITHUB_TOKEN="your_token_here"

latest_tag=$(curl --silent --fail \
  -H "Authorization: Bearer $GITHUB_TOKEN" \
  "https://api.github.com/repos/$OWNER/$REPO/tags?per_page=100" |
  sed -n 's/.*"name": "\([^"]*\)".*/\1/p' |
  grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' |
  sort -V |
  tail -n 1)

if [ -z "$latest_tag" ]; then
  echo "Error: No stable tag found or API request failed"
  exit 1
fi

echo "Latest stable tag: $latest_tag"

PowerShell Implementation

PowerShell provides native JSON parsing and type-aware sorting, making the implementation more concise.

Basic Command

$tags = Invoke-RestMethod -Uri "https://api.github.com/repos/OWNER/REPO/tags?per_page=100"
$tags.name |
  Where-Object { $_ -match '^v?\d+\.\d+\.\d+$' } |
  Sort-Object { [version]($_ -replace '^v','') } |
  Select-Object -Last 1

How it works:

  • Invoke-RestMethod automatically parses the JSON response
  • Where-Object filters for clean semantic versions
  • Sort-Object with [version] type casting performs semantic version sorting
  • Select-Object -Last 1 selects the highest version

Complete Example with Error Handling

$owner = "GoogleCloudPlatform"
$repo = "cloud-sql-proxy"

try {
    $tags = Invoke-RestMethod -Uri "https://api.github.com/repos/$owner/$repo/tags?per_page=100"
    $latestTag = $tags.name |
      Where-Object { $_ -match '^v?\d+\.\d+\.\d+$' } |
      Sort-Object { [version]($_ -replace '^v','') } |
      Select-Object -Last 1

    if (-not $latestTag) {
        Write-Error "No stable tag found"
        exit 1
    }

    Write-Output "Latest stable tag: $latestTag"
}
catch {
    Write-Error "Failed to fetch tags: $_"
    exit 1
}

With Authentication

$owner = "GoogleCloudPlatform"
$repo = "cloud-sql-proxy"
$token = "your_token_here"

$headers = @{
    Authorization = "Bearer $token"
}

try {
    $tags = Invoke-RestMethod -Uri "https://api.github.com/repos/$owner/$repo/tags?per_page=100" -Headers $headers
    $latestTag = $tags.name |
      Where-Object { $_ -match '^v?\d+\.\d+\.\d+$' } |
      Sort-Object { [version]($_ -replace '^v','') } |
      Select-Object -Last 1

    if (-not $latestTag) {
        Write-Error "No stable tag found"
        exit 1
    }

    Write-Output "Latest stable tag: $latestTag"
}
catch {
    Write-Error "Failed to fetch tags: $_"
    exit 1
}

Summary

Both approaches use a whitelist strategy to match only clean semantic version tags, automatically filtering out pre-releases. By sorting the results semantically, we ensure we get the true latest stable version regardless of the API's return order.


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment