GitHub Actions CI/CD ワークフローを用いてパッケージ配布物のリリースを公開する

GitHub Actions CI/CD を使うと、 GitHub プラットフォームで何かイベントが発生するたびに一連のコマンドを実行することができます。よくある選択のひとつは、 push イベントを引き金にしてあるワークフローを行うというものです。このガイドでは、タグ付きのコミットが push されるたびに Python 配布物を公開するやり方をお見せします。それには pypa/gh-action-pypi-publish GitHub Action を使って出版 <publish> します。また、一時的な格納やソースコード配布物のダウンロードのためにGitHubの upload-artifactdownload-artifact を使います。

注意

このガイドでは、配布物をビルドするやり方を知っているプロジェクトが既にそんざいして、それが GitHub に置いてある ことを 前提 にしています。このガイドは、また、特定のプロジェクトをビルドするプラットフォームの詳細に立ち入ることはしません。もしあなたがバイナリのコンポーネントを持っているなら、 cibuildwheel にある GitHub Action の例を調べてみてください。

信頼された出版 <Trusted Publishing> を設定する

このガイド文書は、GitHub Actions CI/CD に接続するために PyPI の Trusted Publishing の実装に依存しています。これは、生成されるトークンが各プロジェクトでそれぞれ独立に作成されて自動的に期限切れになるというセキュリティ上の理由から推奨されています。さもなければ、 PyPI と TestPyPI の両方について API トークン を生成する必要があるでしょう。devpi のような第三者パーティのインデックス向けに出版 <publish> する場合には、ユーザ名とパスワードの組み合わせを提供する必要があるかもしれません。

このガイドではPyPIとTestPyPIの両方へのアップロードを実証しますので、ふたつの信頼あるパブリッシャが設定されていることが必要になるでしょう。以下に示すステップによって、新しい PyPI project 用の " ペンディングされた" パブリッシャ群を作成する手順を一通りお見せします。しかしながら、あなたが所有者であるならば、任意の既存プロジェクトに Trusted Publishing を追加することが可能でもあります。

注意

このガイドの以前のバージョンを読み通したことがあるのであれば、PyPI や TestPyPI への直接のアクセスをするために PYPI_API_TOKENTEST_PYPOI_API_TOKEN という秘密のトークンを作成したことでしょう。今ではこれらは過去のものとなっていて、旧来の設定を新しいものに置き換える際に GitHub リポジトリから削除し、 PyPI や TestPyPI のアカウント設定から取り除くべきです。

始めましょう! 🚀

  1. https://pypi.org/manage/account/publishing/ へ行く。

  2. 新しい PyPI プロジェクト 向けに発行したいと思う名前を (setup.cfgpyproject.toml の中の 名称 <name> の値として)、GitHub のリポジトリ所有者の名称 (org または user) と、リポジトリの名称と、 .github/ フォルダの下のリリースワークフローの名称を記入するには、 ワークフロー定義 をみてください。最後に、これからリポジトリ内に設定しようとするGutHub 環境 (pypi) の名称を追加してください。トラステッドパブリッシャを登録してください。

  3. ここで https://test.pypi.org/manage/account/publishing/ へ行って第2のステップを繰り返してください、ただし、今回は GitHub 環境の名称として testpypi を入力します。

  4. ペンディングされたパブリッシャは今やその初回使用の準備ができており、初回使用を行えば自動的にあなたのプロジェクトを生成します。

    注釈

    TestPyPI のアカウントを持っていなければ、新たに作成する必要があります。これは通常の PyPI のアカウントとは別のものです。

    注意

    セキュリティ上の理由から、pypi 環境の実行の度に、 手動での承認 を要求しなければなりません。

ワークフロー定義を作成する

GitHub CI/CD ワークフローは、リポジトリの .github/workflows/ ディレクトリに置かれた YAML ファイルで宣言されます。

.github/workflows/publish-to-pypi.yml ファイルを作成しましょう。

意味のある名前で始めて、 GitHub がこのワークフローを走らせるべきイベントを定義しましょう:

name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI

on: push

プロジェクトをチェックアウトして配布物をビルドする

PyPI と TestPyPI に向けて出版 <publish> するためには、二つのジョブを定義しなければならず、また、配布物のパッケージ群をビルドするための追加のジョブも定義しなければなりません。

最初に、あなたのプロジェクトの dist パッケージをビルドして、その後の使用のために保存するジョブを定義しましょう:

Tip

If you adapt this workflow to build multiple platform-specific wheels, use uniquely named artifacts for each build job and adjust the download step accordingly. The cibuildwheel GitHub Actions examples show a fuller wheel matrix layout.

jobs:
  build:
    name: Build distribution 📦
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - name: Set up Python
      uses: actions/setup-python@v6
      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@v5
      with:
        name: python-package-distributions
        path: dist/

ワークフローのジョブ環境を定義する

さて、PyPI へ公開 <publish> する予定のジョブに初期設定を追加しましょう。それは、後ほど定義するであろうコマンドを実行するプロセスです。このガイド文書では、GitHub Actions が提供している最新版の Ubuntu LTS 安定版バージョンです。これは、また、そのコンテキスト内でジョブが走る GitHub 環境 <GitHub Environment> を定義するものでもあり、 GitHub の UI の中に申し分なく表示される URL でもあります。さらに、それは、 pypi-publish アクションがシークレットなしの PyPI への信頼ある公開を実装するために必要となる OpenID Connect のトークンを取得することを可能にするものでもあります。

  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@v6
      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 へアップロードします。

Tip

バージョン v1.11.0 以降、 pypa/gh-action-pypi-publish は、デフォルトで各配布物に対する PEP 740 互換の証明書を生成しアップロードするようになりました。手動の署名手順はもはや必要ありません。

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@v6
      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@v6
      with:
        persist-credentials: false
    - name: Set up Python
      uses: actions/setup-python@v6
      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@v5
      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@v6
      with:
        name: python-package-distributions
        path: dist/
    - name: Publish distribution 📦 to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

  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@v6
      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 で公開されますので、アルファ版のユーザにテストビルドを提供するためにも、あなたのリリースパイプラインが健全な状態に保たれていることを確認するためにも役に立ちます!

注意

リポジトリで活発に頻繁なコミットがあって、前述のようにあらゆるプッシュが TestPyPI へアップロードされているなら、そのプロジェクトは PyPI プロジェクトサイズ制限 を超過するかもしれません。この制限を緩和することもできますが、より良い解決策は試験目的の CI の中で pypiserver のような PyPI と互換のあるサーバを使うように構成することかもしれません。

注釈

統合された GitHub Actions を最新版に保ち、頻繁にアップデートすることを推奨します。