概要
GitHub Actionsの
- 仕組みや構造
- 基本的な書き方(ワークフロー、ジョブ、イベントなど)
- よく出てくるruns-on、actions/checkoutで何してるのか
- 環境変数とsecrets
- サービスコンテナ
- 環境のsetup
- 権限
について解説していきたいと思います
そもそもGitHub Actionsとは
公式に
GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline.
と書かれているようにビルド、テスト、デプロイなどを自動化するCI/CDプラットフォームのことです
今まではCircleCIやTravisCIなどの外部サービスと連携する必要がありましたがGitHub Actionsと使うことでCI/CDをGitHub内で完結させることができるようになりました
GitHub Actionsの仕組み
.github/workflows
ディレクトリ内にymlファイルを設置し、Workflow(ワークフロー)をファイル内に書きます。指定したEvent(push、pull_requestなど。後述)が発生すると実行されます
.github/workflowsのディレクトリ名を間違えると実行されないので注意してください
Workflow(ワークフロー)はJob(ジョブ)単位で分けられています
JobはRunner(ランナー)という仮想マシンのインスタンス上で実行されます
JobはさらにStep単位で分けられており、Step内にコマンドなどの処理が実行されます。
書き方を学んでいこう!
簡単なワークフローを例に書き方を少しずつ学んでいきましょう
今回説明するのは
- jobs
- on
- runs-on
- uses
- steps
- run
- env
- secrets
- service container
です。かなり量が多いですが一つずつ確認していきましょう
jobs
処理の最上位単位です
jobsの中に1つ以上のジョブIDと共に処理が設定され、並列で実行されます
jobs:
# "my_first_job"というジョブIDでジョブを定義
my_first_job:
name: My first job
# "my_second_job"というジョブIDでジョブを定義
my_second_job:
name: My second job
左のJobsにジョブ名が記載されます
on
どのイベントが発生したら処理を実行するか指定します
# リモートリポジトリへpush時にjobsを実行
on: [push]
よく使われるのは以下の表のとおりです
イベント | 説明 |
---|---|
push | リモートリポジトリへpush時 |
pull_request | プルリクエスト作成時 |
deployment | デプロイ時 |
release | リリース時 |
issues | GitHub Issues関連の処理発生時 |
schedule | cronによる定期実行 |
上記以外で使用できるイベントはかなり多いため、詳細はドキュメントを参照してください
runs-on
ジョブを実行するOS(ランナー)を指定します
# ubutnuの最新版を指定
runs-on: ubuntu-latest
指定したランナーの詳細は実行する際にSet up jobsという項目が表示されるのでそこから確認できます
一般的なWeb開発ではUbuntuを選択するケースが多いかと思います(ローカル上で動かすコンテナのイメージがUbuntuかAlphineのため)
使用できるランナーの一覧は以下の通りです
使用できるランナー(OS) | 記述方法 | メモ |
---|---|---|
Windows Server 2022 | windows-latest/windows-2022 | windows-latestと記述してもWindows Server 2022を指定できる |
Windows Server 2019 | windows-2019 | - |
Ubuntu 22.04 | ubuntu-latest/ubuntu-22.04 | ubuntu-latestと記述してもUbuntu 22.04を指定できる |
Ubuntu 20.04 | ubuntu-20.04 | - |
macOS 13 | macos-13 | Beta版 |
macOS 12 | macos-latest/macos-12 | macos-latestと記述してもmacOS 12を指定できる |
macOS 11 | macos-11 | - |
uses
第三者もしくはGitHubが作成したActionsをyml内に記載することで実行することもできます
よく使われているのがGitHubが用意しているactions/checkout@v4
です
- name: Checkout
uses: actions/checkout@v4
usesに見たことない文字列が表示されていることが多いのでそういう時は下記のmarketplaceから検索してみましょう
actions/checkout@v4って何してるの?
興味のある方だけ見ていただければ幸いです
簡単なActionを作ってみたので挙動を確認してみましょう
今までのおさらいで
- jobs
- on
- runs-on
を使用しています。後述のsteps、name、runについては後ほど説明します
現時点ではこれ書いてたらコマンド実行できるんだなあと思っていただけたらと大丈夫です
リモートリポジトリへpushすると
- (チェックアウト前の)ファイル構成とパスを確認するコマンドを実行
- usesを使ってactions/checkout@v4を実行
- (チェックアウト後の)ファイル構成とパスを確認するコマンドを実行
するという流れになっています
name: command
on: [push]
jobs:
command:
name: Use Linux commands
runs-on: ubuntu-20.04
steps:
- name: Show ubuntu details
run: lsb_release -a
- name: Inspect files before checkout
run: ls -la
- name: show current directory before checkout
run: pwd
- name: Checkout
uses: actions/checkout@v4
- name: Inspect files after checkout
run: ls -la
- name: show current directory after checkout
run: pwd
- name: show all branches after checkout
run: git branch -a
まずはUbuntuのバージョンやコードネームを確認します
runs-onで指定したUbuntuが選択されていることが確認できます
- name: Show ubuntu details
run: lsb_release -a
Run lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.5 LTS
Release: 20.04
Codename: focal
次にactions/checkout@v4
を実行する前のファイル構成とパスを確認します
- name: Inspect files before checkout
run: ls -la
- name: show current directory before checkout
run: pwd
/home/runner/work/<リポジトリ名>/<リポジトリ名>
のパス内に何も入っていないことが確認できます
Run ls -la
total 8
drwxr-xr-x 2 runner docker 4096 Oct 9 22:02 .
drwxr-xr-x 3 runner docker 4096 Oct 9 22:02 ..
Run pwd
/home/runner/work/<リポジトリ名>/<リポジトリ名>
/home/runner/work/<リポジトリ名>/<リポジトリ名>
ってどこから来ているのか解説すると、自身で作成されたワークフローを実行するパスのことで$GITHUB_WORKSPACEという環境変数を読み込んで実行しています
環境変数の中身を確認すると以下のようになっているかと思います
GITHUB_WORKSPACE=/home/runner/work/<リポジトリ名>/<リポジトリ名>
/home/runner/work/
までは一緒ですがその後はWorkflowを実行するリポジトリ名がディレクトリ名として指定されます
actions/checkout@v4
を実行します
- name: Checkout
uses: actions/checkout@v4
Run actions/checkout@v4
Syncing repository: shun198/***-<リポジトリ名>
Getting Git version info
Temporarily overriding HOME='/home/runner/work/_temp/da727d01-5e2c-459c-b8db-380fb0265762' before making global git config changes
Adding repository directory to the temporary git global config as a safe directory
/usr/bin/git config --global --add safe.directory /home/runner/work/<リポジトリ>/<リポジトリ>
Deleting the contents of '/home/runner/work/*/<リポジトリ名>/<リポジトリ名>'
Initializing the repository
Disabling automatic garbage collection
Setting up auth
Fetching the repository
Determining the checkout info
Checking out the ref
/usr/bin/git log -1 --format='%H'
ログがかなり多いので手順を簡単に説明すると
- ランナー内のリポジトリのGitの処理設定
- Gitの認証設定
- リモートリポジトリから処理を実行するブランチのリポジトリのソースコードをfetch
- fetchしたソースコードと同じブランチをチェックアウト
しています。要するにランナー内にリモートリポジトリにあるソースコードをクローンに限りなく近い形(厳密には違う)で複製していることになります。
クローンだとデフォルトのブランチ(main、develop)のソースコードしか抽出できず、作業する際に使うfeatureブランチのソースコードだけテストできないからfetchとcheckoutをしているのだと筆者は考えています
これについて詳しい方がいましたらぜひご教授いただけると幸いです
actions/checkout@v4
の詳細を知りたい方は実際にログを確認してみてください
実行した後のファイル構成、パス、ブランチ一覧を確認します
- name: Inspect files after checkout
run: ls -la
- name: show current directory after checkout
run: pwd
- name: show all branches after checkout
run: git branch -a
/home/runner/work/<リポジトリ名>/<リポジトリ名>
のパス内に自身が作業したブランチのファイル構成が確認できました
また、git branch -a
を実行してもmainやdevelopブランチはなく、作業していたfeatureブランチのみ表示されたことが確認できました(git cloneだとデフォルトで設定したmainブランチのソースコードがクローンされるはずです)
Run ls -la
ls -la
shell: /usr/bin/bash -e {0}
total 88
drwxr-xr-x 11 runner docker 4096 Oct 9 22:02 .
drwxr-xr-x 3 runner docker 4096 Oct 9 22:02 ..
drwxr-xr-x 2 runner docker 4096 Oct 9 22:02 .devcontainer
drwxr-xr-x 8 runner docker 4096 Oct 9 22:02 .git
drwxr-xr-x 3 runner docker 4096 Oct 9 22:02 .github
-rw-r--r-- 1 runner docker 3088 Oct 9 22:02 .gitignore
drwxr-xr-x 2 runner docker 4096 Oct 9 22:02 .vscode
-rw-r--r-- 1 runner docker 4664 Oct 9 22:02 README.md
drwxr-xr-x 5 runner docker 4096 Oct 9 22:02 containers
-rw-r--r-- 1 runner docker 1836 Oct 9 22:02 docker-compose.yml
-rwxr-xr-x 1 runner docker 126 Oct 9 22:02 entrypoint.sh
-rwxr-xr-x 1 runner docker 661 Oct 9 22:02 manage.py
drwxr-xr-x 3 runner docker 4096 Oct 9 22:02 application
-rw-r--r-- 1 runner docker 207 Oct 9 22:02 requirements.txt
drwxr-xr-x 5 runner docker 4096 Oct 9 22:02 static
Run pwd
pwd
shell: /usr/bin/bash -e {0}
/home/runner/work/<ディレクトリ名>/<ディレクトリ名>
Run git branch -a
git branch -a
shell: /usr/bin/bash -e {0}
* feature/01
actions/checkout@v4
の説明は以上です
steps
図でも説明した通り一つのjobsは1つ以上のstepsで構成されており、stepsの中にrunコマンドが1つ以上構成されています
run
runに実行するコマンドを以下のように記載していきます
nameにrunの処理について書くことができ、GitHub上で確認できます
- name: Inspect files before checkout
run: ls -la
- name: show current directory before checkout
run: pwd
また、複数以上のコマンドを実行したい時は run |
と記載します
- name: Inspect and show current directory files before checkout
run: |
ls -la
pwd
env
ワークフロー内に環境変数を設定できます
環境変数を使用する際はenvの下に以下のように
SECRET_KEY: test
という風に環境変数の名前と対応する値を記載します
env:
SECRET_KEY: test
DJANGO_SETTINGS_MODULE: project.settings.local
ALLOWED_HOSTS: 127.0.0.1
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test-db
MYSQL_HOST: 127.0.0.1
MYSQL_PORT: 3306
MYSQL_USER: test
MYSQL_PASSWORD: test
secrets
こちらも環境変数を格納することができます
envと違って
- AWSのシークレットキー
- AWSのアクセスキー
など見られたくない情報を格納するときに使うのが一般的です
secretsを使うことで秘匿情報を暗号化した状態で使用できます
以下にsecret名と値を入れます
一度Add secretを押すと二度と中身が見れないので注意してください
secretsの使用方法
secretsを使用するときは
${{ secrets.AWS_ACCESS_KEY }}
という風に記載することで暗号化した状態の環境変数を使用できます
以下が公式のAWSの認証用のActionを使った例です
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
aws-region: ap-northeast-1
デバッグの設定
Settingsタブのsecretsに
- ACTIONS_RUNNER_DEBUG
- ACTIONS_STEP_DEBUG
をTrueで保存すると
- Runnerの実行ログ
- Step毎の実行ログ
を確認することができ、デバッグ効率がよくなリます
サービスコンテナ
ワークフローを実行する際に必要になるサービスを使用するためのDockerコンテナです
バックエンドの単体テストを自動実行する際にDBのサービスコンテナを使うのが一般的です
サービスコンテナを使うことでGitHub内のランナー上でDBなどのサービスを利用する必要がないのでワークフローを単純化できる上にランナー内の限られたリソースを無駄に使わなくて済むので実行速度も短縮できます
サービスコンテナの設定方法
services:
db:
image: mysql:8.0
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test-db
MYSQL_USER: test
MYSQL_PASSWORD: test
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
サービスコンテナを作成するときはservicesを定義し、
imageはDocker hubにある各サービスの公式のDocker imageのバージョンを指定します
今回はMySQLのコンテナを作成します
DBをフレームワークと接続する際は環境変数も設定するのが一般的なので先ほど紹介したenvを使用します
今回の例ではテストを実行するだけなのでsecrets内に環境変数を定義せずにテスト用の環境変数を使用しています
options
options内に実行したい任意のコマンドを記載します
DBやRedisなどのインメモリキャッシュとの接続の際はヘルスチェックコマンドを記載するのが一般的です
ヘルスチェックって何?コマンドの意味がわからないという方は以下の記事を参考にしてください
サービスコンテナの詳細は以下のとおりです
環境のsetup
GitHub Actionsの公式もしくは各言語の公式が出しているactionを使うとワークフロー内でパッケージのインストールコマンドを書かずに設定できます
例えばGitHub Actionsが出しているsetup-pythonというactionを使うだけでランナー内にpipを入れたり、Pythonを入れるコマンドを書かずにPythonを実行できる環境を用意できます
今回は
- Python
- Node.js
を例にsetup系のactionの使い方を記載します
Python
公式が出しているPythonのsetup用のactionです
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: python my_script.py
オプションとしてPoetryを使ってインストールしたパッケージをCacheできます
Cacheを使うことで2回目以降ワークフローを実行する際にpoetry installを実行するとインストールしたパッケージをCacheを参照して使うのでインストール時間が短縮されます
steps:
- uses: actions/checkout@v4
- name: Install poetry
run: pipx install poetry
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'poetry'
- run: poetry install
また、python-version-fileのオプションを使うことでpyproject.toml内に記載されたPythonのversionを自動的に適用させることもできます
- uses: actions/setup-python@v5
with:
python-version-file: "pyproject.toml"
cache: 'poetry'
- run: poetry install
以下がsetup-pythonの詳細です
Node.js
公式が出しているNode.jsのsetup用のactionです
Nodeのversionについては16,18,20をサポートしています
以下がnpmを使ってCacheを使うときのワークフロー例です
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
- run: npm ci
npm以外にyarnを使ったCacheのワークフローも実装できます
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
cache: 'yarn'
cache-dependency-path: '**/package-lock.json'
- run: npm ci
また、node-version-fileのオプションを使うことでpackage.json内に記載されたNodeのversionを自動的に適用させることもできます
The node-version-file input accepts a path to a file containing the version of Node.js to be used by a project, for example .nvmrc, .node-version, .tool-versions, or package.json. If both the node-version and the node-version-file inputs are provided then the node-version input is used. See supported version syntax.
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
- run: npm ci
- run: npm test
Voltaを使ってNodeのバージョンを指定した際はVoltaに記載したNodeのバージョンを適用します
もし、Voltaの記述がない場合はenginesがあればengines内のNodeのバージョンを適用します
When using the package.json input, the action will look for volta.node first. If volta.node isn't defined, then it will look for engines.node.
{
"engines": {
"node": ">=16.0.0"
},
"volta": {
"node": "16.0.0"
}
}
権限
GitHub Actionsではデフォルトのバージョンをワークフロー内で自身で決めることもできます
各権限のことをスコープといい、デフォルトは全てread権限です
スコープには
- read
- write
- none
の3つの権限を付与できます
ワークフロー内でスコープとその権限を一つ以上定義した場合、定義していない他のスコープの権限は全てnoneになります
各スコープの詳細は以下の通りです
permissions:
actions: read|write|none
checks: read|write|none
contents: read|write|none
deployments: read|write|none
id-token: read|write|none
issues: read|write|none
discussions: read|write|none
packages: read|write|none
# GitHub Pages
pages: read|write|none
pull-requests: read|write|none
repository-projects: read|write|none
security-events: read|write|none
statuses: read|write|none
permissionを自身で定義するワークフロー例
GitHubが公式に出しているconfigure-aws-credentialsを例に説明します
本ワークフローではAWSと認証するためにトークンが必要で、そのトークンを使うためにpermissionを変更しているんだ、くらいの理解で大丈夫です
jobs:
deploy:
name: Upload to Amazon S3
runs-on: ubuntu-latest
# These permissions are needed to interact with GitHub's OIDC Token endpoint.
permissions:
id-token: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS credentials from Test account
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::111111111111:role/my-github-actions-role-test
aws-region: us-east-1
- name: Copy files to the test website with the AWS CLI
run: |
aws s3 sync . s3://my-s3-test-website-bucket
- name: Configure AWS credentials from Production account
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::222222222222:role/my-github-actions-role-prod
aws-region: us-west-2
- name: Copy files to the production website with the AWS CLI
run: |
aws s3 sync . s3://my-s3-prod-website-bucket
yml内で以下のようにpermissionを使っています
permissions:
id-token: write
contents: read
id-tokenはGitHub Actions内でOIDCを使う際に使用します
Fetch an OpenID Connect (OIDC) token. This requires id-token: write. For more information, see "About security hardening with OpenID Connect"
先ほど説明した通り全てのスコープのデフォルトの権限はreadです
OIDCを取得するにはwrite権限を付与する必要があるので明記します
ここで一つ注意してほしいのが、先ほど
ワークフロー内でスコープとその権限を一つ以上定義した場合、定義していない他のスコープの権限は全てnoneになります
と記載しました
ワークフロー内でcheckout
を使ってリポジトリ内のコードをfetchしています
id-tokenのpermissionをwriteにしてしまうとcheckoutを実行するのに必要なcontentsスコープがnoneになってしまい、fetchできなくなってしまうのでcontentsのpermissionをreadと明記する必要があります
permissionを書き換えるときは注意しましょう
まとめ
初めてGitHub Actionsを触った時はどれが何をしているかさっぱりわからなかったのですが仕組みや書き方をある程度理解していればそこまで難しくなく、むしろやっていて楽しいなと個人的に思いました
記事の紹介
本記事は基礎編なのでより本格的なGitHub Actionsの使い方について詳しく記載しきれませんでしたが、
- Cache
- メタデータ構文
- GitHub Pages
- OpenID Connect
など様々な記事を執筆したので興味ある方はよかったら見ていただけると幸いです
参考