イントロダクション

目的: 読者が最小限かつ機能的な CI/CD パイプラインを構築できるように導き、Python アプリケーションに対して PyPI への自動デプロイを実現する。

開発プロセスの自動化は、現代のプロジェクトにおいて必須となった。よく設計されたCI/CDパイプラインは、開発サイクルの早い段階で回帰を検出するだけでなく、デプロイメントプロセスを完全に自動化することも可能にする。

この記事では、GitHub Actionsを使ってPythonアプリケーション用の完全なパイプラインをCI(継続的インテグレーション)からCD(継続的デプロイメント)まで設定する方法を探ります。

パイプラインのアーキテクチャ

私たちのパイプラインは2つの異なるワークフローで構成されています:

  1. CIパイプライン: 各プッシュおよびプルリクエストごとに実行されます

  2. 継続的デリバリーパイプライン: GitHubのリリース時にのみトリガーされます

@startuml
!theme plain

package "GitHub リポジトリ" {
  [Source Code] as source
  [GitHub Actions] as actions
}

package "CI パイプライン" {
  [Checkout] as checkout_ci
  [Setup Python] as python_ci
  [Install Dependencies] as deps_ci
  [Linting (Ruff)] as lint
  [Unit Tests] as tests
}

package "CD パイプライン" {
  [Checkout] as checkout_cd
  [Setup Python] as python_cd
  [Build Package] as build
  [Publish to PyPI] as pypi
}

source --> actions : Push/PR
actions --> checkout_ci
checkout_ci --> python_ci
python_ci --> deps_ci
deps_ci --> lint
lint --> tests

source --> actions : Release
actions --> checkout_cd : On Release
checkout_cd --> python_cd
python_cd --> build
build --> pypi

@enduml

�継続的インテグレーション (CI) パイプラインの設定

CIワークフローの構造

CI ワークフローは、コードへの各貢献を検証するように設計されています。以下はその完全な設定です:

name: CI/CD Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.x'

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install ruff pytest pytest-mock

    - name: Run Linting (Ruff)
      run: ruff check .

    - name: Run Tests (Pytest)
      run: pytest

CIステップの分析

トリガー (on)

----
----
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
----

パイプラインは次の条件でトリガーされます : - 各プッシュをブランチに`main` - 各 pull request へ`main`

このアプローチは、メインコードが安定したまま保たれ、すべての貢献が統合前に検証されることを保証します。

==== 2. 実行環境

----

runs-on: ubuntu-latest

Ubuntu Latestは、性能、コスト、互換性の良いバランスを提供し、ほとんどのPythonプロジェクトに適しています。

==== 3. コードのチェックアウト

```yaml
----
----
- name: Checkout code
  uses: actions/checkout@v4
----

行動`checkout@v4`リポジトリのソースコードを取得します。バージョン v4 は、パフォーマンスとセキュリティの改善をもたらします。

==== 4. Python の設定

----

- name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x'

の使用`'3.x'`これにより、Python 3 の最新安定バージョンを自動的に使用できるようになり、メンテナンスが簡素化されます。

==== 5. 依存関係のインストール

```yaml
----
----
- name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt
    pip install ruff pytest pytest-mock
----

このステップ: - pipを最新バージョンに更新 - プロジェクトの依存関係をインストール - 開発ツール(リンティングとテスト)を追加

==== 6. Ruffによるリンティング

----

- name: Run Linting (Ruff) run: ruff check .

**ラフ**は非常に高速なRust製のPython linterです。Flake8、Black、isortなど複数のツールの機能を1つの高性能なツールに統合しています。

==== 7. テストの実行

```yaml
----
----
- name: Run Tests (Pytest)
  run: pytest
----

Pytest はテストスイート全体を実行し、変更が回帰を引き起こさないことを保証します。

== デプロイメントパイプライン (CD) の設定

=== CD ワークフローの構造

CD ワークフローは GitHub のリリース時にのみトリガーされ、PyPI への公開を自動化します :

[source,yaml]
----
name: Publish to PyPI

on:
  release:
    types:
      - published

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.x'

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install setuptools wheel twine

    - name: Build and publish
      env:
        TWINE_USERNAME: __token__
        TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
      run: |
        python setup.py sdist bdist_wheel
        twine upload dist/*
----

=== CDステップの分析

==== リリース トリガー

----

on: release: types: - published

CDパイプラインはGitHubのリリースが公開されたときのみトリガーされます。このアプローチにより、デプロイメントの正確な制御が確保されます。

==== 2. ビルドツールのインストール

```yaml
----
----
pip install setuptools wheel twine
----

- **セットアップツール**: Python パッケージング ツール - **ホイール**: モダンなPythonディストリビューションフォーマット - **ひも**: セキュアなPyPIアップロードツール

==== 3. 認証設定

----

env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}

認証は、GitHubシークレットとして保存されたPyPI APIトークンを使用し、従来の認証情報よりも安全です。

==== 4. ビルドと公開

```yaml
----
----
run: |
  python setup.py sdist bdist_wheel
  twine upload dist/*
----

- `sdist`ソースディストリビューションを作成 - `bdist_wheel`: ホイールを作成 (バイナリディストリビューション) - `twine upload`: PyPIにディストリビューションを公開

== Pythonパッケージの設定

=== setup.pyの構造

パイプラインが機能するために、プロジェクトにはファイルを含めなければなりません`setup.py`:

[source,python]
----
from setuptools import setup, find_packages

with open("README.adoc", "r", encoding="utf-8") as fh:
    long_description = fh.read()

setup(
    name="playlist-downloader",
    version="1.0.0",
    author="Votre Nom",
    author_email="[email protected]",
    description="CLI tool for managing YouTube playlists",
    long_description=long_description,
    long_description_content_type="text/plain",
    url="https://github.com/cheroliv/playlist-downloader",
    packages=find_packages(),
    classifiers=[
        "Development Status :: 4 - Beta",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: 3.10",
        "Programming Language :: Python :: 3.11",
    ],
    python_requires=">=3.8",
    install_requires=[
        "typer>=0.9.0",
        "yt-dlp>=2023.1.6",
        "google-api-python-client>=2.70.0",
        "google-auth-oauthlib>=0.7.1",
        "pymonad>=2.4.0",
        "pyyaml>=6.0",
    ],
    entry_points={
        "console_scripts": [
            "playlist-downloader=cli:app",
        ],
    },
)
----

=== セットアップ.pyの重要なポイント

1. **メタデータ**: 名前, バージョン, 作者, 説明
2. **依存関係**必要なパッケージのリスト
3. **エントリーポイント**公開されたCLIコマンド
4. **分類器**PyPIのメタデータ

== GitHub Secretsを使用したセキュリティ

=== PyPIトークンの設定

1. **PyPIでAPIトークンを作成する**:

- PyPIにログインしてください - Account Settings > API tokensに移動してください - 適切な権限で新しいトークンを作成してください

1. **GitHubにシークレットを追加**:

- リポジトリの設定 > シークレットと変数 > アクション - 新しいシークレットを作成`PYPI_API_TOKEN` - PyPIのトークンを貼り付けて

[plantuml, secrets-flow, svg]
----
@startuml
!theme plain

actor Developer as dev
participant "GitHubリポジトリ" as repo
participant "GitHub アクション" as actions
participant PyPI

dev -> repo : Configure PYPI_API_TOKEN secret
repo -> actions : Trigger CD pipeline on release
actions -> actions : Access secret securely
actions -> PyPI : Authenticate with token
PyPI -> PyPI : Validate and publish package

note over actions, PyPI
  Token never exposed in logs
  Automatic rotation possible
end note

@enduml
----

== 完全なデプロイメントワークフロー

=== 展開シーケンス

[plantuml, deployment-sequence, svg]
----
@startuml
!theme plain

actor Developer as dev
participant "ローカルGit" as git
participant "GitHub" as github
participant "GitHub Actions" as actions
participant PyPI
participant "エンドユーザー" as users

dev -> git : git tag v1.0.0
dev -> git : git push origin v1.0.0
git -> github : Push tag
dev -> github : Create release from tag
github -> actions : Trigger CD pipeline
actions -> actions : Checkout code
actions -> actions : Setup Python environment
actions -> actions : Install build tools
actions -> actions : Build distributions (sdist + wheel)
actions -> PyPI : Upload to PyPI with token
PyPI -> PyPI : Validate and publish
users -> PyPI : pip install playlist-downloader

note over dev, github
  Release creation can be automated
  or done manually through GitHub UI
end note

@enduml
----

=== パイプラインの状態

[plantuml, pipeline-states, svg]
----
@startuml
!theme plain

[*] --> Idle

Idle --> CI_Running : Push/PR created
CI_Running --> CI_Success : All checks pass
CI_Running --> CI_Failed : Linting/Tests fail
CI_Success --> Idle : Merge completed
CI_Failed --> Idle : Fix and retry

Idle --> CD_Running : Release published
CD_Running --> CD_Success : Package published
CD_Running --> CD_Failed : Build/Upload error
CD_Success --> Idle : Package available on PyPI
CD_Failed --> Idle : Fix and retry release

note on link #red : Blocks merge
note on link #green : Allows deployment

@enduml
----

== ベストプラクティスと最適化

=== バージョン管理

セマンティックなGitタグを使用してください:

----

git tag -a v1.2.3 -m "Release version 1.2.3" git push origin v1.2.3

=== 2. マトリックステスト

複数のPythonバージョンでテストするには:

```yaml
----
----
strategy:
  matrix:
    python-version: [3.8, 3.9, "3.10", "3.11"]
----

=== 3. 依存関係のキャッシュ

キャッシュでビルドを高速化:

----

- name: Cache pip dependencies uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}

=== 4. デプロイメント環境

GitHub環境を使用して、制御されたデプロイを行ってください :

```yaml
----
----
jobs:
  deploy:
    environment: production
    runs-on: ubuntu-latest
----

== 使用例とアーキテクチャ

=== ユースケース図

[plantuml, use-cases, svg]
----
@startuml
!theme plain

left to right direction

actor "開発者" as dev
actor "GitHub Actions" as ga
actor "エンドユーザー" as user

package "CI/CDシステム" {
  usecase "リンティングを実行" as lint
  usecase "テストを実行" as test
  usecase "パッケージのビルド" as build
  usecase "PyPIに公開する" as publish
  usecase "リリースを作成" as release
}

dev --> lint : Pushes code
dev --> test : Pushes code
dev --> release : Creates release
ga --> build : On release trigger
ga --> publish : After successful build
user --> publish : Downloads package

lint .> test : triggers
test .> build : on success
build .> publish : on success

@enduml
----

=== デプロイメントのアーキテクチャ

[plantuml, deployment-architecture, svg]
----
@startuml
!theme plain

cloud "GitHub" {
  [Source Repository]
  [GitHub Actions]
  [Secrets Store]
}

cloud "パイピーアイ" {
  [Package Registry]
  [Distribution Files]
}

node "CI/CD パイプライン" {
  [Linting]
  [Testing]
  [Building]
  [Publishing]
}

[Source Repository] --> [GitHub Actions] : Triggers
[GitHub Actions] --> [Linting]
[Linting] --> [Testing]
[Testing] --> [Building]
[Building] --> [Publishing]
[Publishing] --> [Package Registry] : Uploads
[Secrets Store] --> [Publishing] : Provides token

note as N1
  Secure token-based
  authentication
end note

[Secrets Store] .. N1

@enduml
----

== 監視とデバッグ

=== ログとモニタリング

GitHub Actionsは各ステップの詳細なログを提供します。デバッグするには:

1. **ログを確認してください**各ステップの
2. **デバッグを有効に**に`ACTIONS_STEP_DEBUG`
3. **アーティファクトを使ってください**ビルドファイルを保存するために

----

- name: Upload build artifacts uses: actions/upload-artifact@v3 if: failure() with: name: build-logs path: build/

=== 通知

Slack or email notifications.

We could also do: "Slackまたはメールの通知を追加してください:" (colon). Provide only that.

</think>

Slackまたはメールの通知を追加してください:

```yaml
----
----
- name: Notify on failure
  if: failure()
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    webhook_url: ${{ secrets.SLACK_WEBHOOK }}
----

== 結論

GitHub Actions を使って堅牢な CI/CD パイプラインを構築すると、開発体験が劇的に変わります。linting、テスト、デプロイを自動化することで、あなたは:

- **エラーを減らしてください**本番中 - **サイクルを加速して**開発の - **信頼を向上させてください**あなたのリリースの中で - **協力を促進してください**チームで

このパイプラインは、lint ツール、テスト フレームワーク、またはデプロイ先を調整することで、さまざまな種類の Python プロジェクトに適合させることができます。

これらのワークフローの設定における初期投資は、時間の節約とデプロイメント時の手動エラーの削減によってすぐに元が取れます。

== 補足リソース

- https://docs.github.com/en/actions[ギットハブ アクションズ ドキュメント] - https://packaging.python.org/[Pythonパッケージングガイド] - https://docs.pytest.org/[Pytestドキュメント] - https://docs.astral.sh/ruff/[Ruff ドキュメンテーション] - https://twine.readthedocs.io/[トワイン ドキュメンテーション]

�✅ パイプライン機能が達成されました! これで、GitHub Actions から直接 PyPI に Python パッケージを公開し、テストを自動化できるシンプルな CI/CD パイプラインができました。

しかし、このパイプラインは意図的に最小限に抑えられています。まだ、プロフェッショナルな文脈で不可欠な一部の側面をカバーしていません。

Pythonのマルチバージョンテスト,

自動セキュリティ分析,

Test PyPIを介した段階的な導入,

パイプラインの監視とメトリクス,

バージョニングの自動化および現代のベストプラクティスの統合(pyproject.toml)。

次のセクションでは、レベルを上げます。基本的なパイプラインを本格的な産業レベルのデプロイメントチェーン、つまり堅牢で安全なものに変換する方法を学びます。これは本番環境のPythonプロジェクトに対応できる状態になります。
----

関連記事