← 開発ノート

curl と jq で GitHub の最新リリースからダウンロード資産を一つ選ぶ

curl で GitHub の最新リリースを取得し、jq で必要な OS、CPU、形式に合うダウンロード資産をちょうど一つ選びます。次のスクリプトは HTTP エラー、不正な JSON、未一致や複数一致で停止し、ファイルをダウンロードせずメタデータを返します。

releases/latest エンドポイントは、数値的に最大の Git タグを計算するものではありません。リリース API のドキュメントでは、通常のタグ、ドラフト、プレリリース、公開済みリリースが区別されています。

この例は公開リポジトリ adoptium/temurin17-binaries から Linux x64 HotSpot JDK tar.gz のメタデータを取得します。トークンも実際のファイルダウンロードも不要です。リポジトリ引数を省略するとこの既定値を使い、空の引数を明示すると拒否します。

処理の流れ

HTTP の失敗では JSON の解析前に停止します。有効なリリースオブジェクトには一致する資産がちょうど一つ必要です。その URL を選んでもダウンロードファイルの検証にはなりません。

HTTP の失敗では JSON の解析前に停止します。有効なリリースオブジェクトには一致する資産がちょうど一つ必要です。その URL を選んでもダウンロードファイルの検証にはなりません。

HTTP の失敗と JSON の選択失敗を分ける

curl、jq、POSIX sh が必要です。latest-release.sh として保存してください。

latest-release.sh

#!/bin/sh
set -eu
if [ "$#" -gt 1 ]; then
    printf '%s\n' 'usage: latest-release.sh OWNER/REPO' >&2
    exit 2
fi
repo=${1-adoptium/temurin17-binaries}
case "$repo" in
    */*/*|/*|*/|*[!A-Za-z0-9_./-]*) printf '%s\n' 'usage: latest-release.sh OWNER/REPO' >&2; exit 2 ;;
esac
case "$repo" in
    */*) ;;
    *) printf '%s\n' 'usage: latest-release.sh OWNER/REPO' >&2; exit 2 ;;
esac
api=${GITHUB_API_URL:-https://api.github.com}
response=$(curl --fail --silent --show-error --location --max-time 20 \
    --header 'Accept: application/vnd.github+json' \
    --header 'X-GitHub-Api-Version: 2026-03-10' \
    "$api/repos/$repo/releases/latest") || exit 1
printf '%s' "$response" | jq --slurp --exit-status '
    if length != 1 or (.[0] | type) != "object" then
        error("expected one release object")
    else .[0] end
    | . as $release
    | if (.tag_name | type) != "string" or (.tag_name | length) == 0
         or (.assets | type) != "array" then
        error("missing release tag or assets")
      else . end
    | [.assets[] | select(.name | test("^OpenJDK17U-jdk_x64_linux_hotspot_.*\\.tar\\.gz$"))] as $assets
    | if ($assets | length) != 1 then
        error("expected exactly one Linux x64 JDK tar.gz asset")
      elif ($assets[0].browser_download_url | type) != "string"
           or ($assets[0].browser_download_url | length) == 0 then
        error("missing asset download URL")
      else {tag_name: $release.tag_name, download_url: $assets[0].browser_download_url} end
'
sh latest-release.sh adoptium/temurin17-binaries

成功時には tag_name と download_url を持つ JSON オブジェクトを返します。値はリクエスト時点のリリースによって変わります。次の例は形式を示すテストデータであり、現在の最新リリースを示すものではありません。

{"tag_name":"jdk-17.0.8.1+1","download_url":"https://github.com/adoptium/temurin17-binaries/releases/download/example/OpenJDK17U-jdk_x64_linux_hotspot_example.tar.gz"}

curl --fail は先に HTTP エラーを処理します。解析前にレスポンスを保存することで、パイプラインの最後のコマンドがネットワークの失敗を隠すことを防ぎます。curl のオプションを参照してください。

jq --slurp は入力全体を配列へ読み込みます。JSON 値がちょうど一つという条件は空のレスポンスも拒否します。続いてリリースオブジェクト、タグ、資産配列を確認し、ファイル名がちょうど一つ一致するときだけ結果を出力します。jq マニュアルを参照してください。

自動化で扱う結果

条件 結果
有効なレスポンスと一致する資産が一つ JSON を出力、ステータス 0。
HTTP403/404 または接続の失敗 ゼロ以外のステータス。
空の本文または不正な JSON ゼロ以外のステータス。
一致する資産がない、または複数ある 任意に選ばず失敗する。
リポジトリ引数の構文が不正 使い方を表示、ステータス 2。

HTTP リクエストが成功しても一致するアセットがない場合は、jq フィルターを変更する前にリリースのファイル名を確認してください。この例が選ぶのは Java 17 の Linux x64 向けパッケージであり、すべての JDK パッケージではありません。Java 21 が必要な場合は Temurin のプラットフォームメタデータの例を使ってください。リポジトリやプラットフォームの値を YAML から取得する場合は、コマンドに渡す前に設定をデータとして解析・検証してください。

ローカルの HTTP レスポンスで成功、HTTP エラー、不正な JSON、資産の欠落、重複を確認しています。GITHUB_API_URL は既定で公開 API を指し、同じレスポンス契約を実装するテストサーバーにも変更できます。

公開リソースの読み取りに認証は不要です。認証付きリクエストへ拡張する場合は、ソースや Shell のトレースログにトークンを残さないでください。レート制限のレスポンスヘッダーと GitHub のレート制限ドキュメントを確認してください。

このスクリプトは URL を選ぶもので、インストールファイルの完全性は検証しません。インストールの自動化には、提供元のチェックサムや署名、および対象プラットフォームの確認が別途必要です。タグを数値バージョン比較器へ渡す前に、その入力契約を満たしているか確認してください。

選択したアセットをダウンロードしたら、ファイルの SHA-256 チェックサムを信頼できる期待値と比較します。検証器はローカルのバイト列を読み取ります。リリースのダウンロードや配布元の認証は行いません。

コメント

GitHubで見る ↗