mirror of
https://github.com/ivuorinen/dotfiles.git
synced 2026-02-17 21:54:24 +00:00
feat(bin): x-gh-get-latest-version improvements
This commit is contained in:
@@ -1,38 +1,66 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# Get latest release version, branch tag, or latest commit from GitHub
|
# Get latest release version, branch tag, or latest commit from GitHub
|
||||||
# Usage: x-gh-get-latest-version <repo>
|
# Usage: x-gh-get-latest-version <repo> [options]
|
||||||
# Author: Ismo Vuorinen <https://github.com/ivuorinen> 2024
|
# Author: Ismo Vuorinen <https://github.com/ivuorinen> 2024
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Environment variables, more under get_release_version() and get_latest_branch_tag()
|
# Environment variables, can be overridden by command line arguments
|
||||||
# functions. These can be overridden by the user.
|
|
||||||
GITHUB_API_URL="${GITHUB_API_URL:-https://api.github.com/repos}"
|
GITHUB_API_URL="${GITHUB_API_URL:-https://api.github.com/repos}"
|
||||||
VERBOSE="${VERBOSE:-0}"
|
VERBOSE="${VERBOSE:-0}"
|
||||||
|
INCLUDE_PRERELEASES="${INCLUDE_PRERELEASES:-0}"
|
||||||
|
OLDEST_RELEASE="${OLDEST_RELEASE:-0}"
|
||||||
|
BRANCH=""
|
||||||
|
LATEST_COMMIT="${LATEST_COMMIT:-0}"
|
||||||
|
LATEST_TAG="${LATEST_TAG:-0}"
|
||||||
|
OUTPUT="${OUTPUT:-text}"
|
||||||
|
SHOW_HELP=0
|
||||||
|
REPOSITORY=""
|
||||||
|
COMBINED=0
|
||||||
|
|
||||||
|
BIN=$(basename "$0")
|
||||||
|
|
||||||
# Prints a message if VERBOSE=1
|
# Prints a message if VERBOSE=1
|
||||||
msg()
|
msg()
|
||||||
{
|
{
|
||||||
[[ "$VERBOSE" -eq 1 ]] && echo "$1"
|
if [[ $VERBOSE -eq 1 ]]; then
|
||||||
|
echo "$1" >&2
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Show usage information
|
# Show usage information
|
||||||
usage()
|
usage()
|
||||||
{
|
{
|
||||||
cat << EOF
|
cat << EOF
|
||||||
Usage: $0 <repo> (e.g. ivuorinen/dotfiles)
|
Usage: $BIN <repo> [options]
|
||||||
|
|
||||||
Fetches the latest release version, latest branch tag, or latest commit SHA from GitHub.
|
Fetches the latest release version, latest branch tag, or latest commit SHA from GitHub.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<repo> Repository in format 'owner/repo' (e.g. ivuorinen/dotfiles)
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
- INCLUDE_PRERELEASES=1 Include prerelease versions (default: only stable releases).
|
-h, --help Show this help message and exit
|
||||||
- OLDEST_RELEASE=1 Fetch the oldest release instead of the latest.
|
-v, --verbose Enable verbose output
|
||||||
- BRANCH=<branch> Fetch the latest tag from a specific branch (default: main).
|
-p, --prereleases Include prerelease versions (default: only stable releases)
|
||||||
- LATEST_COMMIT=1 Fetch the latest commit SHA from the specified branch.
|
-o, --oldest Fetch the oldest release instead of the latest
|
||||||
- OUTPUT=json Return output as JSON (default: plain text).
|
-b, --branch <branch> Fetch the latest tag from a specific branch (default: main)
|
||||||
- GITHUB_API_URL=<url> Override GitHub API URL (useful for GitHub Enterprise).
|
-c, --commit Fetch the latest commit SHA from the specified branch
|
||||||
- GITHUB_TOKEN=<token> Use GitHub API token to increase rate limits (default: unauthenticated).
|
-t, --tag Fetch the latest Git tag (any branch)
|
||||||
|
-j, --json Return output as JSON (default: plain text)
|
||||||
|
-a, --all Fetch all information types in a combined output
|
||||||
|
|
||||||
|
Environment Variables (can be used instead of command line options):
|
||||||
|
- INCLUDE_PRERELEASES=1 Same as --prereleases
|
||||||
|
- OLDEST_RELEASE=1 Same as --oldest
|
||||||
|
- BRANCH=<branch> Same as --branch <branch>
|
||||||
|
- LATEST_COMMIT=1 Same as --commit
|
||||||
|
- LATEST_TAG=1 Same as --tag
|
||||||
|
- OUTPUT=json Same as --json
|
||||||
|
- GITHUB_API_URL=<url> Override GitHub API URL (useful for GitHub Enterprise)
|
||||||
|
- GITHUB_TOKEN=<token> Use GitHub API token to increase rate limits (default: unauthenticated)
|
||||||
|
- VERBOSE=1 Same as --verbose
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
- curl
|
- curl
|
||||||
@@ -40,28 +68,34 @@ Requirements:
|
|||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
# Fetch the latest stable release
|
# Fetch the latest stable release
|
||||||
$0 ivuorinen/dotfiles
|
$BIN ivuorinen/dotfiles
|
||||||
|
|
||||||
# Fetch the latest release including prereleases
|
# Fetch the latest release including prereleases
|
||||||
INCLUDE_PRERELEASES=1 $0 ivuorinen/dotfiles
|
$BIN ivuorinen/dotfiles --prereleases
|
||||||
|
|
||||||
# Fetch the oldest release
|
# Fetch the oldest release
|
||||||
OLDEST_RELEASE=1 $0 ivuorinen/dotfiles
|
$BIN ivuorinen/dotfiles --oldest
|
||||||
|
|
||||||
# Fetch the latest tag from the 'develop' branch
|
# Fetch the latest tag from the 'develop' branch
|
||||||
BRANCH=develop $0 ivuorinen/dotfiles
|
$BIN ivuorinen/dotfiles --branch develop
|
||||||
|
|
||||||
# Fetch the latest commit SHA from 'main' branch
|
# Fetch the latest commit SHA from 'main' branch
|
||||||
LATEST_COMMIT=1 $0 ivuorinen/dotfiles
|
$BIN ivuorinen/dotfiles --commit
|
||||||
|
|
||||||
|
# Fetch the latest Git tag (any branch)
|
||||||
|
$BIN ivuorinen/dotfiles --tag
|
||||||
|
|
||||||
|
# Fetch all information types in a combined output
|
||||||
|
$BIN ivuorinen/dotfiles --all
|
||||||
|
|
||||||
# Output result in JSON format
|
# Output result in JSON format
|
||||||
OUTPUT=json $0 ivuorinen/dotfiles
|
$BIN ivuorinen/dotfiles --json
|
||||||
|
|
||||||
# Use GitHub API token for higher rate limits
|
# Use GitHub API token for higher rate limits
|
||||||
GITHUB_TOKEN="your_personal_access_token" $0 ivuorinen/dotfiles
|
GITHUB_TOKEN="your_personal_access_token" $BIN ivuorinen/dotfiles
|
||||||
|
|
||||||
# Use GitHub Enterprise API
|
# Use GitHub Enterprise API
|
||||||
GITHUB_API_URL="https://github.example.com/api/v3/repos" $0 ivuorinen/dotfiles
|
GITHUB_API_URL="https://github.example.com/api/v3/repos" $BIN ivuorinen/dotfiles
|
||||||
EOF
|
EOF
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
@@ -77,6 +111,140 @@ check_dependencies()
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Check GitHub API rate limits and warn if they're getting low
|
||||||
|
check_rate_limits()
|
||||||
|
{
|
||||||
|
local auth_status="unauthenticated"
|
||||||
|
local auth_header=()
|
||||||
|
|
||||||
|
if [[ -n ${GITHUB_TOKEN:-} ]]; then
|
||||||
|
auth_status="authenticated"
|
||||||
|
auth_header=(-H "Authorization: token $GITHUB_TOKEN")
|
||||||
|
fi
|
||||||
|
|
||||||
|
msg "Making $auth_status GitHub API requests"
|
||||||
|
|
||||||
|
local rate_limit_info
|
||||||
|
rate_limit_info=$(curl -sSL "${auth_header[@]}" "https://api.github.com/rate_limit")
|
||||||
|
|
||||||
|
local remaining
|
||||||
|
local reset_timestamp
|
||||||
|
local reset_time
|
||||||
|
|
||||||
|
remaining=$(echo "$rate_limit_info" | jq -r '.resources.core.remaining')
|
||||||
|
reset_timestamp=$(echo "$rate_limit_info" | jq -r '.resources.core.reset')
|
||||||
|
|
||||||
|
# Handle date command differences between Linux and macOS
|
||||||
|
if date --version > /dev/null 2>&1; then
|
||||||
|
# GNU date (Linux)
|
||||||
|
reset_time=$(date -d "@$reset_timestamp" "+%H:%M:%S %Z" 2> /dev/null)
|
||||||
|
else
|
||||||
|
# BSD date (macOS)
|
||||||
|
reset_time=$(date -r "$reset_timestamp" "+%H:%M:%S %Z" 2> /dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
msg "Rate limit status: $remaining requests remaining, reset at $reset_time"
|
||||||
|
|
||||||
|
if [[ $remaining -le 5 ]]; then
|
||||||
|
echo "Warning: GitHub API rate limit nearly reached ($remaining requests left)" >&2
|
||||||
|
echo "Rate limits will reset at: $reset_time" >&2
|
||||||
|
|
||||||
|
if [[ $auth_status == "unauthenticated" ]]; then
|
||||||
|
echo "Tip: Set GITHUB_TOKEN to increase your rate limits (60 → 5000 requests/hour)" >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make a GitHub API request with proper error handling
|
||||||
|
api_request()
|
||||||
|
{
|
||||||
|
local url="$1"
|
||||||
|
local auth_header=()
|
||||||
|
|
||||||
|
if [[ -n ${GITHUB_TOKEN:-} ]]; then
|
||||||
|
auth_header=(-H "Authorization: token $GITHUB_TOKEN")
|
||||||
|
fi
|
||||||
|
|
||||||
|
local response
|
||||||
|
local status_code
|
||||||
|
|
||||||
|
# Use a temporary file to capture both headers and body
|
||||||
|
local tmp_file
|
||||||
|
tmp_file=$(mktemp)
|
||||||
|
|
||||||
|
msg "Making API request to: $url"
|
||||||
|
|
||||||
|
status_code=$(curl -sSL -w "%{http_code}" -o "$tmp_file" "${auth_header[@]}" "$url")
|
||||||
|
response=$(< "$tmp_file")
|
||||||
|
rm -f "$tmp_file"
|
||||||
|
|
||||||
|
# Check for HTTP errors
|
||||||
|
if [[ $status_code -ge 400 ]]; then
|
||||||
|
local error_msg
|
||||||
|
error_msg=$(echo "$response" | jq -r '.message // "Unknown error"')
|
||||||
|
|
||||||
|
if [[ $status_code -eq 403 && $error_msg == *"API rate limit exceeded"* ]]; then
|
||||||
|
# Extract rate limit reset info
|
||||||
|
local reset_timestamp
|
||||||
|
reset_timestamp=$(echo "$response" | jq -r '.rate.reset // empty')
|
||||||
|
|
||||||
|
local reset_time
|
||||||
|
if date --version > /dev/null 2>&1; then
|
||||||
|
# GNU date (Linux)
|
||||||
|
reset_time=$(date -d "@$reset_timestamp" "+%H:%M:%S %Z" 2> /dev/null \
|
||||||
|
|| echo "unknown time")
|
||||||
|
else
|
||||||
|
# BSD date (macOS)
|
||||||
|
reset_time=$(date -r "$reset_timestamp" "+%H:%M:%S %Z" 2> /dev/null \
|
||||||
|
|| echo "unknown time")
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Error: GitHub API rate limit exceeded" >&2
|
||||||
|
echo "Rate limit will reset at: $reset_time" >&2
|
||||||
|
|
||||||
|
if [[ -z ${GITHUB_TOKEN:-} ]]; then
|
||||||
|
echo "Tip: Set GITHUB_TOKEN to increase your rate limits (60 → 5000 requests/hour)" >&2
|
||||||
|
else
|
||||||
|
echo "You've exceeded even authenticated rate limits (5000 requests/hour)" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 3
|
||||||
|
elif [[ $status_code -eq 404 ]]; then
|
||||||
|
echo "Error: Repository not found or no access permission: $url" >&2
|
||||||
|
exit 2
|
||||||
|
else
|
||||||
|
echo "GitHub API error ($status_code): $error_msg" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$response"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if repository exists before proceeding
|
||||||
|
check_repository()
|
||||||
|
{
|
||||||
|
local repo="$1"
|
||||||
|
local api_url="${GITHUB_API_URL}/${repo}"
|
||||||
|
|
||||||
|
msg "Checking if repository exists: $api_url"
|
||||||
|
|
||||||
|
local response
|
||||||
|
response=$(api_request "$api_url")
|
||||||
|
|
||||||
|
# If we got here, the repository exists (otherwise api_request would have exited)
|
||||||
|
msg "Repository found: $(echo "$response" | jq -r '.full_name')"
|
||||||
|
|
||||||
|
# Get default branch if no branch is specified
|
||||||
|
if [[ -z ${BRANCH} ]]; then
|
||||||
|
BRANCH=$(echo "$response" | jq -r '.default_branch')
|
||||||
|
msg "Using default branch: $BRANCH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Return the repository full name (in case it differs from input due to redirects)
|
||||||
|
echo "$response" | jq -r '.full_name'
|
||||||
|
}
|
||||||
|
|
||||||
# Fetches the latest release or the oldest if OLDEST_RELEASE=1
|
# Fetches the latest release or the oldest if OLDEST_RELEASE=1
|
||||||
# $1 - GitHub repository (string)
|
# $1 - GitHub repository (string)
|
||||||
get_release_version()
|
get_release_version()
|
||||||
@@ -86,38 +254,55 @@ get_release_version()
|
|||||||
local oldest_release="${OLDEST_RELEASE:-0}"
|
local oldest_release="${OLDEST_RELEASE:-0}"
|
||||||
local api_url="${GITHUB_API_URL}/${repo}/releases"
|
local api_url="${GITHUB_API_URL}/${repo}/releases"
|
||||||
|
|
||||||
local auth_header=()
|
msg "Fetching release data from: $api_url " + \
|
||||||
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
|
"(Include prereleases: $include_prereleases, Oldest: $oldest_release)"
|
||||||
auth_header=(-H "Authorization: token $GITHUB_TOKEN")
|
|
||||||
fi
|
|
||||||
|
|
||||||
msg "Fetching release data from: $api_url (Include prereleases: $include_prereleases, Oldest: $oldest_release)"
|
|
||||||
|
|
||||||
local json_response
|
local json_response
|
||||||
json_response=$(curl -sSL "${auth_header[@]}" "$api_url")
|
json_response=$(api_request "$api_url")
|
||||||
|
|
||||||
# Check for API errors
|
local version=""
|
||||||
if echo "$json_response" | jq -e 'has("message")' > /dev/null; then
|
local prerelease_version=""
|
||||||
msg "GitHub API error: $(echo "$json_response" | jq -r '.message')"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local filter='.[] | select(.tag_name)'
|
# Get stable release version
|
||||||
[[ "$include_prereleases" -eq 0 ]] && filter+='.prerelease == false'
|
if [[ $oldest_release -eq 1 ]]; then
|
||||||
|
version=$(echo "$json_response" \
|
||||||
local version
|
| jq -r '[.[] | select(.tag_name != null and .prerelease == false)] | sort_by(.created_at) | first.tag_name // empty')
|
||||||
if [[ "$oldest_release" -eq 1 ]]; then
|
|
||||||
version=$(echo "$json_response" | jq -r "[${filter}] | last.tag_name // empty")
|
|
||||||
else
|
else
|
||||||
version=$(echo "$json_response" | jq -r "[${filter}] | first.tag_name // empty")
|
version=$(echo "$json_response" \
|
||||||
|
| jq -r '[.[] | select(.tag_name != null and .prerelease == false)] | sort_by(.created_at) | reverse | first.tag_name // empty')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -z "$version" ]]; then
|
# Get prerelease version if requested
|
||||||
msg "Failed to fetch release version for repository: $repo"
|
if [[ $include_prereleases -eq 1 ]]; then
|
||||||
|
if [[ $oldest_release -eq 1 ]]; then
|
||||||
|
prerelease_version=$(echo "$json_response" \
|
||||||
|
| jq -r '[.[] | select(.tag_name != null and .prerelease == true)] | sort_by(.created_at) | first.tag_name // empty')
|
||||||
|
else
|
||||||
|
prerelease_version=$(echo "$json_response" \
|
||||||
|
| jq -r '[.[] | select(.tag_name != null and .prerelease == true)] | sort_by(.created_at) | reverse | first.tag_name // empty')
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Error if no releases found and we're not in combined mode
|
||||||
|
if [[ -z $version && -z $prerelease_version && $COMBINED -eq 0 ]]; then
|
||||||
|
echo "No releases found for repository: $repo" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$version"
|
# Return both values for combined output
|
||||||
|
if [[ $COMBINED -eq 1 ]]; then
|
||||||
|
echo "$version"
|
||||||
|
echo "$prerelease_version"
|
||||||
|
else
|
||||||
|
# Return prerelease if specifically requested, otherwise stable
|
||||||
|
if [[ $include_prereleases -eq 1 && -n $prerelease_version ]]; then
|
||||||
|
msg "Found prerelease version: $prerelease_version"
|
||||||
|
echo "$prerelease_version"
|
||||||
|
else
|
||||||
|
msg "Found stable release version: $version"
|
||||||
|
echo "$version"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Fetches the latest tag from the specified branch
|
# Fetches the latest tag from the specified branch
|
||||||
@@ -130,16 +315,42 @@ get_latest_branch_tag()
|
|||||||
msg "Fetching latest tag for branch '$branch' from: $api_url"
|
msg "Fetching latest tag for branch '$branch' from: $api_url"
|
||||||
|
|
||||||
local json_response
|
local json_response
|
||||||
json_response=$(curl -sSL "$api_url")
|
json_response=$(api_request "$api_url")
|
||||||
|
|
||||||
local version
|
local version
|
||||||
version=$(echo "$json_response" | jq -r "[.[] | select(.ref | contains(\"refs/tags/$branch\"))] | last.ref | sub(\"refs/tags/\"; \"\") // empty")
|
version=$(echo "$json_response" \
|
||||||
|
| jq -r "[.[] | select(.ref | contains(\"refs/tags/$branch\"))] | sort_by(.ref) | reverse | first.ref | sub(\"refs/tags/\"; \"\") // empty")
|
||||||
|
|
||||||
if [[ -z "$version" ]]; then
|
if [[ -z $version && $COMBINED -eq 0 ]]; then
|
||||||
msg "Failed to fetch latest tag for branch: $branch"
|
echo "No tags found for branch: $branch in repository: $repo" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
msg "Found branch tag: $version"
|
||||||
|
echo "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fetches the latest Git tag (regardless of branch)
|
||||||
|
get_latest_git_tag()
|
||||||
|
{
|
||||||
|
local repo="$1"
|
||||||
|
local api_url="${GITHUB_API_URL}/${repo}/git/refs/tags"
|
||||||
|
|
||||||
|
msg "Fetching latest Git tag from: $api_url"
|
||||||
|
|
||||||
|
local json_response
|
||||||
|
json_response=$(api_request "$api_url")
|
||||||
|
|
||||||
|
local version
|
||||||
|
version=$(echo "$json_response" \
|
||||||
|
| jq -r '[.[] | select(.ref | startswith("refs/tags/"))] | sort_by(.ref) | reverse | first.ref | sub("refs/tags/"; "") // empty')
|
||||||
|
|
||||||
|
if [[ -z $version && $COMBINED -eq 0 ]]; then
|
||||||
|
echo "No Git tags found in repository: $repo" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
msg "Found Git tag: $version"
|
||||||
echo "$version"
|
echo "$version"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,42 +364,240 @@ get_latest_commit()
|
|||||||
msg "Fetching latest commit SHA from: $api_url"
|
msg "Fetching latest commit SHA from: $api_url"
|
||||||
|
|
||||||
local json_response
|
local json_response
|
||||||
json_response=$(curl -sSL "$api_url")
|
json_response=$(api_request "$api_url")
|
||||||
|
|
||||||
local sha
|
local sha
|
||||||
sha=$(echo "$json_response" | jq -r '.sha // empty')
|
sha=$(echo "$json_response" | jq -r '.sha // empty')
|
||||||
|
|
||||||
if [[ -z "$sha" ]]; then
|
if [[ -z $sha && $COMBINED -eq 0 ]]; then
|
||||||
msg "Failed to fetch latest commit SHA for branch: $branch"
|
echo "Failed to fetch latest commit SHA for branch: $branch in repository: $repo" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
msg "Found commit SHA: $sha"
|
||||||
echo "$sha"
|
echo "$sha"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Format combined text output
|
||||||
|
format_combined_text()
|
||||||
|
{
|
||||||
|
local repo="$1"
|
||||||
|
local branch="$2"
|
||||||
|
local tag="$3"
|
||||||
|
local commit="$4"
|
||||||
|
local release="$5"
|
||||||
|
local prerelease="$6"
|
||||||
|
|
||||||
|
echo "Repository: $repo"
|
||||||
|
|
||||||
|
if [[ -n $branch ]]; then
|
||||||
|
echo "Branch: $branch"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $tag ]]; then
|
||||||
|
echo "Git Tag: $tag"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $commit ]]; then
|
||||||
|
echo "Commit: $commit"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $prerelease ]]; then
|
||||||
|
echo "Prerelease: $prerelease"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $release ]]; then
|
||||||
|
echo "Release: $release"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Format combined JSON output
|
||||||
|
format_combined_json()
|
||||||
|
{
|
||||||
|
local repo="$1"
|
||||||
|
local branch="$2"
|
||||||
|
local tag="$3"
|
||||||
|
local commit="$4"
|
||||||
|
local release="$5"
|
||||||
|
local prerelease="$6"
|
||||||
|
|
||||||
|
local json="{"
|
||||||
|
json+="\"repository\":\"$repo\""
|
||||||
|
|
||||||
|
if [[ -n $branch ]]; then
|
||||||
|
json+=",\"branch\":\"$branch\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $tag ]]; then
|
||||||
|
json+=",\"tag\":\"$tag\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $commit ]]; then
|
||||||
|
json+=",\"commit\":\"$commit\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $prerelease ]]; then
|
||||||
|
json+=",\"prerelease\":\"$prerelease\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $release ]]; then
|
||||||
|
json+=",\"release\":\"$release\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
json+="}"
|
||||||
|
|
||||||
|
echo "$json"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse command line arguments
|
||||||
|
parse_arguments()
|
||||||
|
{
|
||||||
|
# If no arguments provided, show usage
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-h | --help)
|
||||||
|
SHOW_HELP=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-v | --verbose)
|
||||||
|
VERBOSE=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-p | --prereleases)
|
||||||
|
INCLUDE_PRERELEASES=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-o | --oldest)
|
||||||
|
OLDEST_RELEASE=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-b | --branch)
|
||||||
|
if [[ $# -lt 2 ]]; then
|
||||||
|
echo "Error: --branch option requires a branch name" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
BRANCH="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-c | --commit)
|
||||||
|
LATEST_COMMIT=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-t | --tag)
|
||||||
|
LATEST_TAG=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-j | --json)
|
||||||
|
OUTPUT="json"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-a | --all)
|
||||||
|
COMBINED=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
echo "Error: Unknown option: $1" >&2
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# If repository is already set, this is an error
|
||||||
|
if [[ -n $REPOSITORY ]]; then
|
||||||
|
echo "Error: Unexpected argument: $1" >&2
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
REPOSITORY="$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Validate that we have a repository
|
||||||
|
if [[ -z $REPOSITORY && $SHOW_HELP -eq 0 ]]; then
|
||||||
|
echo "Error: Repository argument is required" >&2
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Main function
|
# Main function
|
||||||
# $1 - GitHub repository (string)
|
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
if [[ $# -ne 1 ]]; then
|
# Parse command line arguments
|
||||||
|
parse_arguments "$@"
|
||||||
|
|
||||||
|
# Show help if requested
|
||||||
|
if [[ $SHOW_HELP -eq 1 ]]; then
|
||||||
usage
|
usage
|
||||||
fi
|
fi
|
||||||
|
|
||||||
check_dependencies
|
check_dependencies
|
||||||
|
|
||||||
local repo="$1"
|
# Check rate limits before making other API calls
|
||||||
local result
|
check_rate_limits
|
||||||
|
|
||||||
if [[ "${LATEST_COMMIT:-0}" -eq 1 ]]; then
|
# Validate repository existence and get normalized repository name
|
||||||
result=$(get_latest_commit "$repo")
|
local repo_fullname
|
||||||
elif [[ -n "${BRANCH:-}" ]]; then
|
repo_fullname=$(check_repository "$REPOSITORY")
|
||||||
result=$(get_latest_branch_tag "$repo")
|
|
||||||
else
|
# If --all specified, get all information types
|
||||||
result=$(get_release_version "$repo")
|
if [[ $COMBINED -eq 1 ]]; then
|
||||||
|
local branch="${BRANCH:-main}"
|
||||||
|
local git_tag=""
|
||||||
|
local commit_sha=""
|
||||||
|
local release_version=""
|
||||||
|
local prerelease_version=""
|
||||||
|
|
||||||
|
# Get Git tag if requested
|
||||||
|
git_tag=$(get_latest_git_tag "$repo_fullname")
|
||||||
|
|
||||||
|
# Get commit SHA
|
||||||
|
commit_sha=$(get_latest_commit "$repo_fullname")
|
||||||
|
|
||||||
|
# Get release versions (stable and prerelease)
|
||||||
|
read -r release_version prerelease_version < <(get_release_version "$repo_fullname")
|
||||||
|
|
||||||
|
# Format output based on selected format
|
||||||
|
if [[ $OUTPUT == "json" ]]; then
|
||||||
|
format_combined_json \
|
||||||
|
"$repo_fullname" \
|
||||||
|
"$branch" \
|
||||||
|
"$git_tag" \
|
||||||
|
"$commit_sha" \
|
||||||
|
"$release_version" \
|
||||||
|
"$prerelease_version"
|
||||||
|
else
|
||||||
|
format_combined_text \
|
||||||
|
"$repo_fullname" \
|
||||||
|
"$branch" \
|
||||||
|
"$git_tag" \
|
||||||
|
"$commit_sha" \
|
||||||
|
"$release_version" \
|
||||||
|
"$prerelease_version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${OUTPUT:-text}" == "json" ]]; then
|
# Not combined mode - get only the requested information type
|
||||||
echo "{\"repository\": \"$repo\", \"result\": \"$result\"}"
|
local result=""
|
||||||
|
|
||||||
|
if [[ $LATEST_COMMIT -eq 1 ]]; then
|
||||||
|
result=$(get_latest_commit "$repo_fullname")
|
||||||
|
elif [[ $LATEST_TAG -eq 1 ]]; then
|
||||||
|
result=$(get_latest_git_tag "$repo_fullname")
|
||||||
|
elif [[ -n $BRANCH ]]; then
|
||||||
|
result=$(get_latest_branch_tag "$repo_fullname")
|
||||||
|
else
|
||||||
|
result=$(get_release_version "$repo_fullname")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Output the result in the requested format
|
||||||
|
if [[ $OUTPUT == "json" ]]; then
|
||||||
|
echo "{\"repository\": \"$repo_fullname\", \"result\": \"$result\"}"
|
||||||
else
|
else
|
||||||
echo "$result"
|
echo "$result"
|
||||||
fi
|
fi
|
||||||
|
|||||||
196
local/bin/x-gh-get-latest-version.md
Normal file
196
local/bin/x-gh-get-latest-version.md
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# GitHub Latest Version Fetcher
|
||||||
|
|
||||||
|
`x-gh-get-latest-version` is a versatile command-line tool for fetching the
|
||||||
|
latest version information from GitHub repositories. It can retrieve release
|
||||||
|
versions, Git tags, branch tags, and commit SHAs with simple commands.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Fetch latest or oldest stable releases
|
||||||
|
- Include prerelease versions
|
||||||
|
- Get latest Git tags from any branch
|
||||||
|
- Fetch latest commit SHA from a specific branch
|
||||||
|
- Output in plain text or JSON format
|
||||||
|
- Combined output mode to get all information at once
|
||||||
|
- Rate limit checking to avoid GitHub API throttling
|
||||||
|
- Authenticated requests with GitHub token support
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- `curl` for making HTTP requests
|
||||||
|
- `jq` for processing JSON responses
|
||||||
|
- A GitHub personal access token
|
||||||
|
(optional, but recommended to avoid rate limiting)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Save the script to a location in your PATH
|
||||||
|
2. Make it executable: `chmod +x x-gh-get-latest-version`
|
||||||
|
3. Optionally set up a GitHub token as an environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export GITHUB_TOKEN="your_personal_access_token"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```text
|
||||||
|
Usage: x-gh-get-latest-version <repo> [options]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<repo> Repository in format 'owner/repo' (e.g. ivuorinen/dotfiles)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help Show this help message and exit
|
||||||
|
-v, --verbose Enable verbose output
|
||||||
|
-p, --prereleases Include prerelease versions (default: only stable releases)
|
||||||
|
-o, --oldest Fetch the oldest release instead of the latest
|
||||||
|
-b, --branch <branch> Fetch the latest tag from a specific branch (default: main)
|
||||||
|
-c, --commit Fetch the latest commit SHA from the specified branch
|
||||||
|
-t, --tag Fetch the latest Git tag (any branch)
|
||||||
|
-j, --json Return output as JSON (default: plain text)
|
||||||
|
-a, --all Fetch all information types in a combined output
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Fetch the Latest Release Version
|
||||||
|
|
||||||
|
```bash
|
||||||
|
x-gh-get-latest-version ivuorinen/dotfiles
|
||||||
|
```
|
||||||
|
|
||||||
|
Output: `v1.2.3`
|
||||||
|
|
||||||
|
### Include Prereleases
|
||||||
|
|
||||||
|
```bash
|
||||||
|
x-gh-get-latest-version ivuorinen/dotfiles --prereleases
|
||||||
|
```
|
||||||
|
|
||||||
|
Output: `v1.3.0-rc.1`
|
||||||
|
|
||||||
|
### Get the Oldest Release
|
||||||
|
|
||||||
|
```bash
|
||||||
|
x-gh-get-latest-version ivuorinen/dotfiles --oldest
|
||||||
|
```
|
||||||
|
|
||||||
|
Output: `v0.1.0`
|
||||||
|
|
||||||
|
### Fetch from a Specific Branch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
x-gh-get-latest-version ivuorinen/dotfiles --branch develop
|
||||||
|
```
|
||||||
|
|
||||||
|
Output: `develop-v1.3.0`
|
||||||
|
|
||||||
|
### Get Latest Commit SHA
|
||||||
|
|
||||||
|
```bash
|
||||||
|
x-gh-get-latest-version ivuorinen/dotfiles --commit
|
||||||
|
```
|
||||||
|
|
||||||
|
Output: `a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0`
|
||||||
|
|
||||||
|
### Fetch Latest Git Tag
|
||||||
|
|
||||||
|
```bash
|
||||||
|
x-gh-get-latest-version ivuorinen/dotfiles --tag
|
||||||
|
```
|
||||||
|
|
||||||
|
Output: `v2.0.0-beta.1`
|
||||||
|
|
||||||
|
### Output as JSON
|
||||||
|
|
||||||
|
```bash
|
||||||
|
x-gh-get-latest-version ivuorinen/dotfiles --json
|
||||||
|
```
|
||||||
|
|
||||||
|
Output: `{"repository": "ivuorinen/dotfiles", "result": "v1.2.3"}`
|
||||||
|
|
||||||
|
### Combined Information Output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
x-gh-get-latest-version ivuorinen/dotfiles --all
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Repository: ivuorinen/dotfiles
|
||||||
|
Branch: main
|
||||||
|
Git Tag: v2.0.0-beta.1
|
||||||
|
Commit: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
|
||||||
|
Prerelease: v1.3.0-rc.1
|
||||||
|
Release: v1.2.3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Combined Output as JSON
|
||||||
|
|
||||||
|
```bash
|
||||||
|
x-gh-get-latest-version ivuorinen/dotfiles --all --json
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"repository": "ivuorinen/dotfiles",
|
||||||
|
"branch": "main",
|
||||||
|
"tag": "v2.0.0-beta.1",
|
||||||
|
"commit": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0",
|
||||||
|
"prerelease": "v1.3.0-rc.1",
|
||||||
|
"release": "v1.2.3"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
You can use environment variables instead of command-line options:
|
||||||
|
|
||||||
|
- `INCLUDE_PRERELEASES=1` - Include prerelease versions
|
||||||
|
- `OLDEST_RELEASE=1` - Fetch the oldest release instead of the latest
|
||||||
|
- `BRANCH=branch_name` - Specify a branch to fetch tags from
|
||||||
|
- `LATEST_COMMIT=1` - Fetch latest commit SHA
|
||||||
|
- `LATEST_TAG=1` - Fetch latest Git tag
|
||||||
|
- `OUTPUT=json` - Output results as JSON
|
||||||
|
- `GITHUB_API_URL=url` - Override GitHub API URL (useful for GitHub Enterprise)
|
||||||
|
- `GITHUB_TOKEN=token` - Use GitHub API token to increase rate limits
|
||||||
|
- `VERBOSE=1` - Enable verbose output
|
||||||
|
|
||||||
|
## GitHub API Rate Limits
|
||||||
|
|
||||||
|
GitHub enforces rate limits on API requests:
|
||||||
|
|
||||||
|
- Unauthenticated requests: 60 requests per hour
|
||||||
|
- Authenticated requests: 5,000 requests per hour
|
||||||
|
|
||||||
|
For frequent use, it's strongly recommended to set up a GitHub token:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export GITHUB_TOKEN="your_personal_access_token"
|
||||||
|
```
|
||||||
|
|
||||||
|
The script will automatically warn you when you're approaching your rate limit
|
||||||
|
and suggest using a token if you haven't already.
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
The script provides informative error messages for common issues:
|
||||||
|
|
||||||
|
- Repository not found
|
||||||
|
- Rate limit exceeded
|
||||||
|
- No releases/tags found
|
||||||
|
- Invalid arguments
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Ismo Vuorinen (<https://github.com/ivuorinen>)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->
|
||||||
Reference in New Issue
Block a user