GitHubアクションを介した安全で自動化された発行のための信頼された発行の設定#

In the previous Python packaging lessons, you learned:

  1. How to create a Python package

  2. How to publish the code to PyPI and Conda

学習目標

In this lesson, you will learn how to:

  • GitHubアクションでパッケージの構築と公開を自動化する

  • プロジェクトの{term}「信頼できる発行」を構成する

  • ワークフローファイル内のGitHubアクションハッシュとバージョンを使用してワークフローを保護する

このチュートリアルでは、プロジェクトがGitHubでホストされており、プロジェクトからPyPIにパッケージを公開することを前提としています。

GitHubアクションでリリースジョブを構成する#

GitHubアクション[^ gha]は、プロジェクトのGitHubリポジトリから直接、ソフトウェアワークフローを自動化するためにGitHubが提供するインフラストラクチャです。プルリクエストごとに自動テストを構成したり、ドキュメントの公開を自動化したり、プロジェクトのWebページの作成を自動化したり、リリースプロセスを自動化したりできます。このレッスンでは、Pythonパッケージを[PyPI ]( publish - pypi)に安全にリリースして公開するためのアクションの使用に焦点を当てます。

信頼できる出版が重要な理由

信頼できるパブリッシングがなぜそれほど重要なのか疑問に思っている方は、こちらのブログ記事をご覧ください:をご覧ください。この記事では、パブリッシングワークフローをロックしないと何が起こるかについて詳しく説明します。

Step 0: Create a release workflow#

開始するには、プロジェクトの'.github/workflows ディレクトリの下に release.yaml 'という名前のファイルを作成します。'.github/workflowsディレクトリが存在しない場合は、作成できます。すべてのGitHubアクションは、'.github/workflows ディレクトリのYAMLファイルを介して構成されることがGitHubの規則です。

ワークフローファイルに名前を付ける

ワークフローファイルには任意の名前を付けることができます。「release.yaml」のようなシンプルで表現力豊かなものを使用することをおすすめします。そうすれば、あなた自身、あなたの将来の自分、そしてあなたのプロジェクトに取り組むコントリビューターは、ワークフローが何をしているのかを正確に知ることができます。

ステップ1 :ワークフローに名前を付ける#

At the top of the release.yaml file, type the following:

name: Release

これにより、GitHubリポジトリの[アクション]タブでこのGitHubアクションのすべての実行をすばやく見つけるために使用できるワークフローの名前が提供されます。

リリース用に構成されたワークフローの例を示す図。上部の「1」という赤いボックスには、GitHubリポジトリの「アクション」タブが表示されます。左側の「2」という赤いボックスには、この手順で設定したワークフローの名前「Release」が表示されます。最後に、中央の「3」という赤いボックスには、パッケージの「1.0」および「1.0.1」リリースのワークフローのいくつかの実行が表示されます。

この画像は、リリース用に構成されたワークフローの例を示しています。上部の「1」という赤いボックスには、GitHubリポジトリの「アクション」タブが表示されます。左側の「2」という赤いボックスには、この手順で設定したワークフローの名前が表示されます。最後に、中央の「3」という赤いボックスには、パッケージの「1.0」および「1.0.1」リリースのワークフローのいくつかの実行が表示されます。#

ステップ2 :ワークフローにトリガーを追加する#

すべてのGitHubアクションワークフローは、特定の条件が満たされたときに実行されます。この場合、リリースワークフローは、リポジトリの所有者がパッケージの新しいリリースを作成したときにのみ実行する必要があると想定しています。リリースを作成して公開するときに実行されることを確認するために、release.yamlファイルに次のものを追加します。

on:
  release:
    types:
      - published  # This tells GitHub to only run this workflow when a release of your package is published

ステップ3 :ワークフローのジョブを構成する#

GitHubアクションワークフローファイルには、独立して実行される複数のジョブを含めることができます。各ジョブには複数の*ステップを含めることもできます。*トリガーされると、GitHubアクションはワークフロー内のすべてのジョブを実行します(条件付き要件を持つステップを除く)。

注釈

ジョブとステップは、特定の基準が存在する場合にのみ実行できる条件付きロジックを持つこともできます。たとえば、パッケージのリリースが行われた場合にのみ、PyPIに公開するジョブステップが必要な場合があります。ただし、新しいプルリクエストをマージするたびに、パッケージの構築をテストすることをお勧めします。

リリースジョブの場合は、リポジトリのクローンを作成するか、チェックアウトする必要があります。「アクション/チェックアウト」アクションを使用して、コードをチェックアウトできます。次に、[Hatch ]( get - to - know - hatch )をインストールして使用し、パッケージを構築します。

また、ワークフローを実行するためにGitHubが使用しているマシンでHatchを設定する必要があります。

最小限のジョブ定義は次のようになります。

jobs:
  build_package:
    name: Build the package
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v5
    - name: Setup Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.12" # Select the version that you want to build your package on
    - name: Upgrade pip, install Hatch, and check Hatch version
        run: |
          pip install --upgrade pip
          pip install --upgrade hatch
          hatch --version # Verify that Hatch is installed

上記では、各アクションステップのバージョンを指定しています。action/checkout @ v 5は、GitHubにチェックアウトアクションのバージョン5を使用するように指示します。チェックアウトアクションは、リポジトリからコードをチェックアウトします。この場合、コードはパッケージのビルドに使用されます。

次に、ワークフローを保護(または「強化」)するためのより良い方法について学びます

ステップ4 : GitHub Actionsワークフローを保護する#

セキュリティと読みやすさを向上させるために構成したGitHubアクションワークフローには、いくつかの改善点があります。

まず、ワークフロー実行中に生成されたログの読みやすさを高めるために、プロセスの関連ステップに名前を付けることができます。これは、`name :'行を使用して達成できます。

さらに重要なことに、既存のアクションを使用するたびに(「使用」を介して)、そのアクションをコミットハッシュにピン留めする必要があります。アクションを固定すると、悪意のあるユーザーがアクションを引き継いだ場合、リポジトリに影響を与えることはできません( GitHubアクションによるサプライチェーン攻撃の例は、最近の「tj - actions/changed - files」攻撃[^ changed - files - supply - chain - attack]です)。

Tip

リポジトリでDependabot [^ dependabot]を有効にすると、アクションが最新の状態に保たれます。dependabotツールは、アクションバージョンを任意の頻度で更新するプルリクエストを開きます。

したがって、使用すべきワークフローは次のようにする必要があります。

name: Release

on:
  release:
    types:
      - published

permissions: {} # no permissions to the token at global level

jobs:
  build_package:
    name: Build the package
    runs-on: ubuntu-latest
    permissions:
      contents: read # this job only needs read access
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - name: Setup Python
        uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
        with:
          python-version: "3.12" # Select the version that you want to build your package on
      - name: Upgrade pip, install Hatch, and check Hatch version
        run: |
          pip install --upgrade pip
          pip install --upgrade hatch
          hatch --version # Verify that Hatch is installed
      - name: Build artifacts

これで、'.github/workflows/release.yaml `ファイルをリポジトリにコミットし、GitHubにプッシュできます。

この時点で、GitHubでプロジェクトの新しいリリースを作成すると、構成されたワークフローが実行され、ホイールが構築されます。残念ながら、ホイールはランナーでのみ使用でき、ワークフロー実行の最後に削除されます。

ステップ5 :構築されたアーティファクトをGitHubアーティファクトにアップロードする#

ホイールにアクセスできるようにするには、ジョブ定義にもう1つステップを追加する必要があります。アーティファクト一時エリア[^ github - artifacts]にアップロードします。次のものをrelease.yamlファイルに追加します。

        with:
          path: dist/
          name: dist.zip
          if-no-files-found: error
          retention-days: 1

アーティファクトパラメータのアップロード

上記では、1日後に削除されるようにアーティファクトを設定しました。GitHubアクション上のアーティファクトストレージは一時的なものです。ユーザーはGitHubアーティファクトからパッケージをダウンロードしないでください。

また、`dist /'ディレクトリが存在しない場合にエラーが発生するようにリリースジョブを設定しました。これは、(前のステップからの)「ハッチビルド」がパッケージのビルドに失敗したため、リリースするものがないことを意味します。

この時点で、'release.yaml`をGitHubにプッシュして新しいリリースを作成すると、GitHubアクションジョブは次のようになります。

  • hashite

  • リポジトリのクローンを作成し、

  • install and set up Hatch,

  • build your package and

  • パッケージをアーカイブとしてアーティファクトストレージにアップロードします。

実行が完了したばかりのリリースワークフローの例を示す図。ログの各ステップは、ワークフロー定義の1つのステップと一致します。

この図は、実行が完了したばかりのリリースワークフローの例を示しています。ログの各ステップは、ワークフロー定義の1つのステップと一致します。#

GitHubのワークフロー実行ページの下部に、実行時に生成され、このストレージ領域にアップロードされたアーティファクトのセクションが表示されます。

リリースワークフローによって生成されたアーティファクトの例を示す図。

この図は、上記のリリースワークフローによって生成されたアーティファクトを示しています。1日以上前にワークフローが実行されたため、期限切れとしてマークされました。#

アーティファクトを(期限が切れる前に)ダウンロードし、解凍して、中に入っているホイールを取り付けることができます。ただし、これは、構築されたホイールをテストする場合にのみ行う必要があります。次に、信頼できる発行を使用してPyPIへのアップロードを設定します。

PyPIへの自動発行を設定する#

GitHub Actionsを使用して上記で構成したジョブは、コードを使用してパッケージをビルドします。まだPyPIにアップロードする必要があります。同じジョブからパッケージをアップロードすることもできますが、タスクの分離を維持するために別のものを作成することをお勧めします。これが、前のセクションでアーティファクトを一時ストレージにアップロードした理由です。

新しいジョブでは、そこからパッケージをダウンロードし、PyPIにアップロードします。「ビルド」ジョブは他に何もしないので、リリース前にパッケージが危険にさらされる可能性はありません。

ステップ1 :アップロードジョブを追加する#

'release.yaml`ファイルで、前のセクションで定義したジョブの後に次の新しいジョブを追加します。

    name: Publish release to PyPI
    needs: [build_package]
    runs-on: ubuntu-latest
    environment:
      name: pypi
      url: <URL TO YOUR PROJECT HERE>
    permissions:
      contents: read # this job needs read access
      id-token: write # but also needs to be able to write the publishing token
    steps:
      - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: dist.zip
          path: dist/
      - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2

必ずURLを変更してください

PyPI上のパッケージのURLに`url :'値を変更することを忘れないでください!

このジョブには2つのステップがあります。

  • 「download - artifact」を使用して、前のジョブで構築されたアーティファクトをダウンロードします

  • パッケージをPyPIに公開するために`gh - action - pypi - publish'を使用します。

あと少しです! !これで、PyPIでプロジェクトの信頼できる公開を有効にするだけです。そして、あなたの仕事は完了しました!

ステップ2 : PyPIで信頼できる公開を有効にする#

PyPIの信頼できるパブリッシャーワークフローを示す図:ステップ1はGitHubを介して配布ファイルをビルド、ステップ2は信頼できる環境(PyPI)を使用、ステップ3は安全にPyPIにアップロード。GitHub ActionsからPython Package Indexに接続する錠前アイコンで信頼の連鎖を表示。

このレッスンは、Pythonのパッケージングを始めるのに役立つ一連のレッスンの最初のものです。#

信頼された公開が作成される前は、GitHub Actions から PyPI にアップロードするためには、gh-action-pypi-publish ステップにユーザー名とパスワードを引数として追加する必要がありました。 While documentation recommends using the GitHub's secrets environment for the password/token, in several cases, users were pasting the password directly into the workflow file. Furthermore, accidental leakage of the password or token could allow attackers to publish new packages using your account, until you discover the compromise and revoke the leaked credentials.

これらの事故を防ぎ、サプライチェーンのセキュリティを向上させるために、開発者はTrusted Publishingを作成しました。Trusted Publishingを使用すると、PyPIで公開ワークフローを登録し、そのワークフローをパッケージの公開を許可された自動化ワークフロー(例:GitHub Actions)にマッピングすることができます。

信頼された発行者のワークフローでは、トークンやパスワードの値を入力する必要はありません。それはあなたの間の安全な接続です

GitHub Actions 以外での信頼された公開

Trusted Publishing supports other automation platforms, beyond GitHub Actions. It is also possible to configure a trusted publisher for multiple workflows or multiple publishers for the same package. これらは高度な使用例であり、このレッスンの範囲外です。

このレッスンでは、すでにPyPIに存在するプロジェクトのために信頼できる公開者を設定することに焦点を当てます。PyPI公開に関するレッスンを完了していれば、このプロジェクトはすでに作成済みのはずです。

このセットアップ手順は、プロジェクトに対して一度だけ実行する必要があります。今後のリリースでは、release.yamlで設定しているGitHub Actionsのワークフローのみが実行されます。

PyPIの「あなたのプロジェクト」ページで、設定したいプロジェクトの「管理」をクリックしてください。

PyPI の「Your projects」ページのスクリーンショットを示すグラフィック。あるプロジェクトの「Manage」ボタンが強調表示されている。

この画像にはいくつかのプロジェクトが表示されています。信頼された公開の設定を行いたいプロジェクトの「管理」ボタンが強調表示されています。#

次に、プロジェクトのサイドバーで「公開」をクリックします。

1つのプロジェクトの管理ページを示すグラフィック。サイドバーの「公開」リンクが強調表示されている。

「管理」ボタンをクリックすると、プロジェクトのページに移動します。サイドバーには、「公開」オプションがあり、ここで強調表示されています。#

これにより、プロジェクトのパブリッシャー設定ページに移動します。信頼されたパブリッシャーは、ここでのフォームを通じて設定できます。次の情報を使用してGitHubフォームに記入してください:

  • オーナー: プロジェクトを所有する組織のGitHub組織名。これがあなたの個人プロジェクトの場合は、ここにあなたのGitHubユーザー名を使用してください。

  • リポジトリ名:プロジェクトを含むリポジトリの名前。

  • ワークフロー名: このガイドに従った場合、release.yaml であるべきです。これは、先ほど設定したワークフローです。

  • 環境名: release.yamlで設定したので、pypiであるべきです。

このフォームに入力して「追加」をクリックすると、パブリッシャーが設定され、パッケージの新しいリリースの公開に使用できるようになります。

完全に強化されたGitHub Actionsリリースワークフロー#

より良いセキュリティのために、ワークフロー内の各ジョブで使用される GitHub トークンの権限を制御することも推奨されます。権限はジョブレベルでスコープされ、可能な限り最小限にするべきです。信頼された公開を設定し、これも行うワークフローは次のとおりです。

name: Release

on:
  release:
    types:
      - published

permissions: {} # no permissions to the token at global level

jobs:
  build_package:
    name: Build the package
    runs-on: ubuntu-latest
    permissions:
      contents: read # this job only needs read access
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - name: Setup Python
        uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
        with:
          python-version: "3.12" # Select the version that you want to build your package on
      - name: Upgrade pip, install Hatch, and check Hatch version
        run: |
          pip install --upgrade pip
          pip install --upgrade hatch
          hatch --version # Verify that Hatch is installed
      - name: Build artifacts
        run: hatch build
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          path: dist/
          name: dist.zip
          if-no-files-found: error
          retention-days: 1

  publish_release_to_pypi:
    name: Publish release to PyPI
    needs: [build_package]
    runs-on: ubuntu-latest
    environment:
      name: pypi
      url: <URL TO YOUR PROJECT HERE>
    permissions:
      contents: read # this job needs read access
      id-token: write # but also needs to be able to write the publishing token
    steps:
      - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: dist.zip
          path: dist/
      - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2

上記を release.yaml ファイルにコピーできます。url: フィールドを更新し、PyPI で信頼できる公開設定を行うだけで済みます。

注釈

上記のワークフローは、現在のGitHubアクションのバージョンに合わせて最新であるはずです。ただし、将来的にアクションのバージョンを更新するためにDependabotを有効にしておくと良いでしょう。

あなたはプロジェクトに対して信頼できる公開を有効にしました#

おめでとうございます!!これで、新しいバージョンがGitHubでタグ付けされたときに、プロジェクトが安全にリリースできるように設定されました。 私たちが設定したワークフローは、タグを付けている正確なバージョンのコードからパッケージをビルドします。 これは、リリースしたパッケージがコードで記載されている通りのことを正確に行うことをユーザーに保証するものです。 あなたのパッケージから発生するサプライチェーン関連の脆弱性の可能性はほとんどありません!もし本物のPyPIで実際に使用できるパッケージがある場合は、同じ手順に従って安全に公開することができます。

脚注#