#10 PyPI公開自動化 (Trusted Publishing) - Python OSS開発記録
python 14 min read

#10 PyPI公開自動化 (Trusted Publishing) - Python OSS開発記録

avatar-m-1

karrinn

著者

タグをpushするたびに「PyPIにログインして、パッケージアップロードして...」ってやっていませんか?
Trusted Publishingを使えば、シークレット不要で自動公開できます。wagtail-reusable-blocksでの設定を振り返ります。

KEY TAKEAWAYS

この記事でわかること

  • リリース自動化の本質(PyPI/k8s/EC2、考え方は同じ)
  • Trusted Publishingの仕組みとメリット
  • TestPyPI → PyPIの2フェーズリリースの実装

用語の定義

TERM 01

Trusted Publishing

PyPIの公式機能です。OIDC (OpenID Connect) を使った認証方式で、APIトークン不要でパッケージを公開できます。GitHub Actionsと連携し、シークレット管理が不要になります。2025年9月時点で100万ファイル以上がこの方式で公開されています。

TERM 02

TestPyPI

PyPIのテスト環境です。本番公開前に動作確認ができます。データは定期的にリセットされるので、失敗を恐れず試せる場所として活用できます。URL: https://test.pypi.org/

TERM 03

OIDC (OpenID Connect)

OAuth 2.0ベースの認証プロトコルです。GitHub ActionsがPyPIに対して「このワークフローは信頼できる」と証明する仕組みで、短命トークン(最大15分で失効)を使うためトークン漏洩リスクがありません。

TERM 04

GitHub Environments

GitHub Actionsのデプロイ先環境設定です。承認ゲート・シークレット管理・保護ルールを設定でき、testpypi/pypi等の環境を分離できます。

Source: PyPI Trusted Publishers Documentation, Internals and Technical Details

リリース自動化の本質

この記事ではPyPI公開を扱いますが、企業プロジェクトのk8s/EC2デプロイも考え方は全く同じです。

共通のパターン

OSS (Pythonパッケージ)

PyPI公開

  1. タグpush (v0.1.0)
  2. GitHub Actions実行
  3. パッケージビルド
  4. TestPyPI公開
  5. PyPI公開

企業 (Kubernetes)

k8sデプロイ

  1. タグpush (v0.1.0)
  2. CI/CD実行
  3. Dockerイメージビルド
  4. Staging環境デプロイ
  5. Production環境デプロイ

企業 (EC2)

EC2デプロイ

  1. タグpush (v0.1.0)
  2. CI/CD実行
  3. アプリケーションビルド
  4. Staging EC2デプロイ
  5. Production EC2デプロイ

本質は同じ: タグ → 自動リリース

デプロイ先がPyPI/k8s/EC2のどれでも、「バージョンタグをpushしたら自動でリリース」という仕組みは同じです。 CI/CDツールも、GitHub Actionsでなくても構いません(GitLab CI、CircleCI、Jenkins等)。 この記事ではwagtail-reusable-blocksの実例(PyPI公開)を通じて、リリース自動化の考え方を学びます。

Trusted Publishingのメリット

wagtail-reusable-blocksはOSSのPythonパッケージなので、PyPIに公開します。 従来は「APIトークンをGitHub Secretsに登録して...」という面倒な設定が必要でしたが、Trusted Publishingを使えばシークレット不要です。

比較項目従来(APIトークン方式)Trusted Publishing (OIDC)
初期設定PyPIでトークン発行 → GitHub Secretsに登録PyPI側で信頼設定のみ
シークレット管理必要(トークン漏洩リスクあり)不要(漏洩リスクなし)
トークン有効期限長期間有効(手動で取り消すまで)短命トークン(最大15分で自動失効)
メンテナンストークン更新が必要メンテナンスフリー
アテステーション手動で設定が必要v1.11.0以降デフォルトで有効(PEP 740準拠)

Source: Introducing 'Trusted Publishers' - PyPI Blog, Security Model and Considerations

PyPI側でTrusted Publisher設定

まずはPyPI(およびTestPyPI)で信頼するリポジトリとワークフローを登録します。

設定項目

PyPIにログインし、プロジェクトページ → Publishing → Add a new publisher から設定します。 まだプロジェクトが存在しない場合は、「Pending publisher」として事前登録することも可能です。

  • PyPI Project Name: パッケージ名
  • Owner: GitHubユーザー名またはOrg名
  • Repository: リポジトリ名
  • Workflow name: ワークフローファイル名
  • Environment name: 環境名(pypi または testpypi)

TestPyPIも同様に設定してください(Environment name: testpypi)

PyPI設定例
PyPI Project Name: wagtail-reusable-blocks
Owner: kkm-horikawa
Repository name: wagtail-reusable-blocks
Workflow name: publish.yml
Environment name: pypi

Source: Publishing with a Trusted Publisher - PyPI Docs, Creating a PyPI Project with a Trusted Publisher

GitHub Actions ワークフローの設定

次にGitHub側でワークフローを作成します。wagtail-reusable-blocksの実際の設定を見ていきましょう。

トリガー設定

v*パターンのタグがpushされたときにワークフローが実行されます。 3つのジョブで構成されています。

  • build: パッケージをビルド
  • publish-testpypi: TestPyPIに公開
  • publish-pypi: PyPIに公開
publish.yml
name: Publish to PyPI

on:
push:
tags:
- "v*"

jobs:
build: ...
publish-testpypi: ...
publish-pypi: ...

ジョブ1: build(パッケージビルド)

ソースコードをチェックアウトし、uvでパッケージをビルドします。

  • fetch-depth: 0: 全履歴取得(hatch-vcsがタグを読むため)
  • uv build: パッケージビルド
  • upload-artifact: 後続ジョブに渡す
publish.yml
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Build package
run: uv build
- name: Upload dist
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

ジョブ2: publish-testpypi

TestPyPIに公開します。シークレット不要なのがポイントです。

  • environment: testpypi: 環境指定
  • id-token: write: OIDC有効化(必須)
  • repository-url: TestPyPI指定
publish.yml
publish-testpypi:
needs: build
runs-on: ubuntu-latest
environment: testpypi
permissions:
id-token: write
steps:
- name: Download dist
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/

ジョブ3: publish-pypi

TestPyPI成功後、PyPIに公開します。repository-url省略時はPyPIがデフォルトです。

  • needs: publish-testpypi: TestPyPI成功後のみ実行
  • environment: pypi: 本番環境指定
  • TestPyPI → PyPI の順でリリース
publish.yml
publish-pypi:
needs: publish-testpypi
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
steps:
- name: Download dist
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

Source: pypa/gh-action-pypi-publish - GitHub, Python Packaging User Guide

実際の動作フロー

タグをpushしてから公開されるまでの流れを見ていきましょう。

  • 01

    開発者: タグをpush

    git tag v0.1.0 && git push origin v0.1.0

  • 02

    GitHub Actions: ワークフロー実行開始

    トリガー条件: tags: v*

  • 03

    ジョブ1 (build): パッケージビルド

    uvでビルド → dist/ に .whl と .tar.gz を生成

  • 04

    ジョブ2 (publish-testpypi): TestPyPIに公開

    OIDC認証 → TestPyPIに公開 → 動作確認可能

  • 05

    ジョブ3 (publish-pypi): PyPIに公開

    OIDC認証 → PyPIに公開 → pip install で誰でもインストール可能に

所要時間: 約5分

開発者がやることはタグをpushするだけ。あとは全て自動でビルド・公開されます。

OSS vs 企業プロジェクト: リリース自動化の応用

OSS: PyPI公開の詳細

wagtail-reusable-blocksでは、TestPyPI → PyPI の2フェーズリリースを採用しています。

なぜTestPyPI経由なのか

  • 本番公開前の最終確認: TestPyPIで実際にインストールして動作確認できる
  • ミスの検出: パッケージ名・依存関係・メタデータの間違いを事前に発見
  • ロールバック不可: PyPIは一度公開したバージョンを削除できない(TestPyPIで確認必須)
  • 心理的安全性: 「失敗してもTestPyPIだけ」という安心感

実際のリリースフロー

  1. git tag v0.1.0 && git push origin v0.1.0 でタグpush
  2. GitHub Actions自動実行
  3. TestPyPIに公開 → pip install --index-url https://test.pypi.org/simple/ wagtail-reusable-blocks で確認
  4. 問題なければPyPIに自動公開
  5. pip install wagtail-reusable-blocks で誰でもインストール可能に
Trusted Publishingのメリット再確認

APIトークン管理不要、漏洩リスクなし、メンテナンスフリー。 「シークレット管理を忘れてトークン漏洩」という事故が起きない

企業: k8s/EC2デプロイへの応用

企業プロジェクトでは、PyPIの代わりにk8sクラスタやEC2にデプロイします。 でも考え方は全く同じです。

Kubernetesデプロイの例

ワークフローの流れ(PyPIと対応)

  1. git tag v0.1.0 && git push origin v0.1.0トリガーは同じ
  2. CI/CD実行(GitHub Actions / GitLab CI / CircleCI等)
  3. Dockerイメージビルド ← PyPIのパッケージビルドと同じ役割
  4. Staging k8sクラスタにデプロイ ← TestPyPIと同じ役割
  5. Production k8sクラスタにデプロイ ← PyPIと同じ役割

なぜ立ち上げ時点で組むべきか

リリース自動化は、後から本番環境で検証するのが非常に難しいです。

立ち上げ時点で組んだ場合

  • 開発環境で自動デプロイを検証
  • Staging環境で本番同様の動作確認
  • 問題があれば何度でも修正可能
  • 本番リリース時には既に安定稼働
  • 誰でもリリースできる

後から組もうとした場合

  • 本番稼働中なのでテストできない
  • 「失敗したら本番が止まる」と萎縮
  • 手動運用が定着して変更困難
  • 結局、手動デプロイのまま
  • 永遠に属人化
技術的理解のあるPMが必要な理由

リリース自動化を設計・実装できるのは、技術的理解のある人だけです。 プロジェクト立ち上げ時点で、技術的理解のある人がPMとして仕切り、自動化を組むべきです。後からでは遅い。

よくある質問

A: はい、完全無料です。

PyPI/TestPyPIのTrusted Publishingは無料で利用可能です。 GitHub Actionsの無料枠内で動作します(Publicリポジトリなら無制限)。

A: 技術的には可能ですが、推奨しません。

TestPyPIは本番公開前の最終確認の場です。 PyPIは一度公開したバージョンを削除できないため、TestPyPIでの確認を強く推奨します。

A: はい、簡単に移行できます。

PyPI側でTrusted Publisher設定 → workflowからPYPI_API_TOKEN削除 → permissions: id-token: write追加。 シークレット削除でセキュリティ向上します。

A: Trusted Publishing利用時は必須です。

PyPI側のTrusted Publisher設定でEnvironment nameを指定するため、GitHub側でも同名の環境が必要です。 Settings → Environments → New environment で作成してください。

A: GitHub Actionsを手動キャンセルできます。

ActionsタブでWorkflowを開き、Cancelボタンで実行中止可能です。 TestPyPIに公開済みでも問題ありません(テスト環境なので)。 PyPIに公開されてしまった場合、そのバージョンは削除できないため、次のバージョンで修正します。

A: はい、考え方は全く同じです。

「バージョンタグpush → 自動リリース」という仕組みはPyPI/k8s/EC2で共通です。 デプロイ先が違うだけで、ワークフローの構造(build → staging → production)は同じ。 「OSS vs 企業プロジェクト」タブの企業側を参考にしてください。

A: はい、複数のプロバイダーに対応しています。

GitHub Actions以外にも、GitLab CI/CD、Google Cloud、ActiveStateがサポートされています。 ただし、GitLabは現時点でgitlab.comのみ対応で、セルフホスト版は未対応です。

Source: GitHub Docs - Configuring OIDC in PyPI, OpenSSF - Trusted Publishers for All Package Repositories

もっと詳しく知りたい人へ(参考文献)

関連トピック

コメント (0)

まだコメントはありません。最初のコメントを残しませんか?

コメントを投稿

メールアドレスが公開されることはありません。必須項目には * が付いています