GitHub Actionsを「名前は知ってるけどちゃんと使ったことない」状態で放置してたんですが、ある日 main にpushしたらテストが全部落ちてることに気づかず数時間溶かしまして。さすがに「自動でテスト走らせる仕組みが必要だな」と重い腰を上げました。
この記事は全4回シリーズ「GitHub Actions × Python 自動化入門」の第1回です。シリーズ全体の構成はざっくりこんな感じです。
- 第1回(本記事):GitHub Actionsの基本概念と、Pythonプロジェクト向けのはじめてのワークフロー作成
- 第2回:pytest連携・複数バージョンのマトリクステスト
- 第3回:Linter(flake8 / black)とコード品質チェックの自動化
- 第4回:S3やLambdaへの自動デプロイ(AWS連携)
第1回はCI/CDの概念とワークフローの書き方を丁寧に整理します。「設定ファイルが何を意味してるのかよくわからない」という状態から抜け出すのが目標です。
- GitHub Actions・CI/CDの基本的な概念がわかる
- Pythonプロジェクト向けの最小構成ワークフローが書ける
- トリガーの種類と使い分けがわかる
- SecretsをYAMLから安全に参照する方法がわかる
- ワークフローの実行結果の確認方法がわかる
GitHub Actionsって何をするものか
一言でいうと、「GitHubリポジトリ上のイベントをトリガーにして、任意の処理を自動実行できる仕組み」です。
トリガーになるイベントの例:
mainブランチへのpush- プルリクエストの作成・更新
- スケジュール実行(cron形式)
- Issueのオープン、など
処理の内容はYAMLファイルに書くだけで、GitHub側のサーバー(Hosted Runner)で実行してくれます。自前のサーバーは原則不要。
よく使われる用途は「テストを自動実行するCI」と「本番環境へのデプロイを自動化するCD」の2つ。CI/CDという言葉はここから来ています。Continuous Integration(継続的インテグレーション)とContinuous Delivery(継続的デリバリー)の略です。まあ、最初は概念より「pushしたら自動でテストが走る」という感覚のほうが掴みやすいと思います。
料金について(個人開発なら気にしなくていいケースが多い)
GitHub Actionsの料金体系、「結局いくらなの?」ってなりがちなので現時点の情報で整理しておきます。
- パブリックリポジトリ:標準のGitHubホストランナー(いわゆる “standard runners”)を使う限り、Actionsの実行は無料です
- プライベートリポジトリ(GitHub Free):月2,000分の無料枠があります(超えると課金、ただし支払い設定がない場合はそこで止まります)
- セルフホストランナー:プライベート/内部リポジトリでは別途料金が発生する場合があります(最新の料金はGitHub公式ドキュメントで確認してください)
個人の学習用・個人開発用だと、プライベートでも月2,000分あればだいたい足ります。地味に大事なのが「支払い方法を設定してない場合、無料枠を超えたらワークフローがブロックされる」点で、うっかり無限に課金される感じではないです(とはいえ、CIが突然止まると困るので、必要なら予算上限も含めて見ておくのが安心)。
余談ですが、料金の話って「後で読もう」で放置しがちなんですが、Actionsが生活導線に入ると地味に効いてくるので、最初に1回だけ把握しておくと精神的にラクです。
ワークフローの構造を理解する
GitHub Actionsの設定はYAMLファイルとして定義し、リポジトリの .github/workflows/ ディレクトリ以下に置きます。ファイル名は何でもOK(例:ci.yml)。
構造はこんな感じです。
name: ワークフローの名前(GitHub上で表示される)
on: # トリガー設定
push:
branches: [main]
jobs: # 実行する処理のまとまり
job名:
runs-on: ubuntu-latest # 実行環境
steps: # 具体的な処理ステップ
- ...
主要な概念を整理するとこうなります。
- Workflow(ワークフロー):YAMLファイル1つ = 1ワークフロー
- Job(ジョブ):ワークフロー内の処理単位。複数のジョブは並列実行が基本
- Step(ステップ):ジョブ内の処理を並べたもの。上から順番に実行される
- Action(アクション):
uses: actions/checkout@v4のような、再利用可能な処理ブロック
uses: で使えるアクションはGitHub Marketplaceで検索できます。公式・サードパーティのものが大量にあって、正直なんでもあります。
Pythonプロジェクト向けのはじめてのワークフローを書く
実際に動くYAMLを見たほうが早いので、シンプルなものから始めます。
プロジェクト構成の前提
myproject/
├── .github/
│ └── workflows/
│ └── ci.yml ← ここに書く
├── src/
│ └── calculator.py
├── tests/
│ └── test_calculator.py
└── requirements.txt
サンプルとして使う calculator.py と test_calculator.py も一応載せておきます。
# src/calculator.py
def add(a, b):
return a + b
def divide(a, b):
if b == 0:
raise ValueError("0では割れません")
return a / b
# tests/test_calculator.py
import pytest
from src.calculator import add, divide
def test_add():
assert add(2, 3) == 5
def test_divide():
assert divide(10, 2) == 5.0
def test_divide_by_zero():
with pytest.raises(ValueError):
divide(1, 0)
# requirements.txt
pytest
ワークフローファイル(ci.yml)
# .github/workflows/ci.yml
name: Python CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
cache: 'pip' # 依存関係をキャッシュしてビルドを速くする
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
run: python -m pytest tests/ -v
各ステップを順番に見ていきます。
actions/checkout@v4:リポジトリのコードをRunner上にチェックアウトします。これがないとコードが存在しない状態で始まるので、ほぼ必須のステップ。
actions/setup-python@v5:GitHubホストランナーにはPythonが事前インストールされているため、追加でインストールしなくても使えます。とはいえ、バージョンを明示的に固定したいケースがほとんどなので、このアクションを使うのがお決まりのパターンです。cache: 'pip' を指定すると依存パッケージをキャッシュしてくれて、2回目以降のビルドが速くなります。これは地味に便利。
python-versionの指定:'3.13' のように具体的に書くか、'3.x' で最新の3系を使うこともできます。本番環境のバージョンに合わせるのが無難です。
このYAMLを .github/workflows/ci.yml として保存してpushすると、GitHubのActionsタブでワークフローが走るのを確認できます。
トリガーの書き方をもう少し詳しく
on: セクションはけっこうバリエーションがあるので、よく使うパターンだけ整理しておきます。
# パターン1: 特定ブランチへのpushとPR
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
# パターン2: 特定のファイルが変わったときだけ実行
on:
push:
paths:
- 'src/**'
- 'tests/**'
- 'requirements.txt'
# パターン3: スケジュール実行(毎朝9時 JST)
on:
schedule:
- cron: '0 0 * * *' # UTCで指定するので注意
# パターン4: 手動実行(GitHub UI上のボタンで起動できる)
on:
workflow_dispatch:
paths: を使うと、関係ないファイルが変更されたときにワークフローが無駄に走るのを防げます。ドキュメントだけ更新したときにテストが走る必要はないので、慣れてきたら設定すると良いかと。
schedule: のcronはUTC基準で、スケジュールトリガー自体にタイムゾーン指定はできません。なのでJSTに直すと+9時間です。ここは正直ハマりポイントで、最初「なんか時間がズレる」と思ったらUTC指定でした。
環境変数とSecretsの使い方
APIキーなどの機密情報は直接YAMLに書いてはいけません。GitHubのSecrets機能を使います。
設定場所:リポジトリの Settings → Secrets and variables → Actions → New repository secret
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run with secrets
env:
API_KEY: ${{ secrets.MY_API_KEY }} # SecretsはこのようにYAMLから参照
run: python script.py
環境変数として渡す方法と、env: をjobレベルで定義する方法があります。スコープの違いがあって、jobレベルに書くとそのjob内の全stepで使えます。個人的にはstepごとに明示的に渡すほうがわかりやすいかなと思っています。
ちなみに、Secretsの値はログに出力されても自動でマスキングされるので安心です(ただ、完全に事故がゼロというわけでもないので、変な echo とかはしないのが無難)。
ワークフローの実行結果を確認する
pushしたあとは、GitHubリポジトリの「Actions」タブを見ます。ワークフローが実行中・成功・失敗のいずれかで表示されます。失敗した場合はどのステップで何のエラーが出たかをログで確認できます。
テストが失敗した場合、プルリクエスト画面にも「×」マークが表示されるので、レビュー前にCIが落ちてることが一目でわかります。これが地味にうれしい。コードを見る前に「あ、テスト落ちてますね」って言えるようになります。
また、pull_request トリガーを設定しておくと、ブランチ保護ルールと組み合わせて「CIが通らないとmergeできない」設定にすることもできます。チームで使う場合はこれが特に効いてくる機能です。
まとめ
第1回では、GitHub Actions・CI/CDの基本的な概念(Workflow / Job / Step / Action)と、Python向けの最小構成ワークフローを整理しました。actions/setup-python@v5 + pytestの組み合わせだけでも、pushするたびに自動でテストが走る環境が作れます。
- GitHub ActionsはYAMLファイル1つで自動化ワークフローを定義できる
- Workflow → Job → Step の階層構造で処理を組み立てる
on:でトリガー条件を細かく制御できる(push / PR / スケジュール / 手動)- APIキーなどはSecretsに登録してYAMLから参照する
- 実行結果はActionsタブとプルリクエスト画面で確認できる
マトリクス機能を使うと複数のPythonバージョンで同時にテストを回せたりして、CIっぽさが一気に出てきます。第2回ではそのあたりを掘り下げていきます。
📚 シリーズ「GitHub Actions × Python 自動化入門」(第1回 / 全4回)
→ 次回の記事: 【第2回】GitHub Actions × Python 自動化入門 — テストの自動化(pytest連携とカバレッジレポートの設定)

