mcpField
P
python3-publish-release-pipeline

python3-publish-release-pipeline

github.com/jamie-bitflightクラウド・DevOps35 回インストール
リポジトリ

python3-publish-release-pipeline について

PythonパッケージのCI/CDリリースパイプラインを設定するスキルです。GitHub ActionsまたはGitLab CIを検出し、テスト、 lint、公開、バージョン管理、リリース文書の構成を案内します。

主な機能

  1. 01GitHub ActionsとGitLab CIの設定
  2. 02テストとlintのワークフロー
  3. 03PyPI公開の設定
  4. 04Gitタグによるバージョン管理
  5. 05リリース手順の文書化
  6. 06Trusted Publishingの案内

活用シーン

  1. 01Pythonパッケージリリースの自動化
  2. 02PyPI公開の設定
  3. 03TestPyPIを使ったリリース検証
  4. 04タグを使うリリース手順の文書化

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jamie-bitflight/claude_skills --skill python3-publish-release-pipeline

インストール後、Claude Code で/python3-publish-release-pipelineを入力して読み込まれたか確認します。

詳細

<pipeline_target>$ARGUMENTS</pipeline_target>

Python リリースパイプラインの設定

モデルは、Python パッケージを自動で公開するための CI/CD パイプラインを設定します。

引数

<pipeline_target/>

引数が指定されていない場合は、リポジトリを確認して(.github/ または .gitlab-ci.yml を探して)検出します。

手順

共通のアーキテクチャ、型付け、テスト、CLI のルールを適用する際は、/python-engineering:standards-for-python-development の標準を読み込み、それに従ってください。

  1. CI プラットフォームを検出する(GitHub Actions または GitLab CI)
  2. テスト、リンティング、公開用のワークフローファイルを作成する
  3. PyPI トークンに関するシークレットのドキュメントを設定する
  4. Git タグを使ったバージョン管理を設定する
  5. リリースプロセスをドキュメント化する

GitHub Actions パイプライン

ディレクトリ構成
.github/
├── workflows/
│   ├── ci.yml           # Run on every push/PR
│   ├── release.yml      # Run on version tags
│   └── docs.yml         # Optional: documentation
└── dependabot.yml       # Optional: dependency updates
CI ワークフロー(ci.yml)
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v4
        with:
          enable-cache: true

      - name: Set up Python
        run: uv python install 3.11

      - name: Install dependencies
        run: uv sync --all-extras

      - name: Run ruff
        run: |
          uv run ruff check --no-fix src/ tests/
          uv run ruff format --check src/ tests/

      - name: Run type check (ty default; swap to mypy if hooks/CI run mypy — not merely [tool.mypy])
        run: uv run ty check src/ tests/

  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.11", "3.12", "3.13"]

    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v4
        with:
          enable-cache: true

      - name: Set up Python ${{ matrix.python-version }}
        run: uv python install ${{ matrix.python-version }}

      - name: Install dependencies
        run: uv sync --all-extras

      - name: Run tests
        run: uv run pytest tests/ -v --cov=src --cov-report=xml

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          files: coverage.xml
          fail_ci_if_error: false
リリースワークフロー(release.yml)
name: Release

on:
  push:
    tags:
      - "v*.*.*"

permissions:
  contents: write
  id-token: write  # Required for trusted publishing

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v4
      - run: uv python install 3.11
      - run: uv sync --all-extras
      - run: uv run ruff check --no-fix src/ tests/
      - run: uv run ruff format --check src/ tests/
      - run: uv run ty check src/ tests/
      - run: uv run pytest tests/ --cov=src --cov-report=term-missing

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v4

      - name: Set up Python
        run: uv python install 3.11

      - name: Build package
        run: uv build

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  # Option 1: Trusted Publishing (Recommended)
  publish-pypi:
    # Publish only an artifact from this release workflow after its tests pass.
    needs: [build, test]
    runs-on: ubuntu-latest
    environment:
      name: pypi
      url: https://pypi.org/p/your-package
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        # No token needed with trusted publishing!

  # Option 2: Token-based Publishing (Alternative)
  # publish-pypi:
  #   needs: [build, test]
  #   runs-on: ubuntu-latest
  #   steps:
  #     - name: Download artifacts
  #       uses: actions/download-artifact@v4
  #       with:
  #         name: dist
  #         path: dist/
  #
  #     - name: Publish to PyPI
  #       uses: pypa/gh-action-pypi-publish@release/v1
  #       with:
  #         password: ${{ secrets.PYPI_API_TOKEN }}

  github-release:
    needs: [build, publish-pypi]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          files: dist/*
          generate_release_notes: true

GitLab CI パイプライン

.gitlab-ci.yml
stages:
  - lint
  - test
  - build
  - publish

variables:
  UV_CACHE_DIR: "$CI_PROJECT_DIR/.uv-cache"
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache"

default:
  image: python:3.11-slim
  before_script:
    - pip install uv
    - uv sync --all-extras
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .uv-cache/
      - .pip-cache/
      - .venv/

lint:
  stage: lint
  script:
    - uv run ruff check --no-fix src/ tests/
    - uv run ruff format --check src/ tests/
    - uv run ty check src/ tests/

test:
  stage: test
  parallel:
    matrix:
      - PYTHON_VERSION: ["3.11", "3.12", "3.13"]
  image: python:${PYTHON_VERSION}-slim
  script:
    - uv run pytest tests/ -v --cov=src --cov-report=xml --junitxml=report.xml
  coverage: '/TOTAL.*\s+(\d+%)/'
  artifacts:
    reports:
      junit: report.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml

build:
  stage: build
  script:
    - uv build
  artifacts:
    paths:
      - dist/
    expire_in: 1 week

# Publish to PyPI on tags
publish:pypi:
  stage: publish
  rules:
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
  script:
    - uv publish --token $PYPI_TOKEN
  environment:
    name: pypi
    url: https://pypi.org/project/your-package

# Publish to GitLab Package Registry on tags
publish:gitlab:
  stage: publish
  rules:
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
  script:
    - |
      TWINE_PASSWORD=${CI_JOB_TOKEN} \
      TWINE_USERNAME=gitlab-ci-token \
      uv publish --publish-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi
  environment:
    name: gitlab-registry

Trusted Publishing の設定(PyPI)

Trusted Publishing を使う理由
  • 管理が必要な API トークンがありません
  • ローテーションが必要なシークレットがありません
  • 漏えいすることがありません
  • 特定のワークフローにスコープを限定できます
設定手順
  1. PyPI に移動します → プロジェクト → Publishing

  2. GitHub Publisher を追加します:

    • Owner:your-username
    • Repository:your-repo
    • Workflow name:release.yml
    • Environment:pypi(任意ですが、推奨)
  3. GitHub Environment を作成します:

    • Settings → Environments → New environment:pypi
    • 保護ルールを追加します(任意):
      • 必須のレビュアー
      • タグのみを対象に制限
代替手段:API トークン

Trusted Publishing を利用できない場合:

  1. PyPI トークンを作成します:

    • PyPI → Account Settings → API tokens
    • プロジェクトにスコープを限定したトークンを作成します
  2. リポジトリのシークレットに追加します:

    • GitHub:Settings → Secrets → PYPI_API_TOKEN
    • GitLab:Settings → CI/CD → Variables → PYPI_TOKEN

バージョン管理

手動でバージョンを設定(pyproject.toml)

タグ付けの前に、pyproject.toml のバージョンを更新します:

[project]
version = "1.2.3"
Git タグから動的にバージョンを設定

hatch-vcs を使用する場合:

[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"

[project]
dynamic = ["version"]

[tool.hatch.version]
source = "vcs"

[tool.hatch.build.hooks.vcs]
version-file = "src/my_package/_version.py"
リリースワークフロー
# 1. Update CHANGELOG.md

# 2. Commit changes
git add CHANGELOG.md pyproject.toml
git commit -m "Prepare release v1.2.3"

# 3. Create annotated tag
git tag -a v1.2.3 -m "Release v1.2.3"

# 4. Push with tags
git push origin main
git push origin v1.2.3

テスト用の TestPyPI

GitHub Actions の TestPyPI ジョブ
publish-testpypi:
  needs: build
  runs-on: ubuntu-latest
  environment:
    name: testpypi
    url: https://test.pypi.org/p/your-package
  steps:
    - uses: actions/download-artifact@v4
      with:
        name: dist
        path: dist/

    - uses: pypa/gh-action-pypi-publish@release/v1
      with:
        repository-url: https://test.pypi.org/legacy/
TestPyPI への手動アップロード
# Build
uv build

# Upload to TestPyPI
uv publish --publish-url https://test.pypi.org/legacy/ --token $TESTPYPI_TOKEN

# Test install
uv pip install --index-url https://test.pypi.org/simple/ your-package

必要な設定ファイル

公開に必要な最小限のファイル
your-package/
├── pyproject.toml          # Package metadata and build config
├── README.md               # Required by PyPI
├── LICENSE                 # Required for distribution
├── src/
│   └── your_package/
│       ├── __init__.py
│       └── py.typed        # PEP 561 marker
└── .github/
    └── workflows/
        ├── ci.yml
        └── release.yml
pyproject.toml チェックリスト
- [ ] [build-system] with requires and build-backend
- [ ] [project] with name, version, description
- [ ] readme = "README.md"
- [ ] license specified
- [ ] requires-python = ">=3.11"
- [ ] authors with name and email
- [ ] classifiers (Development Status, License, Python versions)
- [ ] dependencies list
- [ ] [project.urls] with Documentation, Issues, Source
- [ ] [project.scripts] if CLI tool

セキュリティのベストプラクティス

  1. API トークンよりも Trusted Publishing を使用します
  2. トークンのスコープを アカウント全体ではなく、特定のプロジェクトに限定します
  3. 保護ルールを設定した 環境を使用します
  4. 完全な SHA またはバージョンタグを使って アクションのバージョンを固定します
  5. ワークフローの変更を 慎重にレビューします
  6. main ブランチの ブランチ保護を有効にします

トラブルシューティング

ビルドが失敗する
# Check package metadata
uv run python -m build --no-isolation
uvx twine check dist/*
アップロードが失敗する
# Never print any portion of a publishing credential.
# Verify authentication only by a non-secret-bearing API/tool response.

# Check package name availability
curl https://pypi.org/pypi/your-package/json
バージョンがすでに存在する

PyPI では、同じバージョンを再アップロードできません。バージョンを上げ、新しいタグを作成してください。


参考資料

コメント