GitHub Actionsをちゃんと理解する【GitHub Actions実践記録 #1】
はじめに
こんにちは。
なかのひとカンパニーの archer です。
これまで個人開発で、GitHub Actionsをかなり使ってきました。
たとえば、
- lint
- build
- test
- Cloudflareへのデプロイ
- 定期処理
- CIの確認
などです。
ただ、最初のころは、
「とりあえず動くYAMLを作る」
という使い方が多かったです。
GitHubや記事にあるWorkflowを参考にして、
on:
push:
と書いて、
runs-on: ubuntu-latest
と書いて、
あとは必要なコマンドを並べる。
それでも動きます。
ただ、使う機会が増えると、
「JobとStepは何が違うのか」
「ActionってGitHub Actionsそのものではないのか」
「Runnerは何をしているのか」
あたりを一度整理した方がよさそうだと思うようになりました。
今回は、GitHub Actionsの基本構造を改めて整理します。
GitHub Actionsとは
GitHub Actionsは、GitHub上のイベントをきっかけに処理を自動実行できる仕組みです。
たとえば、
push
↓
lint
↓
test
↓
build
や、
mainへMerge
↓
build
↓
本番Deploy
といった処理を自動化できます。
設定はRepositoryの、
.github/workflows/
にYAMLファイルとして置きます。
たとえば、
.github/workflows/ci.yml
です。
まず全体構造
GitHub Actionsを理解するとき、自分は以下のように考えています。
Workflow
└─ Job
└─ Step
└─ Action または Command
さらに、そのJobを実際に動かす場所が、
Runner
です。
一つずつ見ていきます。
Workflow
Workflowは、自動化処理全体です。
たとえば、
CI
というWorkflowを作るなら、
name: CI
とします。
Workflowファイル自体は、
.github/workflows/ci.yml
に置きます。
そして、
いつ実行するか
を on で指定します。
たとえば、
on:
push:
ならpushされたときです。
Pull Requestでも動かすなら、
on:
push:
pull_request:
のように書けます。
つまりWorkflowは、
何を、いつ自動実行するか
をまとめたものです。
Job
Workflowの中にはJobがあります。
たとえば、
jobs:
build:
なら、build というJobです。
Job単位で、
runs-on: ubuntu-latest
のように実行環境を指定します。
たとえば、
lint
test
build
を全部一つのJobで実行することもできます。
逆に、
lint Job
test Job
build Job
と分けることもできます。
この違いは今後の記事で扱いますが、
Jobは大きな処理単位
と考えると分かりやすいです。
Step
Jobの中で実行する一つ一つの処理がStepです。
たとえば、
steps:
- name: Install
run: npm ci
- name: Test
run: npm test
なら、
Install
Test
がそれぞれStepです。
Stepは上から順番に実行されます。
途中で失敗すれば、基本的にはそのJobも失敗します。
Action
少し名前が紛らわしいのがActionです。
GitHub Actionsというサービスの中で、
再利用できる処理の部品
もActionと呼びます。
たとえば、
- uses: actions/checkout@v7
です。
これはRepositoryのコードをRunnerへCheckoutするActionです。
Node.jsをセットアップするなら、
- uses: actions/setup-node@v7
with:
node-version: 24
のように書けます。
自分で全部Shellを書く代わりに、既存のActionを利用できます。
run と uses
Stepにはよく、
run:
と、
uses:
が出てきます。
違いはシンプルです。
run
自分でコマンドを実行します。
- name: Test
run: npm test
uses
既存のActionを利用します。
- uses: actions/checkout@v7
ざっくり、
run
→ コマンドを実行
uses
→ Actionを利用
と考えています。
Runner
Runnerは、Workflowを実際に実行する環境です。
たとえば、
runs-on: ubuntu-latest
ならUbuntu環境でJobが動きます。
他にも、
runs-on: windows-latest
や、
runs-on: macos-latest
があります。
つまり、
GitHub Actions
↓
Runnerを用意
↓
その中でJobを実行
という流れです。
普段使っている、
npm install
npm test
npm run build
などのコマンドも、このRunner上で実行されています。
最小構成を見てみる
ここまでをまとめると、たとえばこんなWorkflowになります。
name: CI
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: 24
- name: Install
run: npm ci
- name: Test
run: npm test
これを分解すると、
Workflow
CI
Trigger
push / pull_request
Job
test
Runner
ubuntu-latest
Step 1
checkout
Step 2
Node.js setup
Step 3
npm ci
Step 4
npm test
となります。
こうして見ると、それほど複雑ではありません。
on はかなり重要
GitHub Actionsでは、
何を実行するか
と同じくらい、
いつ実行するか
が重要です。
たとえば、
on:
push:
だけなら、pushするたびにWorkflowが動きます。
でも、
featureブランチへのpush
mainへのpush
で同じ処理が必要とは限りません。
たとえばmainだけなら、
on:
push:
branches:
- main
とできます。
Pull Requestだけなら、
on:
pull_request:
です。
手動実行もできます。
on:
workflow_dispatch:
定期実行もできます。
on:
schedule:
- cron: "0 0 * * *"
GitHub Actionsを使っていると、
「何を実行するか」
ばかり考えがちですが、
不要なタイミングで動かさない
ことも大事だと感じています。
最初から複雑にしない
GitHub Actionsにはかなり多くの機能があります。
- Matrix
- Cache
- Artifact
- Environment
- Concurrency
- Reusable Workflow
- Secrets
- Permissions
などです。
最初から全部入れると、何が原因で動かないのか分からなくなります。
自分の場合は、
まず1つのJobを動かす
↓
必要ならJobを分ける
↓
遅ければCache
↓
成果物が必要ならArtifact
↓
重複したらReusable Workflow
くらいの順番で考える方が分かりやすいと思っています。
YAMLをコピペする前に見るところ
今では、他のWorkflowを参考にするときも、最初にこの5つを見るようになりました。
on
↓
いつ動く?
jobs
↓
何をする?
runs-on
↓
どこで動く?
uses
↓
何のActionを使う?
run
↓
何のコマンドを実行する?
これだけでも、知らないWorkflowがかなり読みやすくなります。
Actionのバージョンも意識する
外部Actionを使う場合、
uses: actions/checkout@v7
のようにバージョンを指定します。
GitHubの公式ドキュメントでも、Actionを利用する際はGit ref、タグ、SHAなどでバージョンを指定することが推奨されています。
より厳密に固定するならCommit SHAを指定できます。
つまり、
Actionも依存ライブラリの一つ
として考えた方がよさそうです。
一度書いたWorkflowを何年も放置するのではなく、Actionのバージョンも定期的に確認する必要があります。
今回のまとめ
今回はGitHub Actionsの基本構造を改めて整理しました。
自分の中では、
Workflow
→ 自動化全体
Job
→ 大きな処理単位
Step
→ Job内の個別処理
Action
→ 再利用できる処理
Runner
→ Jobを実際に動かす環境
という理解にしています。
GitHub Actionsは、YAMLをコピーすれば意外と簡単に動きます。
だからこそ、
動いているけれど、なぜ動いているのか分からない
状態にもなりやすいです。
自分も最初はかなりそんな感じでした。
でも構造が分かると、
ここはTrigger
ここはRunner
ここからJob
これはAction
これはただのCommand
と分けて読めます。
これだけでも、Workflowを修正するときの分かりやすさがかなり変わりました。
次回は、
GitHub Actionsをpush・PR・手動実行で使い分ける
というテーマで、push、pull_request、workflow_dispatch などのTriggerについてもう少し深掘りします。
