概要
GitHub Actionsの
- 仕組みや構造
- 基本的な書き方(ワークフロー、ジョブ、イベントなど)
- よく出てくるruns-on、actions/checkoutで何してるのか
- 環境変数とsecrets
- サービスコンテナ
- 環境のsetup
- 権限
- GitHub Pages
- Matrix
- メタデータ構文
- OpenID Connect
について解説していきたいと思います
そもそも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
処理の最上位単位です
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
オプション
checkoutにはコミット履歴を取得する機能も存在します
以下のようにfetch-depth: 0
と指定すれば全てのタグとブランチのコミット履歴を取得できます
jobs:
command:
name: Use Linux commands
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
actions/checkout@v4
の説明は以上です
steps
図でも説明した通り一つのjobsは1つ以上のstepsで構成されており、stepsの中にrunコマンドが1つ以上構成されています
jobs:
command:
name: Use Linux commands
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v4
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を使用するときは
${{ 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を例に説明します
詳細はOpenID Connectの章で説明しますが、本ワークフローでは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@v4
- 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 Pages
HTML、CSS、JSなどの静的ファイルを直接取得した後、任意でビルドプロセスを通じてファイルを実行し、ウェブサイトを公開できる静的なサイトホスティングサービスです
使用例として挙げられるのは
- Swagger
- StoryBook
のホスティングです
プライベートリポジトリでの使用はGitHub Pro、GitHub Team、GitHub Enterprise Cloud、GitHub Enterprise Serverで利用できます
今回はSwaggerをGitHub Pagesへ公開するワークフローを作成します
まず、Legion2/swagger-ui-action@v1
を使ってswagger-uiフォルダに静的ファイルとしてexportします
次に公式のupload-pages-artifact
を使ってGitHub PagesへアップロードできるようArtifactを作成します
似たようなactionにupload-artifact
もありますが
- Artifact名をgithub-pagesにする必要がある
- 単一のtarファイルを含む単一のgzipアーカイブにする必要がある
など手間がかかるので公式ではGitHub Pages用のArtifactを生成するのであればupload-pages-artifact
を使うよう推奨しています
While choosing to use this action as part of your approach to deploying to GitHub Pages is technically optional, we highly recommend it since it takes care of producing (mostly) valid artifacts.
However, if you do not choose to use this action but still want to deploy to Pages using an Actions workflow, then you must upload an Actions artifact that meets the following criteria:
・Be named github-pages
・Be a single gzip archive containing a single tar file
最後に公式のdeploy-pages
を使って作成したArtifactをデプロイします
name: Deploy Swagger UI to GitHub Pages
on:
push:
branches:
- develop
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Chekcout code
uses: actions/checkout@v4
- name: Install swagger-cli
run: npm install -g swagger-cli
- name: Generate Swagger UI
uses: Legion2/swagger-ui-action@v1
with:
output: swagger-ui
spec-file: doc/openapi.yml
- name: Upload Documents
uses: actions/upload-pages-artifact@v3
with:
path: swagger-ui
# Deploy the artifact to GitHub pages.
# This is a separate job so that only actions/deploy-pages has the necessary permissions.
deploy:
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
GitHub Pagesの設定は
Settings>Pages
で行うことができ、GitHub Actionsを選択します
2024年6月30日から全てのGitHub PagesではGitHub Actionsが使用されるので今後も使用する場合はGitHub Actionsを有効にする必要があります
ワークフローを実行すると以下のようにdeployの箇所にURLのリンクがあります
URLにアクセスすると以下のようにSwaggerが表示されます
ただし、静的ファイルをアップロードしてるだけなので実行することはできません
Matrix
同じワークフローを複数の
- 実行環境(Node.jsなど)
- runnerOS (Ubuntuなど)
- 言語(Pythonなど)
のバージョンで実行できる仕組みです
Matrixを使うと例えばバージョンをアップデートする際の検証やトラブルシューティングに役立つかと思います
デフォルトでは全てのバージョンを以下のように並列で実行できます
また、Matrixを使用する際は一つのJobsが失敗すると他のJobsが実行されないのも特徴です
(ただし、continue-on-errorを追加した場合は除く)
Matrixの設定方法
Matrixを設定する際は以下の例のようにstrategy
を定義し、その下にmatrix
を定義します
matrixの下に任意の変数を定義し、テストしたいバージョンの一覧を配列に入れます
jobs:
Setup:
name: Run Test Code
# 実行したいバッケージ等のバージョンを配列内にstrategyとmatrixに指定
strategy:
matrix:
python-version: [3.10.5, 3.10.6, 3.10.7]
os: [ubuntu-latest, ubuntu-20.04]
Matrixで定義したバージョンを適用させたい処理には以下のように記載します
例えばOSのバージョンを適用させたいときは以下のように記載します
${{ matrix.os }}
以下がワークフロー例です
name: Run Pytest Using Matrix
on: workflow_dispatch
env:
SECRET_KEY: test
DJANGO_SETTINGS_MODULE: project.settings
ALLOWED_HOSTS: 127.0.0.1
DEBUG: "True"
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test-db
MYSQL_HOST: 127.0.0.1
MYSQL_PORT: 3306
MYSQL_USER: test
MYSQL_PASSWORD: test
jobs:
Setup:
name: Run Test Code
# 実行したいバッケージ等のバージョンを配列内にstrategyとmatrixに指定
strategy:
matrix:
python-version: [3.10.5, 3.10.6, 3.10.7]
os: [ubuntu-latest, ubuntu-20.04]
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: application
services:
db:
image: mysql:8.0
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: ${{ env.MYSQL_ROOT_PASSWORD }}
MYSQL_DATABASE: ${{ env.MYSQL_DATABASE }}
MYSQL_USER: ${{ env.MYSQL_USER }}
MYSQL_PASSWORD: ${{ env.MYSQL_PASSWORD }}
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Chekcout code
uses: actions/checkout@v4
- name: Grant privileges to user
run: mysql --protocol=tcp -h 127.0.0.1 -P 3306 -u root -p$MYSQL_ROOT_PASSWORD -e "GRANT ALL PRIVILEGES ON *.* TO '$MYSQL_USER'@'%'; FLUSH PRIVILEGES;"
- name: Install poetry
run: pipx install poetry
- name: Use cache dependencies
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'poetry'
- name: Install Packages
run: poetry install
- name: Run migration
run: |
poetry run python manage.py makemigrations
poetry run python manage.py migrate
- name: Run Pytest
run: poetry run pytest -x -n auto --cov --no-cov-on-fail --suppress-no-test-exit-code
特定のバージョンのみ追加したいとき
例えば
- Python 3.10.8
- Ubuntu 18.04
だけ追加で検証したいときはinclude
を使用します
2つとも配列に入れてしまうと
- Python 3.10.8
- Ubuntu 20.04 と latest
など、実行しなくてもいいバージョンのJobまで実行されてしまいます
そのため、今回みたいなケースではinclude
を使用します
strategy:
matrix:
python-version: [3.10.5, 3.10.6, 3.10.7]
os: [ubuntu-latest, ubuntu-20.04]
# includeを使って python-version: 3.10.8 と ubuntu-18.04 のみ追加
include:
- python-version: 3.10.8
os: ubuntu-18.04
以下のように
- Python 3.10.8
- Ubuntu 18.04
のJobのみ追加されていることが確認できます
特定のバージョンのみ除外したいとき
特定のバージョンのみ除外するときはincludeとは逆にexcludeを使用します
今回は
- Python 3.10.5
- Ubuntu 20.04
のJobのみ実行しないようにします
strategy:
matrix:
python-version: [3.10.5, 3.10.6, 3.10.7]
os: [ubuntu-latest, ubuntu-20.04]
# includeを使って python-version: 3.10.5 と ubuntu-20.04 のみ除外
exclude:
- python-version: 3.10.5
os: ubuntu-20.04
以下のように
- Python 3.10.5
- Ubuntu 20.04
が実行されていないことが確認できます
メタデータ構文
メタデータ構文を使うことで
- パッケージのインストール
- Cacheの設定
など、複数のワークフローで共通利用できるものを1つのファイルにまとめることができます
今回は一般的によく利用されているcomposite action
について解説します
composite action
を使用するために共通化したい
- パッケージのインストール
- Cacheの設定
などの処理をaction.ymlに記載します
作成するファイルの構成は以下の通りです
tree
└──.github
├── actions
│ └── set-up-node
| └── action.yml
└── workflows
├── build.yml
└── jest.yml
今回は今後別の処理を共通化したい時を想定してaction.ymlを.github/actions/set-up-node/
内に格納します
name: 'Setup Node.js'
description: 'Setup Node.js by using cache and npm'
inputs:
working-directory:
description: 'working-directory of package-lock.json'
required: true
runs:
# compositeが必須
using: 'composite'
steps:
- name: Setup NodeJS
uses: actions/setup-node@v4
with:
node-version-file: ${{ inputs.working-directory }}/package.json
- name: Install dependencies
run: npm ci
shell: bash
working-directory: ${{ inputs.working-directory }}
メタデータファイルを作成する場合は以下の構文が必須です
構文 | 説明 |
---|---|
name | アクションの名前 |
description | アクションの簡単な説明 |
runs | 共通化したい処理を記載 |
using | compositeに設定する必要があります |
action.yml内でrun句を使うとき
runを使用するときは使用するshellを必ず定義する必要があります
shell: bash
以下に使用できるshellが記載されていますが基本的にはbashで問題ないかと思います
また、action.yml内で処理を行う際は参照先で
defaults:
run:
working-directory: application
と定義したとしてもルートディレクトリで実行されてしまいます
そのため、action.yml内の処理にworking-directoryを使ってディレクトリを指定する必要があります
inputs
そこでinputsを使って参照先のyml側で実行するディレクトリを指定するようにすれば
action.yml内でハードコーディングしなくてもよくなります
今回は必須入力にしています
inputs:
working-directory:
description: 'working-directory of package-lock.json'
required: true
また、任意入力にする場合はrequiredの値をfalseにし、defaultを定義することでデフォルト値を設定することもできます
inputs:
working-directory:
description: 'working-directory of package-lock.json'
required: false
default: 'application'
作成したaction.ymlの処理をワークフローで使おう!
- build.yml
- jest.yml
にaction.ymlの処理を参照するよう設定します
まずは、usesにaction.ymlのパスを指定します
パスを指定する際はaction.ymlまで記載しなくても大丈夫です
また、inputsを使用する際はwith内に値を指定します
working-directoryにapplicationを指定します
- name: Setup Node.js
uses: ./.github/actions/set-up-node
with:
working-directory: ${{ env.WORKING_DIRECTORY }}
以上を踏まえると以下のようになります
# アクション名
name: Jest
# タイミングを指定
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
env:
WORKING_DIRECTORY: application
jobs:
test:
name: Run test codes
if: |
github.event.pull_request.draft == false
&& !startsWith(github.head_ref, 'release')
&& !startsWith(github.head_ref, 'doc')
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{ env.WORKING_DIRECTORY }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/set-up-node
with:
working-directory: ${{ env.WORKING_DIRECTORY }}
- name: Show coverage
run: npm test -- --bail --maxWorkers=100% --watchAll=false --coverage
name: Build
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
env:
WORKING_DIRECTORY: application
jobs:
build:
name: Build
if: |
github.event.pull_request.draft == false
&& !startsWith(github.head_ref, 'release')
&& !startsWith(github.head_ref, 'doc')
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{ env.WORKING_DIRECTORY }}
steps:
- name: Check out
uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/set-up-node
with:
working-directory: ${{ env.WORKING_DIRECTORY }}
- name: Run npm run build
run: npm run build
OpenID Connect
OpenID Connectを使用することでAWSの
- シークレットキー
- アクセスキー
を使わずにIAMロールを使ってよりセキュアにGitHub Actionsを実行できますので設定していきます
OpenID Connectとは
簡単にいうと認証を行うための仕組みです
OpenID Connectを使用することでAWSの
- シークレットキー
- アクセスキー
を使わずにIAMロールを使ってよりセキュアにGitHub Actionsを実行できます
GitHub Actions内でOpenID Connectを使うメリットは以下の通りです
- GitHub Secretsでアクセスキーとシークレットキーを管理する必要がないため、AWS関連の秘匿情報を管理するコストがなくなる
- IAMロールを使った運用になるため、ロールに応じた細かい権限別のワークフローを実行できる
- OpenID Connectを使う際に生成されるJWTトークンが1つのジョブに対してのみ有効であり、自動的に失効する有効期間が短いトークンを使用できるためセキュリティリスクが低い
OpenID Connectの仕組み
仕組みをざっと説明すると
- AWSなどのクラウドプロバイダにOpenID Connect(OIDC Trust)を設定
- GitHub Actionsのワークフロー内のjobを実行するたびにGitHubがJWTトークンを生成し、クラウドプロバイダへ渡す
- クラウドプロバイダが受け取ったJWTトークンを使って認証が成功したらGitHubのOpenID Connectのプロバイダがクラウドプロバイダからアクセストークンを受け取る
- 受け取ったアクセストークンを使用してaws cliコマンドをIAMロールを使って実行する
という流れになっています
詳細は以下の公式ドキュメントを参照してください
Next.jsのアプリケーションをS3へデプロイするには
今回はNext.jsのアプリケーションをS3へデプロイするワークフローを作成するので
- 静的ファイルをデプロイするS3バケット
- デプロイを実行するIAMロール
の作成が完了した状態でワークフローを作成します
OpenID Connectを使用する際はAWSが出している公式のActionを使用します
name: Deploy Frontend Project to S3
on:
push:
branches:
- main
env:
REGION_NAME: ap-northeast-1
WORKING_DIRECTORY: frontend
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
defaults:
run:
working-directory: ${{ env.WORKING_DIRECTORY }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: configure aws credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ secrets.S3_DEPLOY_ROLE }}
role-session-name: deploy_role_session
aws-region: ${{ env.REGION_NAME }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: ${{ env.WORKING_DIRECTORY }}/package.json
- name: Build and Export
run: npm run build
- name: Deploy To S3 Bucket
run: aws s3 sync --region ${{ env.REGION_NAME }} ./out s3://${{ secrets.BUCKET_NAME }} --delete
S3バケット、IAMロール、OpenID Connectの作成および設定方法は以下の記事を参照してください
まとめ
初めてGitHub Actionsを触った時はどれが何をしているかさっぱりわからなかったのですが仕組みや書き方をある程度理解していればそこまで難しくなく、むしろやっていて楽しいなと個人的に思いました
参考