概要
Pythonでパッケージ公開するプロジェクトでは、安定版バージョンのサポートも重要です。
GitHubでリポジトリを管理している場合、多くはGitHub Actionsを使用してCI/CDで更新管理していると思います。
例えば、Pythonではsetup-pythonアクションを使用して、複数のOS,Version情報をmatrix構文を指定して、テストを実行することでサポートバージョンで開発、保守することが挙げられます。
しかし、matrix構文ではバージョン情報を静的に記述しなければならず、新しい安定板バージョンが公開されても、ユーザー自身でバージョンを更新管理する必要があるため手間が発生します。
一方でGitHubでは、GitHub API(以降API)が公開されており、APIを活用すれば、Pythonの安定版バージョンを取得することができます。
この記事では、GitHub APIとcurlコマンドを使用してPythonの安定版バージョンを取得する方法を紹介し、バージョン情報(安定板/最新版)を静的/動的更新に、且つ、複数言語に対応した高機能な自作のGitHub Actionであるjson2vars-setterについて紹介します。
GitHub APIの基本
GitHub APIは、GitHubで管理するデータやリソース情報を取得できます。バージョン情報はリポジトリのタグ情報から取得します。そして取得するためにRest APIを使用します。
エンドポイント
GET /repos/{owner}/{repo}/tags
このエンドポイントは、指定したリポジトリのすべてのタグを一覧表示します。
基本的なcurlコマンド
curl -s https://api.github.com/repos/{owner}/{repo}/tags
認証付きの場合:
export GITHUB_TOKEN=pat_hoge
curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/{owner}/{repo}/tags
APIレート制限について
GitHub APIにはレート制限があります。認証方法によって制限回数が異なります。比較のため他のAPIタイプも記載します。
| API タイプ | エンドポイント | APIレート(認証なし) | APIレート(認証あり) |
|---|---|---|---|
| REST API | https://api.github.com |
60リクエスト/時間 | 5,000リクエスト/時間 |
| Search API | https://api.github.com/search |
10リクエスト/分 | 30リクエスト/分 |
| GraphQL API | https://api.github.com/graphql |
利用不可 | 5,000リクエスト/時間 |
APIレートを確認する方法
# 環境変数を使った認証 export GITHUB_TOKEN=pat_hoge curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/rate_limit | jq '.rate' # 認証なしの場合 curl -s https://api.github.com/rate_limit | jq '.rate'
レート制限を超えると、APIは403エラーを返します。安定版の取得処理では複数のリクエストが必要になる場合があるため、認証付きでのリクエストが必要になります。
タグから安定版バージョンを取得する
Pythonはタグを使ってリリースを管理しています。安定版バージョンを取得するには、以下のステップに従います。
- リポジトリのタグ一覧を取得する
- 安定版と見なせるタグをフィルタリングする
- 必要に応じてバージョン情報を解析・ソートする
安定版の判断基準
Pythonの安定版バージョンは一般的に以下のパターンに従います:
- セマンティックバージョニング(例:
v3.10.0) - プレリリース識別子がない(
alpha,beta,rcなどを含まない) - 通常
vから始まり、その後にメジャー.マイナー.パッチ番号が続く
Pythonの安定版取得用curlコマンド
Pythonの安定版バージョンを効率的に取得するためのcurlコマンドを紹介します。このコマンドはシェルスクリプトなどに組み込んで利用できます。
環境変数の設定
まず、GitHub APIトークンを環境変数に設定します。 APIトークンはGitHubの設定から取得してください。
export GITHUB_TOKEN=pat_xxx
トークンは個人アクセストークン(PAT)を使用します。これにより、レート制限が大幅に緩和されます。
基本的なPython安定版取得コマンド
Pythonの最新5つの安定版バージョンを取得:
curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/python/cpython/tags?per_page=100" | \
jq '[.[] | select(.name | test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))] | sort_by(.name | sub("^v"; "") | split(".") | map(tonumber)) | reverse | .[0:5]'
このコマンドでは以下の処理を行なっています:
per_page=100パラメータでページあたり100件のタグを取得- 正規表現
^v[0-9]+\.[0-9]+\.[0-9]+$でセマンティックバージョニング形式のタグのみ選択(例:v3.10.0) - バージョン番号によるソートとリバース処理で最新版順に配列
- 最新5つのバージョンのみ取得
出力フォーマットのカスタマイズ
必要な情報のみを抽出する方法
curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/python/cpython/tags?per_page=100" | \
jq '[.[] | select(.name | test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))] | sort_by(.name | sub("^v"; "") | split(".") | map(tonumber)) | reverse | .[0:5] | map({version: .name, url: .zipball_url, commit: .commit.sha})'
このコマンドでは、各バージョンの名前、ZIPダウンロードURL、コミットハッシュを抽出しています。
エラーハンドリングの実装
実際の運用では、エラーハンドリングを適切に実装することが重要です。以下にエラーハンドリングを組み込んだコマンドを示します。
curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/python/cpython/tags?per_page=100" | \
jq 'if type=="object" and has("message") then
{error: .message, documentation_url: .documentation_url}
else
[.[] | select(.name | test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))] |
sort_by(.name | sub("^v"; "") | split(".") | map(tonumber)) |
reverse | .[0:5]
end'
このコマンドでは、GitHub APIがエラーを返した場合(レート制限超過など)にエラー情報を適切に表示できます。
実行結果の例
適切に実行された場合、以下のような出力が得られます。
[ { "name": "v3.13.2", "zipball_url": "https://api.github.com/repos/python/cpython/zipball/refs/tags/v3.13.2", "tarball_url": "https://api.github.com/repos/python/cpython/tarball/refs/tags/v3.13.2", "commit": { "sha": "4f8bb3947cfbc20f970ff9d9531e1132a9e95396", "url": "https://api.github.com/repos/python/cpython/commits/4f8bb3947cfbc20f970ff9d9531e1132a9e95396" }, "node_id": "MDM6UmVmODE1OTg5NjE6cmVmcy90YWdzL3YzLjEzLjI=" }, { "name": "v3.13.1", "zipball_url": "https://api.github.com/repos/python/cpython/zipball/refs/tags/v3.13.1", "tarball_url": "https://api.github.com/repos/python/cpython/tarball/refs/tags/v3.13.1", "commit": { "sha": "067145177975eadd61a0c907d0d177f7b6a5a3de", "url": "https://api.github.com/repos/python/cpython/commits/067145177975eadd61a0c907d0d177f7b6a5a3de" }, "node_id": "MDM6UmVmODE1OTg5NjE6cmVmcy90YWdzL3YzLjEzLjE=" }, ... ]
補足: APIレート制限の確認
APIレート使用状況は以下で確認できます。
# レート制限の確認 curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/rate_limit | jq '.resources.core'
参考: workflowによるAPIレートの確認結果
ファイル(check_github_api_rate_limits.yml)
name: Check GitHub API Rate Limits on: workflow_dispatch: permissions: contents: read jobs: check_rate_limits: runs-on: ubuntu-latest steps: - name: Check GitHub API Rate Limits run: | echo "=== GitHub API Rate Limits with Token ===" curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/rate_limit | jq '.rate' echo -e "\n=== Search API Rate Limits with Token ===" curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/rate_limit | jq '.resources.search' echo -e "\n=== GraphQL API Rate Limits with Token ===" curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/rate_limit | jq '.resources.graphql' echo -e "\n=== GitHub API Rate Limits without Token ===" curl -s https://api.github.com/rate_limit | jq '.rate' echo -e "\n=== Search API Rate Limits without Token ===" curl -s https://api.github.com/rate_limit | jq '.resources.search' echo -e "\n=== GraphQL API Rate Limits without Token ===" curl -s https://api.github.com/rate_limit | jq '.resources.graphql' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
実行結果
Run echo "=== GitHub API Rate Limits with Token ==="
=== GitHub API Rate Limits with Token ===
{
"limit": 5000,
"used": 0,
"remaining": 5000,
"reset": 1742012041
}
=== Search API Rate Limits with Token ===
{
"limit": 30,
"used": 0,
"remaining": 30,
"reset": 1742008501
}
=== GraphQL API Rate Limits with Token ===
{
"limit": 5000,
"used": 0,
"remaining": 5000,
"reset": 1742012041
}
=== GitHub API Rate Limits without Token ===
{
"limit": 60,
"remaining": 54,
"reset": 1742008504,
"used": 6,
"resource": "core"
}
=== Search API Rate Limits without Token ===
{
"limit": 10,
"remaining": 10,
"reset": 1742008501,
"used": 0,
"resource": "search"
}
=== GraphQL API Rate Limits without Token ===
{
"limit": 0,
"remaining": 0,
"reset": 1742012041,
"used": 0,
"resource": "graphql"
}
GitHub Actionsでの使用例
GitHub Actionsでは、ワークフローの中でPythonの安定版バージョンを取得して活用することができます。以下に具体的な例を示します。
Pythonの最新安定版を取得するワークフロー
name: Get Python Stable Versions on: workflow_dispatch: schedule: - cron: '0 0 * * 1' # 毎週月曜日に実行 permissions: contents: read jobs: get-python-versions: runs-on: ubuntu-latest steps: - name: Check API Rate Limits run: | curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/rate_limit | jq '.resources.core' - name: Get Latest Python Stable Versions id: get_versions run: | # 最新の5つの安定版バージョンを取得 VERSIONS=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ "https://api.github.com/repos/python/cpython/tags?per_page=100" | \ jq -c 'if type=="object" and has("message") then {error: .message, documentation_url: .documentation_url} else [.[] | select(.name | test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))] | sort_by(.name | sub("^v"; "") | split(".") | map(tonumber)) | reverse | .[0:5] | map(.name) end') # 結果を出力 echo "Python stable versions: $VERSIONS" # 安定板バージョンの内の最新バージョンを環境変数に設定 LATEST=$(echo $VERSIONS | jq -r '.[0]') echo "LATEST_PYTHON_VERSION=${LATEST#v}" >> $GITHUB_ENV echo "latest_version=${LATEST#v}" >> $GITHUB_OUTPUT - name: Use Latest Python Version run: | echo "Latest stable Python version: ${{ env.LATEST_PYTHON_VERSION }}" echo "This information can be used for further steps in the workflow" - name: Check API Rate Remaining run: | curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/rate_limit | jq '.resources.core'
このワークフローについて
- GitHub Actionsの
GITHUB_TOKENを使用して認証 - 最新の5つの安定版Pythonバージョンを取得
- 取得した安定バージョンの内の最新バージョンを環境変数とワークフロー出力に設定
- APIレート制限の使用状況を確認
実用的な応用例:最新バージョンでのテスト実行
name: Test with Latest Python on: workflow_dispatch: push: branches: [ main ] jobs: get-version: runs-on: ubuntu-latest outputs: python_version: ${{ steps.get_version.outputs.latest_version }} steps: - name: Get Latest Python Version id: get_version run: | LATEST=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ "https://api.github.com/repos/python/cpython/tags?per_page=100" | \ jq -r '[.[] | select(.name | test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))] | sort_by(.name | sub("^v"; "") | split(".") | map(tonumber)) | reverse | .[0].name' | sed 's/^v//') echo "latest_version=$LATEST" >> $GITHUB_OUTPUT echo "Latest Python version: $LATEST" test: needs: get-version runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ needs.get-version.outputs.python_version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install pytest if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Test with pytest run: | pytest
この例では、最初のジョブで最新のPythonバージョンを取得し、それを使用して次のジョブでテストを実行しています。
より高度な選択肢
ここまで説明したcurlコマンドやGitHub Actionsの実装はPython専用であればこれでも十分です。しかし、実際のプロジェクトでは、python以外の言語を使用していることもあると思います。
また、これは私の場合ですが、workflowの内容に応じて、件数の指定や、バージョンを静的/動的に管理したいことや、毎回APIを呼びたくないなど、バージョン管理機能として満足できないことがありました。
そこで私はこれらの機能を考慮したGitHub Actionsの専用カスタムアクションとして、「json2vars-setter」を作ることにしました。
このアクションは上記不満点を解決し、CLIにも対応しています。より使い勝手の良いバージョン管理機能となっていますので、是非以下の内容を確認して、気になれば使ってみてください。
json2vars-setter - 複数言語対応のjsonファイルによるバージョン管理ツール
json2vars-setterドキュメント
主な特長
- 複数言語サポート: Python, Node.js, Ruby, Go, Rust など複数の言語に対応
- 柔軟なバージョン取得:
- 静的定義: 明示的にバージョンを指定
- 動的定義:
stable,latestタグから自動取得 - 件数指定: 最新N件の取得が可能
- キャッシュ管理: API制限を回避するインテリジェントなキャッシュ機能
- JSON統合: すべてのバージョン情報をJSONで管理
- 簡単設定: 複雑なJQフィルタなしで簡単に設定可能
使用例
name: Example workflow with json2vars-setter on: workflow_dispatch: jobs: setup: runs-on: ubuntu-latest steps: - name: Setup Variables with json2vars-setter uses: 7rikazhexde/json2vars-setter@v1 with: json-file-path: 'versions.json' cache-path: 'cached-versions.json' token: ${{ secrets.GITHUB_TOKEN }} - name: Use Python Version run: | echo "Python version: ${{ env.PYTHON_VERSION }}" # その他の処理...
json2vars-setterの利点
- 実装の手間削減: 本記事で説明したような複雑なJQフィルタ処理を自分で実装する必要がない
- レート制限対策: キャッシュ機能によりGitHub APIのレート制限問題を解決
- 複数言語対応: 1つのツールで複数言語のバージョン管理が可能
- CI/CD安定化: バージョン管理を安定させ、CI/CDパイプラインの信頼性を向上
このカスタムアクションはGitHub Actionsのワークフローをシンプルかつ堅牢にし、バージョン管理の煩わしさから解放されます。ぜひ試してみてください。また、良いと感じたら、是非GitHubでスターもいただけると嬉しいです。
まとめ
GitHub APIを使用すると、Pythonの安定版バージョン情報を効率的に取得できます。この記事で紹介した方法を活用すれば、CI/CDパイプラインの構築、互換性チェック、開発環境の整備など、様々な用途に役立てることができます。
実装ポイントをまとめ。
- 環境変数
export GITHUB_TOKEN=pat_hogeを使って認証情報を管理する - curlとjqを組み合わせてGitHub APIからタグ情報を取得・フィルタリングする
- Pythonの命名規則に合わせた正規表現(
^v[0-9]+\.[0-9]+\.[0-9]+$)で安定版を特定する - APIレート制限を考慮し、ページネーションパラメータ(
per_page=100)を活用する - エラーハンドリングを実装し、APIエラーに適切に対応する
- GitHub Actionsのワークフローに組み込み、自動化プロセスに活用する
これらの点に注意すれば、シンプルなcurlコマンドだけで、信頼性の高いPython安定版バージョン取得システムを構築できます。
なお、他の言語やフレームワークでも同様のアプローチは適用可能ですが、タグの命名規則が異なるため、正規表現やフィルタリングのロジックを調整する必要があります。