Created
March 27, 2025 22:50
-
-
Save mborgerson/721fe7b76e543f67768afba4ced80862 to your computer and use it in GitHub Desktop.
GitHub Actions Version Pinner
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# Usage: ./pin-gh-workflow-action-versions.sh input.yml > output.yml | |
# | |
# Pins GitHub Actions referenced by tags to the associated commit SHA. | |
# e.g.: | |
# | |
# < uses: actions/checkout@v4 | |
# --- | |
# > uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 | |
# | |
# GitHub has pretty low unauthenticated API limits, so you may want to create | |
# and use an API token before running this script. You can create a token under | |
# GitHub settings > Developer settings > Personal access tokens > Tokens | |
# (classic). Provide it to this script via the GITHUB_API_TOKEN environment | |
# variable. | |
# | |
# Dependencies: curl, jq | |
# | |
#------------------------------------------------------------------------------- | |
if ! command -v curl &> /dev/null; then | |
echo "Error: curl is not installed. Please install curl to proceed." >&2 | |
exit 1 | |
fi | |
if ! command -v jq &> /dev/null; then | |
echo "Error: jq is not installed. Please install jq to proceed." >&2 | |
exit 1 | |
fi | |
if [ "$#" -ne 1 ]; then | |
echo "Usage: $0 <file-with-gh-actions-uses>" | |
exit 1 | |
fi | |
INPUT_FILE="$1" | |
# Build curl options; add auth header if GITHUB_API_TOKEN is set. | |
curl_opts=("-s") | |
if [ -n "$GITHUB_API_TOKEN" ]; then | |
curl_opts+=("-H" "Authorization: token $GITHUB_API_TOKEN") | |
fi | |
while IFS= read -r line; do | |
if [[ $line =~ ^([[:space:]]*-?[[:space:]]*uses:[[:space:]]*)([^/@[:space:]]+)/([^@[:space:]]+)@((v[0-9]+(\.[0-9]+){0,2})) ]]; then | |
prefix="${BASH_REMATCH[1]}" | |
owner="${BASH_REMATCH[2]}" | |
repo="${BASH_REMATCH[3]}" | |
version="${BASH_REMATCH[4]}" | |
echo "Pinning $owner/$repo@$version..." >&2 | |
# Query the Git ref API to get the tag object | |
ref_url="https://api.github.com/repos/${owner}/${repo}/git/ref/tags/${version}" | |
ref_json=$(curl "${curl_opts[@]}" "$ref_url") | |
# Extract the object type and SHA from the JSON. | |
object_type=$(echo "$ref_json" | jq -r '.object.type') | |
object_sha=$(echo "$ref_json" | jq -r '.object.sha') | |
if [ "$object_type" = "tag" ]; then | |
# For annotated tags, dereference the tag object to get the commit SHA. | |
tag_url="https://api.github.com/repos/${owner}/${repo}/git/tags/${object_sha}" | |
commit_sha=$(curl "${curl_opts[@]}" "$tag_url" | jq -r '.object.sha') | |
elif [ "$object_type" = "commit" ]; then | |
commit_sha="$object_sha" | |
else | |
echo "Warning: Unrecognized object type for ${owner}/${repo}:${version}" >&2 | |
echo "$line" | |
continue | |
fi | |
if [ -z "$commit_sha" ] || [ "$commit_sha" = "null" ]; then | |
echo "Warning: Could not resolve commit SHA for ${owner}/${repo}:${version}" >&2 | |
echo "$line" | |
continue | |
fi | |
echo "${prefix}${owner}/${repo}@${commit_sha} # ${version}" | |
else | |
# If the line doesn't match, print it unchanged. | |
echo "$line" | |
fi | |
done < "$INPUT_FILE" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment