GitHub Actions¶
GitHub Actions を使えば、例えば main ブランチに push したら、まず pytest を実行して、PASS
したら 自分のローカルネットワークにあるサーバーでデプロイする、っていう CI(Continuous Integrity)/CD(Continuous Deployment) パイプラインを構築できる。
必要なのは、ワークフローファイルを .github/workflows/my-actions.yml (ファイル名はなんでもいい)のように作成しておいて、 GitHub にソースコードと一緒にあげること。
ワークフローファイルのサンプル¶
# .github/workflows/my-actions.yml
name: CI/CD pipeline
on:
push:
branches: [ "main" ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v8.0.0
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Install dependencies
run: uv sync --frozen
- name: Run tests
run: uv run pytest
deploy:
needs: test
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install dependencies
run: uv sync --frozen
- name: Restart service
run: sudo systemctl restart fastapi-app
テストが成功したときだけデプロイする
deploy に needs: test をつけてるので、 test ジョブが成功したときだけ deploy ジョブが走る。
pytest が失敗したらデプロイされない: cf. Defining prerequisite jobs (needs)
test ジョブ(ubuntu-latest)¶
runs-on: ubuntu-latest— GitHub がホストするサーバーで pytest を実行する。actions/checkout@v6— リポジトリのコードを GitHub サーバーにコピーする。astral-sh/setup-uv@v8.0.0— uv をインストールする。enable-cacheとcache-dependency-glob: "uv.lock"で、uv.lockが変わらない限り依存をキャッシュして高速化する。uv sync --frozen—uv.lockの内容そのままで依存をインストールする。--frozenをつけるとuv.lockを更新せず、ロックとpyproject.tomlがズレてたらエラーにする。 CI ではロックを勝手に書き換えてほしくないので--frozenにしておく方がいいか。uv run pytest— テストの実行: cf. pytest
deploy ジョブ(self-hosted)¶
デプロイ先のサーバーに self-hosted runner をセットアップしておくと、そのサーバー上でジョブが走る。ここではコードを取得して依存を同期し、 systemd で FastAPI のサービスを再起動する。
runs-on: self-hosted— 自前のサーバー(self-hosted runner)で実行する。 runner がチェックアウトした場所で、そのまま依存の同期とサービス再起動をやる。uv sync --frozen— デプロイ先でも同じロックファイルから依存をそろえる。sudo systemctl restart fastapi-app— FastAPI を動かしてる systemd サービスを再起動して、新しいコードを反映する。
Note
このサンプルは、デプロイ先サーバーに self-hosted runner と FastAPI の systemd サービス、 sudoers が用意されてる前提。セットアップ手順は self-hosted runner のセットアップ を参照。
self-hosted runner のセットアップ¶
cf. Adding self-hosted runners
デプロイ先のサーバー(ここでは RockyLinux、ユーザーは alice)に runner をインストールする。 GitHub リポジトリの Settings → Actions → Runners → New self-hosted runner → Linux を選ぶと、ダウンロードと config.sh での登録( token 付き)のコマンドが表示されるので、それをサーバーで順に実行する。
-
ダウンロードして展開する。 URL / バージョン / token は GitHub の画面に出てくるものを使う。
-
config.shで runner を登録する。いくつか聞かれるけど、基本そのまま Enter でいい。 -
./run.shで起動すると、 GitHub を監視する状態(Listening for Jobs)になる。$ ./run.sh √ Connected to GitHub Current runner version: '2.333.1' 2026-04-20 02:02:44Z: Listening for Jobsこの状態で
mainに push すると、ジョブが自動で走る。 -
./run.shはターミナルを閉じると止まるので、常時Listening for Jobsを維持するにはサービスとして登録する。Note
svc.sh installは、 runner のファイルを持ってるユーザー(ここではalice)で動くサービスとして登録される。なのでジョブもaliceで走り、チェックアウト先は/home/alice/actions-runner/_work/my-fastapi-app/my-fastapi-appのようになる。<runnerインストール先>/_work/は、config.shの work フォルダ設定(デフォルト_work)で決まる。- リポジトリ名が 2 回繰り返される部分は、
GITHUB_WORKSPACEを参照
runner のセットアップができたら、 FastAPI 本体を systemd サービスとして登録する。 runner と同じユーザー( alice )で動かすシステムサービス設定例。
-
systemd の設定
WorkingDirectoryを runner のチェックアウト先(<runnerインストール先>/_work/<リポジトリ名>/<リポジトリ名>)にしておけば、デプロイステップのuv syncと再起動がそのまま反映される(コピー処理は不要)。# /etc/systemd/system/fastapi-app.service [Unit] Description=FastAPI App After=network.target [Service] User=alice Group=alice WorkingDirectory=/home/alice/actions-runner/_work/my-fastapi-app/my-fastapi-app ExecStart=/home/alice/.local/bin/uv run gunicorn -c gunicorn_config.py app.main:app Restart=always RestartSec=5 [Install] WantedBy=multi-user.target -
sudoers の設定
aliceユーザーがパスワードなしでsudo systemctl restart fastapi-appを実行できるようにしておく。 -
gunicorn の設定
gunicorn_config.py を作成しておく。
Secrets¶
デプロイにトークンなどの秘密情報が必要なら、リポジトリの Settings → Secrets and variables → Actions に登録して、ワークフローから ${{ secrets.NAME }} で参照する: cf. Using secrets in GitHub Actions