Git入門:バージョン管理のきほん

GitHub Actions で自動化のさわり

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

このレッスンで分かること

  • GitHub Actions が「push をきっかけに GitHub のサーバーでコマンドを流す」仕組みであること
  • push のたびに lint を回す最小の workflow ファイルの中身
  • 実行結果の見方と、プルリクエストとのつながり

push をきっかけにコマンドを流す仕組み

ここまでで、ブランチを push してプルリクエストを出し、レビューを受ける流れができました。この流れの中には、毎回同じことを繰り返す作業が混ざっています。lint を走らせる、テストを流す、ビルドが通るか確かめる。どれも人がやる必要はありません。

GitHub Actions は、リポジトリで何かが起きたときに、GitHub 側が用意した仮想マシンの上でコマンドを実行してくれる仕組みです。push されたら、プルリクエストが作られたら、毎朝 9 時になったら、といったきっかけを指定できます。

やっていることは単純です。まっさらな Linux マシンを 1 台起動し、リポジトリを clone して、こちらが書いたコマンドを順に打つ。それだけです。手元のターミナルでやっていることを、GitHub のマシンが代わりにやる、と考えてください。

覚える語は 4 つだけです。

意味
workflow自動化 1 セットの定義。YAML ファイル 1 つが 1 workflow
event実行のきっかけ。push やプルリクエストなど
job1 台のマシンで動く作業のまとまり
stepjob の中の 1 手順。コマンド 1 つ、または既製の部品 1 つ

最小の workflow を 1 つ作る

まず完成形を出します。リポジトリ直下に .github/workflows/lint.yml を作り、次の内容を書きます。

YAML

name: Lint on: push: branches: - main - "feature/**" jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" - run: npm ci - run: npm run lint

これで完成です。この 1 ファイルを commit して push すれば、次の push から自動で lint が走ります。

ターミナル

$ mkdir -p .github/workflows $ git add .github/workflows/lint.yml $ git commit -m "push時にlintを走らせるworkflowを追加" $ git push

ディレクトリ名は .github/workflows で固定です。.github/workflow と単数にしたり、github/workflows と先頭のドットを落としたりすると、GitHub はまったく反応しません。動かないときは、まずここを疑ってください。

1 行ずつ読む

上から順に見ていきます。

name

YAML

name: Lint

この workflow の表示名です。Actions タブの一覧にこの名前で並びます。省略するとファイルのパスが表示されます。動きには影響しません。

on

YAML

on: push: branches: - main - "feature/**"

on が実行のきっかけです。ここでは push されたときに動くよう指定しています。

branches でさらに絞り込んでいます。main への push と、feature/ で始まるブランチへの push のときだけ動きます。** は階層をまたいだワイルドカードで、feature/loginfeature/admin/user-list も対象になります。

この絞り込みを消すと全ブランチが対象になります。実験用の一時ブランチでも毎回実行されるので、無駄が気になるなら絞っておくとよいです。

jobs と job 名

YAML

jobs: lint:

jobs の下に job を並べます。lint は job につけた名前で、自分で決めます。job を複数書くと、既定では同時に走ります。今回は 1 つだけです。

runs-on

YAML

runs-on: ubuntu-latest

どのマシンで動かすかの指定です。ubuntu-latest は GitHub が用意している Ubuntu の仮想マシンです。ほかに macos-latestwindows-latest が選べますが、Ubuntu が最も速く、無料枠の消費も少ないので、理由がなければこれを使います。

このマシンは毎回まっさらな状態で起動し、workflow が終わると捨てられます。前回の実行結果は残りません。

steps の 1 つ目、checkout

YAML

steps: - uses: actions/checkout@v4

steps の下に手順を並べます。- で始まる項目 1 つが 1 step です。

uses は、他人が作った既製の部品(アクション)を使うという意味です。actions/checkout は GitHub 公式のアクションで、起動したマシンにリポジトリを clone してきます。

これがないと、マシンの中は空っぽのままです。ほぼすべての workflow の 1 行目がこれになります。@v4 はバージョンの指定です。

steps の 2 つ目、Node.js の用意

YAML

- uses: actions/setup-node@v4 with: node-version: "20"

仮想マシンに Node.js を入れます。with はアクションに渡す設定で、ここではバージョン 20 を指定しています。

"20" を引用符で囲んでいるのには理由があります。YAML では引用符のない 20 は数値として読まれ、たとえば 20.10 と書いたときに末尾のゼロが落ちて 20.1 になってしまいます。バージョン番号は文字列として書くのが安全です。

Python のプロジェクトなら actions/setup-python、Go なら actions/setup-go に置き換えます。

steps の 3 つ目、依存のインストール

YAML

- run: npm ci

run は、そのままシェルでコマンドを実行するという意味です。ここからは手元のターミナルでやることと同じです。

npm install ではなく npm ci を使っています。npm cipackage-lock.json に書かれたバージョンをそのまま入れるコマンドで、node_modules を作り直してから入れ直します。自動実行の環境では、毎回まったく同じ状態を作れるこちらが適しています。

steps の 4 つ目、lint

YAML

- run: npm run lint

本題です。package.jsonscripts に定義した lint を実行します。

このコマンドが終了コード 0 以外で終わると、step が失敗し、job 全体が失敗になります。逆に言うと、Actions が判定しているのは終了コードだけです。手元で失敗するコマンドは Actions でも失敗し、手元で通るものは通ります。特別な仕組みではありません。

結果を見る

push すると、リポジトリの Actions タブに実行が並びます。実行を選ぶと job が出て、job を選ぶと step ごとのログが展開できます。

失敗したときは、赤い印の付いた step を開いてください。中身は普通のコマンド出力です。ESLint が「この行にセミコロンがありません」と言っているだけ、ということがほとんどです。

コミットの一覧やプルリクエストの画面にも、結果が印として出ます。

意味
黄色い丸実行中
緑のチェックすべて成功
赤いバツどこかの step が失敗

CLI から確認したいときは gh コマンドが使えます。

ターミナル

$ gh run list $ gh pr checks

プルリクエストとつなげる

on の指定を次のように変えると、プルリクエストが作られたときと更新されたときにも走ります。

YAML

on: push: branches: - main pull_request:

こうすると、レビュアーはプルリクエストの画面を見るだけで lint の結果が分かります。書式の指摘を人がやらずに済むので、レビューは中身の議論に集中できます。これは チームでのコンフリクトを減らす で触れた、道具をそろえる話の続きです。

さらにリポジトリの Settings から Branch protection rules を設定すると、この lint が通るまで main へのマージを禁止できます。

main への push と pull_request の両方を指定すると、プルリクエストのマージ時に 2 回動くことがあります。無駄が気になるうちは、まず pull_request だけにしておくのも手です。

よくある失敗

ここで実際にありがちな失敗を 1 つ挙げます。手元では npm run lint が通るのに、Actions だけが赤くなるケースです。

ログを開くと、こう出ています。

プレーンテキスト

npm error code ENOENT npm error syscall open npm error path /home/runner/work/my-app/my-app/package.json

package.json が見つからない、というエラーです。原因は、リポジトリの直下ではなく frontend/ のような下の階層にプロジェクトがあることです。

Actions のマシンは、actions/checkout でリポジトリを clone したあと、リポジトリの直下でコマンドを実行します。手元では cd frontend してから作業しているので気づきません。

直し方は 2 通りあります。step ごとに作業ディレクトリを指定するか、

YAML

- run: npm ci working-directory: frontend

job 全体の既定として指定するかです。

YAML

jobs: lint: runs-on: ubuntu-latest defaults: run: working-directory: frontend

似た失敗として、手元にだけ入っている道具に依存しているケースもあります。グローバルに npm install -g eslint した環境では動くのに、まっさらなマシンでは eslint が見つからず落ちます。この場合は package.jsondevDependencies に入れて、npm ci で入るようにするのが正解です。

どちらも共通しているのは、Actions のマシンは毎回まっさらだという点です。手元で暗黙に使っているものは、全部 workflow に書き出す必要があります。

修正のたびに push して確認するのは時間がかかります。試行錯誤するときは、専用のブランチを切って Draft の PR を出しておくと、他の人に通知を飛ばさずに何度も回せます。

ここから先

このレッスンの範囲はここまでです。深追いはしませんが、次に何があるかだけ挙げておきます。

  • npm run lintnpm test に変えればテストの自動実行になる
  • job を 2 つ書けば lint とテストを並行して回せる
  • onschedule を書けば定期実行になる
  • secrets を使えば API キーを安全に渡せる
  • デプロイまで自動化する

やっていることは全部同じです。きっかけを決めて、マシンを用意して、コマンドを並べる。この 3 つだけです。

この章のポイント
  • GitHub Actions は、push などをきっかけに GitHub のマシンでコマンドを流す仕組み
  • workflow は .github/workflows/ に置く YAML ファイル 1 つ。on できっかけ、jobs で作業を書く
  • step は uses で既製の部品を使うか、run でコマンドを直接打つかの 2 通り
  • 判定しているのは終了コードだけ。手元で通るコマンドは Actions でも通る