手動デプロイは、慣れるほど確認が雑になります。
テストを通ったものだけが本番に出る仕組みにすると、
修正から反映までが数分になり、小さな改善を出しやすくなります。
ワークフローの構成
name: deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
db:
image: postgres:14
env: { POSTGRES_PASSWORD: test }
options: >-
--health-cmd pg_isready --health-interval 5s --health-retries 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.11", cache: pip }
- run: pip install -r requirements.txt
- run: ruff check .
- run: python manage.py makemigrations --check --dry-run # 反映漏れ検知
- run: python manage.py test
deploy:
needs: test # ← テストが通らなければ実行されない
runs-on: ubuntu-latest
steps:
- uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
key: ${{ secrets.SSH_KEY }}
script: /srv/app/deploy.sh
needs: test が要点です。これでテスト失敗時にデプロイが止まります。
makemigrations --check を必ず入れる
モデルを変更したのにマイグレーションを作り忘れる、
という事故は必ず起きます。CI で機械的に検出します。
python manage.py makemigrations --check --dry-run
# 未作成の変更があれば非ゼロで終了する
サーバー側のデプロイスクリプト
#!/usr/bin/env bash
set -euo pipefail # エラーで即停止(これが無いと途中失敗を見逃す)
cd /srv/app
git pull --ff-only
# マイグレーション前にダンプ(切り戻せるように)
pg_dump -Fc mydb > /var/backups/pre_deploy_$(date +%Y%m%d_%H%M%S).dump
venv/bin/pip install -q -r requirements.txt
venv/bin/python manage.py migrate --noinput
venv/bin/python manage.py collectstatic --noinput
systemctl reload myapp # graceful(処理中のリクエストを落とさない)
set -euo pipefail が無いと、途中でコケても最後まで走り
「成功したように見えて壊れている」状態になります。
秘密情報の扱い
SSH 鍵やトークンは GitHub の Secrets に置き、リポジトリには入れません。
デプロイ用の鍵は専用に作り、実行できる操作を最小限にします。
command="/srv/app/deploy.sh",no-port-forwarding ssh-ed25519 AAAA...
authorized_keys に command= を付けると、その鍵では
デプロイスクリプト以外を実行できなくなります。
自動デプロイをやってよい条件
- テストがある(無いなら、自動化は事故を速くするだけ)
- 切り戻せる(ダンプ、あるいは1つ前のコミットに戻せる)
- 落ちたことに気づける(デプロイ後のヘルスチェック)
この3つが揃っていないうちは、手動のほうが安全です。
まとめ
needs:でテスト通過を必須にするmakemigrations --checkでマイグレーション漏れを検出- デプロイスクリプトは
set -euo pipefailから始める - マイグレーション前に必ずダンプ
- SSH 鍵は
command=で操作を限定する