クイックスタート:5分で作るはじめてのGitHub Actionsワークフロー
コードをプッシュした後、サーバーにSSHで手動ログインしてデプロイしたことがある人なら、あの苦労はよくわかるはずだ。GitHub Actionsがその問題を解決してくれたのは約2年前のこと。それ以来、手動デプロイのミスで深夜に予期せぬロールバックをすることはなくなった。
基本的な仕組みはシンプルだ。YAMLファイルを書いてリポジトリにコミットするだけで、プッシュ、プルリクエスト、タグなど何かが起きるたびにGitHubがスクリプトを自動実行してくれる。外部CIサーバーも、Jenkinsのセットアップも不要。追加費用もかからない(必要な場合を除いて)。
始めるのに必要なコマンドはたった2つ:
# ワークフローディレクトリを作成する
mkdir -p .github/workflows
touch .github/workflows/ci.yml
この最小限のパイプラインを貼り付けよう:
name: CIパイプライン
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: コードをチェックアウト
uses: actions/checkout@v4
- name: Pythonのセットアップ
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: 依存関係のインストール
run: pip install -r requirements.txt
- name: テストの実行
run: pytest tests/
これをコミットしてGitHubにプッシュし、リポジトリのActionsタブを確認しよう。ワークフローが実行されているのが見えるはずだ。これがあなたの最初のCIパイプラインだ。
詳細解説:ワークフロー構造を理解する
YAML構造は見た目よりも直感的だ。4つの概念を理解すれば、日常的に出会う場面の90%に対応できる。
トリガー(on:)
on:ブロックはワークフローを起動するイベントを定義する。よく使うトリガーはこちら:
on:
push:
branches: [main]
tags:
- 'v*' # v1.0.0のようなバージョンタグで起動
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * *' # 毎日UTC午前2時に実行
workflow_dispatch: # GitHub UIから手動でトリガーできる
workflow_dispatchトリガーは過小評価されている。GitHub UI上のボタンをクリックするだけで手動デプロイを実行できる。完全自動化が常に望ましいとは限らないステージング環境では特に重宝する。
ジョブとステップ
ジョブはデフォルトで並列実行される。ジョブ内のステップは順次実行される。ジョブを順番に実行したい場合はneeds:を使う:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pytest tests/
build:
runs-on: ubuntu-latest
needs: test # 'test'が成功した場合のみ実行
steps:
- uses: actions/checkout@v4
- run: docker build -t myapp:latest .
deploy:
runs-on: ubuntu-latest
needs: build # 'build'の成功を待ってから実行
steps:
- run: echo "デプロイ中..."
シークレット管理
ワークフローファイルに認証情報をハードコードしてはいけない。リポジトリのSettings → Secrets and variables → Actionsからシークレットを追加し、以下のように参照する:
- name: サーバーにデプロイ
env:
SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SERVER_HOST: ${{ secrets.SERVER_HOST }}
run: |
echo "$SSH_KEY" > /tmp/deploy_key
chmod 600 /tmp/deploy_key
ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no \
ubuntu@$SERVER_HOST "cd /app && git pull && systemctl restart myapp"
応用編:本番環境で実際に機能するパターン
複数環境向けマトリックスビルド
Python 3.9、3.10、3.11でコードが動作するか確認したいとき、3つの別々のジョブを書く必要はない。マトリックス戦略がそれを処理してくれる:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11']
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pytest tests/
これで6つの並列ジョブが作成される。すべてのOSですべてのPythonバージョンをテストする。何か壊れたとき、GitHubはどの組み合わせが失敗したかを正確に教えてくれる。3.9が通るのにWindows上の3.11がクラッシュしている?1回の実行でわかる。
依存関係のキャッシュ
毎回クリーンインストールするとパイプラインが遅くなる。実行間でpipパッケージをキャッシュしよう:
- name: pipパッケージのキャッシュ
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: 依存関係のインストール
run: pip install -r requirements.txt
キャッシュキーはrequirements.txtをハッシュ化しているので、依存関係が変わると自動的に無効化される。中規模のPythonプロジェクトでは、インストール時間が約60秒から約5秒に短縮される。
DockerビルドとレジストリへのPush
Docker Hub向けのビルド&プッシュワークフローの完全版はこちら:
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Docker Hubにログイン
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: ビルドしてプッシュ
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
myuser/myapp:latest
myuser/myapp:${{ github.sha }}
すべてのビルドにgitコミットSHAがタグ付けされる。ロールバックは簡単になる。本番環境でどのコミットが動いているか常に正確にわかるからだ。
環境ベースのデプロイ
GitHub Environmentsを使うと、環境ごとに保護ルールと別々のシークレットを設定できる(ステージング対本番など):
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- name: ステージングにデプロイ
run: ./deploy.sh staging
deploy-production:
runs-on: ubuntu-latest
environment: production # 手動承認を要求できる
needs: deploy-staging
if: github.ref == 'refs/heads/main'
steps:
- name: 本番環境にデプロイ
run: ./deploy.sh production
本番環境にレビュアーを必須設定にすれば、誰かがGitHub UI上でApproveをクリックするまで何もリリースされない。手動ゲートは1つ。それ以外はすべて自動化されたまま。
実際のパイプラインから学ぶ実践的なヒント
ワークフローを集中させる
よくあるミスはすべてを1つのワークフローファイルに詰め込むことだ。関心事を分離しよう。毎回のプッシュでテストを実行するci.ymlと、タグまたはmainへのマージ時のみ起動するdeploy.ymlに分ける。ファイルが小さいほど、夜11時に何か壊れたときのデバッグが楽になる。
if:条件を使ってステップをスマートにスキップする
- name: mainブランチのみデプロイ
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: ./deploy.sh
テストはすべてのブランチで実行される。デプロイはmainでのみ起動する。フィーチャーブランチが誤って本番環境に触れることはない。
早期失敗、ただしやりすぎない
strategy:
fail-fast: false # 1つ失敗しても全マトリックスジョブを完了させる
matrix:
python-version: ['3.9', '3.10', '3.11']
デフォルトでは1つのジョブが失敗した瞬間、すべてのマトリックスジョブが停止する。fail-fast: falseを設定すると全体像が見える。3.9は通るのに3.11が壊れているかもしれない。データが多いほど、修正も速くなる。
READMEにステータスバッジを追加する
GitHubはワークフローのステータスバッジを自動生成する。Actionsタブ → ワークフロー → 三点メニュー → Create status badgeから確認できる。READMEに貼り付けよう:

ランナー使用時間に注意する
フリープランではプライベートリポジトリに月2,000分が付与される。パブリックリポジトリは無制限。大規模なマトリックスビルドでクォータを消費している場合、2つの方法が効果的だ。キャッシュの改善と、ドキュメントの変更でフルテストが起動しないためのpaths:フィルターだ:
on:
push:
paths:
- 'src/**'
- 'tests/**'
- 'requirements.txt'
# docs/やREADME.mdの変更はパイプラインを起動しない
GitHub Actionsは、かつて15ステップの手動デプロイチェックリストだったものをプッシュ1回に凝縮してくれた。学習曲線は見た目ほど急ではない。YAML構造が頭に入れば、あとは自然についてくる。パイプラインがリポジトリ内に存在するということは、コードと一緒にバージョン管理されるということでもある。まずは上のクイックスタートから始め、必要に応じて応用パターンを組み込んでいこう。

