概要
uvで作成したpythonパッケージのバージョン更新をgithub actionsから実施するワークフローサンプルを作りました。主な機能は以下の通りです。
- lint、format、testの実行
- Gitタグを用いたバージョン更新
- PyPIおよびTestPyPIへの公開
モチベーション
uvで作成したpythonパッケージのバージョン管理を一元化し、GitHubとPyPIの状態を自然に同期させるために、本サンプルを作りました。
通常、uvを使ってPythonパッケージを作成する際のバージョンアップ手順は以下の通りです:
-
pyproject.toml
のproject.version
を手動で更新 -
uv build && uv publish
を実行(バージョンはpyproject.toml
に基づいて決定)
GitHubでソースコードを管理する場合、さらに以下の作業が必要です:
- 更新内容をGitHubに反映
- 最新バージョンに対応するGitタグを追加
これらの手順には以下の課題があります:
- バージョンがGitのタグと
pyproject.toml
で二重管理される - プルリクエスト時に
pyproject.toml
の更新を忘れることがある。忘れると、バージョン更新のためだけのコミットやプルリクエストが必要になる - GitHubへのプッシュとPyPIへの公開が別々に行われるため、GitHubとPyPIを同じ状態に保つには注意が必要
これらの課題を解決するために、以下を目指しました:
- バージョン情報をGitタグで一元管理
- GitHub Actionsでバージョン更新、ビルド、公開を同時に実行
これにより、pyproject.toml
とGitタグのバージョンの二重管理を避けることができます。また、GitHub Actionsを通じてPyPIを更新することで、GitHubとPyPIの状態を意識せずに一致させることができ、開発の効率化につながると考えました。
利用ツール
- パッケージ管理: uv
- Lintツール: ruff, mypy
- フォーマットツール: ruff
- テストツール: pytest, bats, act
- タスク管理: taskipy
- ビルドツール: hatchling, hatch-vcs
使い方
GitHub Actionsでの実行
手順概要
-
.github
ディレクトリとその中身をGitHubリポジトリのデフォルトブランチにアップロードします。 - GitHubのSecretsに
TEST_PYPI_TOKEN
とPYPI_TOKEN
を登録します。 - GitHub > Setting > Actions > General > Workflow Permissionで
read and write permissions
を選択 - GitHub > Actionsから該当のworkflowを自動実行します。
python-check.yaml
-
.py
ファイルに対して、LintとFormatを実施します。 -
main
ブランチへのプルリクエストをトリガーとします。
publish-to-testpypi.yaml
- バージョン更新とTestPyPIへの公開を行います。
- GitHub Actionsから手動で実行します。
- 実行時に以下のオプションを指定できます(すべて任意):
-
バージョン番号:
v
で始まるセマンティックバージョニング形式の番号。空欄の場合、v
で始まる最新のタグが使用されます。 - Recreate Tag: チェックすると、指定されたバージョン番号が既に存在する場合、タグを作り直します。
- Dry Run: チェックすると、リモートへのタグのプッシュやTestPyPIへの公開など、Workflow外に影響を残す処理を行いません。
-
バージョン番号:
publish-to-pypi.yaml
このファイルは、バージョンを更新し、PyPIにパッケージを公開するためのものです。
実行方法や指定可能なオプションは、publish-to-testpypi.yaml
と同じです。
ローカルでの実行
環境構築
以下はM1 macOSでの手順です。他のOSを使用している場合は、適宜読み替えてください。
uv
# uvのインストール
curl -LsSf https://astral.sh/uv/install.sh | sh
dockerまたはdocker desktop
actを実行するために必要です。商用利用する際は、利用規約に注意してください。
act
このツールは、ローカル環境でGitHub Actionsを実行するために使用します。
Dockerコンテナ内でアクションを実行し、コンテナのサイズは3種類から選べます。ここではMediumサイズを選択します。
brew install act
act --container-architecture linux/amd64
# コンテナのサイズを聞かれたらMediumを選択
bats
bashのテストツールです。
# batsのインストール。ローカルでシェルのテストをしたい場合のみ必要
brew install bats
単体実行
テスト
uv経由でpytestを実行できます。
uv run task test
update_version.sh
workflowでのバージョン更新に用いるシェルスクリプトです。
sh .github/scripts/update_version.sh [-v version] [-i increment_type] [-n] [-d]
詳細は以下を御覧ください。
https://gist.github.com/jiroshimaya/5f4524ca296357e1c5347f1674217529
workflow
actでworkflowのテストを実行できます。
act [trigger] -j [jobname] -W [workflow yaml filepath] -e [event file path]
テスト
トリガーまたはjobを指定して実行します。
# triggerとしてpull_requestを指定
act pull_request -W .github/workflows/python-check.yaml
# jobを指定
act -j test -W .github/workflows/python-check.yaml
publish
原則として、dry-runで実行してください。テスト中に実際にpublishされると、管理が複雑になるためです。
workflow_dispatchがトリガーとなるジョブの場合は、eventファイルで必要な入力を指定します。
{"inputs": {"version": "", "recreate": "true", "dry_run": "true"}}
act -j publish -W .github/workflows/publish-to-testpypi.yaml -e tests/workflow/event.json
複数のeventに対してテストしたい場合はスクリプトを使用してください。
uv run task test-workflow # batsで実行
uv run task test-workflow-py # pytestで実行
検討メモ
上記の形に落ち着くまでに検討したことのメモです。
バージョン管理の方法
バージョンをgit tagとpyproject.tomlのどちらで管理するかを検討しました。pyproject.tomlで管理する場合、sedコマンドで動的に更新し、git tagはその情報に基づいて作成されます。しかし、pyproject.tomlの書き換えによりコミット履歴が増えることが懸念されたため、git tagでの管理を選びました。
publishワークフローの設定
バージョンを手動で入力する代わりに、現在のタグに基づいて自動でインクリメントする方法も考えましたが、試しに使ったところ、次のタグが何になるかわからないことにストレスを感じたため、手動入力にしました。GitHub ActionsのUIから現在のタグは簡単に確認でき、バージョンアップの頻度も低いため、手動での記載が問題にならないと判断しました。また、rc(リリース候補)などの特殊なタグにも対応できる利点があります。
TestPyPIとPyPIに異なるタイミングでpublishする必要があるため、バージョンを空欄にして最新タグでのpublishを可能にしました。TestPyPIでのバグ修正後、同じバージョンをPyPIに上げやすくするため、recreateフラグで現在のタグを最新コミットに付け直すこともできます。
ビルドツールの選択
tagの作成やリモートへのpushはシェルスクリプトで行っていますが、自動インクリメントのためにhatchやbump-my-versionの使用も検討しました。しかし、uvとの相性問題のためか、エラーが発生し、解決に時間がかかりそうだったため、採用を見送りました。
ビルドバージョン決定ツールの選定
gitのtagに基づいてバージョンを決定するツールは複数ありますが、uvのデフォルトビルドバックエンドがhatchlingであるため、相性の良さそうなhatch-vcsを使用することにしました。
ワークフローの独立性
バージョンアップデートを独立したワークフローにすることも考えましたが、バージョンアップデートとpublishが2ステップになることが実際に試してみると煩わしかったため、独立させないことにしました。
GitHub Actionsのローカルテスト
GitHub Actionsのローカルテストには、pytest
とbats
のどちらを使うべきか検討中です。以下にそれぞれの利点と欠点をまとめます。
pytest
- 利点: 追加のパッケージが不要で、Pythonでテストケースを記述できるため、Pythonに慣れている人には使いやすいです。
- 欠点: シェルスクリプトのテストにPythonを使うのは、少し非効率に感じることがあります。また、実行に時間がかかるため、通常のpyファイルとは別に実行できるようにする必要があります。そのため、マーカーの管理が必要になります。
bats
- 利点: シェルスクリプトのテストには自然な選択肢です。
-
欠点: 構文に慣れる必要があり、batsのインストールが必要です。
uv
でのインストールが可能であれば手間は少ないですが、手元ではエラーが発生しました(パッケージ自体は存在しているようです)。
プルリクのマージによるタグの自動更新
mainブランチへのプルリクがマージされたときにtagを自動更新したほうが便利な可能性もあると思いました。ただ、minor、majorバージョンのインクリメントをどう運用するか悩みそうだったのと、複数のPRをたばねたアップデートを作りたいときもあるかと思い、採用しませんでした。ただ、PRのマージとパッチバージョンの更新を厳密に対応させる運用もありだと思いますし、必要に応じてやっても良いとは思います。
参考資料
-
Python パッケージのバージョン管理一元化と自動リリースプロセスを整理する
- モチベーションが近く、広い視点から検討されており参考になりました。
-
uvだけでPythonプロジェクトを管理する
- uvの基本的な使い方の参考にしました。
-
ローカルで GitHub Actions が実行できる act のお作法を整理する
- actのインストールや使い方の参考にしました。
-
actを使ってGitHub Actionsをローカル実行する
- actのインストールや使い方の参考にしました。
-
シェルスクリプトのテストフレームワーク Bats を試してみた
- batsのインストールや使い方の参考にしました。