GitHub Actions CI/CD ワークフローを用いてパッケージ配布物のリリースを公開する#
GitHub Actions CI/CD を使うと、 GitHub プラットフォームで何かイベントが発生するたびに一連のコマンドを実行することができます。よくある選択のひとつは、 push
イベントを引き金にしてあるワークフローを行うというものです。このガイドでは、タグ付きのコミットが push されるたびに Python 配布物を公開するやり方をお見せします。それには pypa/gh-action-pypi-publish GitHub Action を使って出版 <publish> します。また、一時的な格納やソースコード配布物のダウンロードのためにGitHubの upload-artifact
と download-artifact
を使います。
注意
このガイドでは、配布物をビルドするやり方を知っているプロジェクトが既にそんざいして、それが GitHub に置いてある ことを 前提 にしています。このガイドは、また、特定のプロジェクトをビルドするプラットフォームの詳細に立ち入ることはしません。もしあなたがバイナリのコンポーネントを持っているなら、 cibuildwheel にある GitHub Action の例を調べてみてください。
信頼された出版 <publish> を設定する#
このガイド文書は、GitHub Actions CI/CD に接続するために PyPI の 信頼ある出版 <trusted publishing>`の実装に依存しています。これは、生成されるトークンが各プロジェクトでそれぞれ独立に作成されて自動的に期限切れになるというセキュリティ上の理由から推奨されています。さもなければ、 PyPI と TestPyPI の両方について `API トークン を生成する必要があるでしょう。devpi のような第三者パーティのインデックス向けに出版 <publish> する場合には、ユーザ名とパスワードの組み合わせを提供する必要があるかもしれません。
このガイドではPyPIとTestPyPIの両方へのアップロードを実証しますので、ふたつの信頼あるパブリッシャが設定されていることが必要になるでしょう。以下に示すステップによって、新しい PyPI プロジェクト 用の " ペンディングされた" パブリッシャ群を作成する手順を一通りお見せします。しかしながら、あなたが所有者であるならば、任意の既存プロジェクトに 信頼ある出版
を追加することが可能でもあります。
注意
このガイドの以前のバージョンを読み通したことがあるのであれば、PyPI や TestPyPI への直接のアクセスをするために
PYPI_API_TOKEN
とTEST_PYPOI_API_TOKEN
という秘密のトークンを作成したことでしょう。今ではこれらは過去のものとなっていて、旧来の設定を新しいものに置き換える際に GitHub リポジトリから削除し、 PyPI や TestPyPI のアカウント設定から取り除くべきです。
始めましょう! 🚀
新しい PyPI プロジェクト 向けに発行したいと思う名前を、 (
setup.cfg
かpyproject.toml
の中の名称 <name>
の値として、ここで https://test.pypi.org/manage/account/publishing/ へ行って第2のステップを繰り返してください、ただし、今回は GitHub 環境の名称として
testpypi
を入力します。ペンディングされたパブリッシャは今やその初回使用の準備ができており、初回使用を行えば自動的にあなたのプロジェクトを生成します。
注釈
TestPyPI のアカウントを持っていなければ、新たに作成する必要があります。これは通常の PyPI のアカウントとは別のものです。
注意
セキュリティ上の理由から、
pypi
環境での実行の度に、 手動での承認 を要求しなければなりません。
ワークフロー定義を作成する#
GitHub CI/CD ワークフローは、リポジトリの .github/workflows/
ディレクトリに置かれた YAML ファイルで宣言されます。
.github/workflows/publish-to-test-pypi.yml
ファイルを作成しましょう。
意味のある名前で始めて、 GitHub がこのワークフローを走らせるべきイベントを定義しましょう:
name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI
on: push
プロジェクトをチェックアウトして配布物をビルドする#
PyPI と TestPyPI に向けて出版 <publish> するためには、二つのジョブを定義しなければならず、また、配布物のパッケージ群をビルドするための追加のジョブも定義しなければなりません。
最初に、あなたのプロジェクトの dist パッケージをビルドして、その後の使用のために保存するジョブを定義しましょう:
jobs:
build:
name: Build distribution 📦
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.x"
これによって、あなたのリポジトリを CI ランナーにダウンロードして、利用可能な最新の Python 3 リリースをインストールしアクティベートすることになります。
そして、今や我々はソースコードから dist 配布物をビルドして保存しておくことができます。この例では、パッケージを build
することになります。ですから、これをステップリストに加えましょう:
- name: Install pypa/build
run: >-
python3 -m
pip install
build
--user
- name: Build a binary wheel and a source tarball
run: python3 -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v3
with:
name: python-package-distributions
path: dist/
ワークフローのジョブ環境を定義する#
さて、PyPI へ発行 <publish> される予定のジョブに初期設定を追加しましょう。それは、後ほど定義するであろうコマンドを実行するプロセスになるでしょう。このガイド文書では、
publish-to-pypi:
name: >-
Publish Python 🐍 distribution 📦 to PyPI
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
needs:
- build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/<package-name> # Replace <package-name> with your PyPI project name
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
これは、当該コミットがタグ付きである時にだけ PyPI 公開 <publishing> ワークフローが起動されることをも保証するものです。
PyPI へ配布物を公開する <publishng>#
最後に、次の手続きを末尾に追加しましょう:
steps:
- name: Download all the dists
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
この手順では、保存されている配布パッケージが download-artifact アクションによってダウンロードされた後に pypa/gh-action-pypi-publish という GitHub Action: を使って、 dist/
フォルダの内容物を無条件に PyPI へアップロードします。
配布パッケージに署名する#
以下に述べるジョブは、`CPython <https://www.python.org/download/sigstore/> に署名するのに使われる`_ ものと同じアーティファクトである Sigstore を使って配布パッケージに署名します。
第一に、配布物パッケージに署名するのに sigstore/gh-action-sigstore-python GitHub Action を使います。次の段階では、現在のタグから空の GitHub Release が``hg`` CLI を使って作成されます。この段階をさらにカスタマイズすることができる点に留意してください。リファレンスとしては、gh リリース説明文書 を見てください。
最後に、署名された配布物が GitHub Release へアップロードされます。
github-release:
name: >-
Sign the Python 🐍 distribution 📦 with Sigstore
and upload them to GitHub Release
needs:
- publish-to-pypi
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
id-token: write # IMPORTANT: mandatory for sigstore
steps:
- name: Download all the dists
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v1.2.3
with:
inputs: >-
./dist/*.tar.gz
./dist/*.whl
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release create
'${{ github.ref_name }}'
--repo '${{ github.repository }}'
--notes ""
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
'${{ github.ref_name }}' dist/**
--repo '${{ github.repository }}'
注釈
これは、 PyPI から削除された GPG 署名を代替するものです。しかしながら、このジョブは PyPI へアップロードする時に必須のものと言うわけではなく、省略することもできます。
TestPyPI へ公開 <publish> するもうひとつのワークフロー#
さて、これらのステップを繰り返して、 jobs
セクションに TestPyPI パッケージインデックスに公開 <publish> するようなもうひとつのジョブを作成しましょう:
publish-to-testpypi:
name: Publish Python 🐍 distribution 📦 to TestPyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/<package-name>
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/
- name: Publish distribution 📦 to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
Tip
testpypi
Github 環境において手動の承認を要求することは、main ブランチへのコミット毎に走るように設計されていることから、典型的な場合には不必要とされていて、使われるとすればリリースの公開 <publish> パイプラインが健全であることを示すためであることが多いのです。
CI/CD ワークフローの全体像#
この段落には、上述のガイド文書に従った場合のワークフローの全体像を披露します。
Click here to display the entire GitHub Actions CI/CD workflow definition
name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI
on: push
jobs:
build:
name: Build distribution 📦
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.x"
- name: Install pypa/build
run: >-
python3 -m
pip install
build
--user
- name: Build a binary wheel and a source tarball
run: python3 -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v3
with:
name: python-package-distributions
path: dist/
publish-to-pypi:
name: >-
Publish Python 🐍 distribution 📦 to PyPI
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
needs:
- build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/<package-name> # Replace <package-name> with your PyPI project name
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
github-release:
name: >-
Sign the Python 🐍 distribution 📦 with Sigstore
and upload them to GitHub Release
needs:
- publish-to-pypi
runs-on: ubuntu-latest
permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
id-token: write # IMPORTANT: mandatory for sigstore
steps:
- name: Download all the dists
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v1.2.3
with:
inputs: >-
./dist/*.tar.gz
./dist/*.whl
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release create
'${{ github.ref_name }}'
--repo '${{ github.repository }}'
--notes ""
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
'${{ github.ref_name }}' dist/**
--repo '${{ github.repository }}'
publish-to-testpypi:
name: Publish Python 🐍 distribution 📦 to TestPyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/<package-name>
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v3
with:
name: python-package-distributions
path: dist/
- name: Publish distribution 📦 to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
これでできましたよ、皆さん!#
これで、手元の Git リポジトリをリモートの GitHub にタグ付きのコミットをプッシュする時はいつでも、このワークフローがそれを PyPI へ公開します。そして、プッシュしさえすればいつでも TestPyPI で公開されますので、アルファ版のユーザにテストビルドを提供するためにも、あなたのリリースパイプラインが健全な状態に保たれていることを確認するためにも役に立ちます!
注意
If your repository has frequent commit activity and every push is uploaded to TestPyPI as described, the project might exceed the PyPI project size limit. The limit could be increased, but a better solution may constitute to use a PyPI-compatible server like pypiserver in the CI for testing purposes.
注釈
統合された GitHub Actions を最新版に保ち、頻繁にアップデートすることを推奨します。