Github Actionsなんもわからん
Github Actionsなんも分かりません。わからないのでおててを動かしていきます。
Github actionsとは?
GithubActinosは、CI(Continuous Integration)/CD(Continuous delivery/deployment)のためのツールになります。
gpt4oに聞いてみました。
GitHub Actions(ギットハブ・アクションズ)は、GitHub上で自動化されたワークフロー(作業の流れ)を実行できるCI/CDツールです。
もっと具体的に言うと…
GitHub Actionsを使うと、以下のような作業を自動で行えます:
- コードがプッシュされたときに自動でテストを実行する
- プルリクエストが作られたときにコードをビルドして確認する
- 本番環境にデプロイする
- 定期的なバッチ処理を走らせる(毎日・毎週など)
- 静的解析やフォーマットチェックを行う
だそうです。
コードを書いた後のテストやデプロイなどの工程を自動化することで、効率化を図れるサービスのようです。自動化万歳!
まずは、公式のチュートリアルを見て学ぶ
今回は、まずは公式のチュートリアルがあったのでそれをすべて見てから話を進めていきます。
(このチュートリアル、ページをリロードするとREADME.mdが徐々に更新されるというなかなか粋なものなのでぜひとも味わってほしい。)
step1 リポジトリを作成する
上記のページから飛んで、start courseというところをクリックし、リポジトリを作成しました。その後、少し待ってからページをリロードしました。
step2 pull requestを出したうえでworkflowを設定する
Github Actionsについての説明を読んだ後、main
ブランチへのプルリクエストを出した後、welcome-workflow
ブランチ内部.github/workfrows
の下に次のファイルを作成しました。
コードの説明はgpt-4oにお願いしました。
name: Post welcome comment # github actionsのタブに表示される名前
on: # イベントのトリガー ワークフローが開始する条件
pull_request: # プルリクエストをトリガーとして指定
types: [opened] # プルリクエストが新規に作成された場合にのみ反応する
permissions: # github apiのアクセス権限を指定
pull-requests: write # プルリクエストに書き込みをする権限を与えるもの
step3 workflowを編集する
先ほど作成したwelcome.yml
を以下のように編集しました。
name: Post welcome comment
on:
pull_request:
types: [opened]
permissions:
pull-requests: write
jobs: # 実際に行う処理の流れを定義(jobs)
build: # それぞれの個別処理の名前 (jobのID)
name: Post welcome comment # 表示されるjobの名前
runs-on: ubuntu-latest # ジョブが実行される仮想環境(ランナー)の指定 今回はubuntu-latest
これによって、workflowを実行する環境を設定できた。
step4 実際にpull-requestにコメントを自動で添付する
次に、githubのアクセストークンを発行しました。作成方法は以下を参考にしました。
1. githubのサイトにログイン
2. profile(画面右上のアカウント画像クリック)
3. 開発者設定
4. personal access tokensのtokens(classic)を選択
5. generate new token -> generate new token (classic)
6. repoとworkflowにチェックを入れて作製
7. 表示されたトークンをコピー
8. 自分のリポジトリの上の方の一番右のsettingsを選択
9. 左サイドバーのsecrets and variablesのactionsを選択
10. リポジトリの環境変数に、secretわ設定する。(name:TOKEN_GITHUBACTIONSに設定。)
リポジトリの環境変数の名前を、github_
から始まるものにすることはできないとのことです。なので、今回は名称をサンプルコードから変更しています。
name: Post welcome comment
on:
pull_request:
types: [opened]
permissions:
pull-requests: write
jobs:
build:
name: Post welcome comment
runs-on: ubuntu-latest
steps:
- run: gh pr comment $PR_URL --body "Welcome to the repository!"
env:
GITHUB_TOKEN: ${{ secrets.TOKEN_GITHUBACTIONS }}
PR_URL: ${{ github.event.pull_request.html_url }}
実際にworkflowが終了すると、リポジトリのactionsから、workflowの実行結果を確認できます。
今回はこのようになっていました。
jobsの部分が、設定したPost welcome commentになっているのが確認できるかと思います。
最後に、pull requestからmerge、confirmしてブランチをマージしました。
チュートリアルまとめ
以上で公式のチュートリアルは終了しました。しかしながら、まだ全然わかった気がしません。
なので、次に、公式のサンプルをいろいろとみていこうと思います。
Actionsのドキュメントを読もうの会
読もうの会です。サンプルコードがいろいろとあるので、興味のあるコードを拾ってみていきます。
ドキュメント
ワークフローについて
まずはワークフローそのものについてです。
ワークフローのトリガー
ひたすらにコードを見ていきます。
# 任意ブランチへのプッシュで実行
on: push
# 任意ブランチへのプッシュorフォークで実行
on: [push, fork]
# ラベル作成時、mainブランチへのプッシュ、github pages対応ブランチへのプッシュ
on:
label:
types:
- created
push:
branches:
- main
page_build:
# issueのopenとラベル付けで開始(二つ引っかかると二回実行)
on:
issues:
types:
- opened
- labeled
#ブランチへのフィルター
on:
push:
branches:
- main
- 'releases/**' # releases/とパターン一致するブランチ
- '!releases/**-alpha' # 逆に、パターン一致するものを無視する。(!での否定)
branches-ignore: # 直接無視するものを指定もできる
- 'mona/octocat'
- 'releases/**-alpha'
tags: # commit時に指定できるタグに関連する指定
- v2
- v1.*
tags-ignore:
- v2
- v1.*
paths: # パス一致があると実行
- '**.js'
- 'sub-project/**'
- '!sub-project/docs/**'
paths-ignore: # パス一致があっても無視
- 'docs/**'
# workflow_runイベント関連
# 以下の者は、指定ブランチで"Build"という名前のワークフローが稼働している場合のみ実行
on:
workflow_run:
workflows: ["Build"]
types: [requested]
branches:
- 'releases/**'
# workflow_dispatchイベント
# ワークフローを手動でトリガーできるようにするイベント
# 具体的には、log_level, print:tags, tags, environmentを入力することでワークフローが実行される。
on:
workflow_dispatch:
inputs:
logLevel:
description: 'Log level'
required: true
default: 'warning'
type: choice
options:
- info
- warning
- debug
print_tags:
description: 'True to print to STDOUT'
required: true
type: boolean
tags:
description: 'Test scenario tags'
required: true
type: string
environment:
description: 'Environment to run tests against'
type: environment
required: true
jobs:
print-tag:
runs-on: ubuntu-latest
if: ${{ inputs.print_tags }}
steps:
- name: Print the input tag to STDOUT
run: echo The tags are ${{ inputs.tags }}
# イベント情報の取得
jobs:
print_context:
runs-on: ubuntu-latest
steps:
- env:
EVENT_CONTEXT: ${{ toJSON(github.event) }}
run: |
echo $EVENT_CONTEXT
# シンプルにイベントで使用できるプロパティの確認
# イベント情報活用の例
# 特定のユーザー以外のpull requestを許さない例
on:
pull_request:
types:
- opened
paths:
- '.github/workflows/**'
- '.github/CODEOWNERS'
- 'package*.json'
jobs:
triage:
if: >- # イベント情報(ユーザーid)による条件一致
github.event.pull_request.user.login != 'octobot' &&
github.event.pull_request.user.login != 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- name: "Comment about changes we can't accept"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR: ${{ github.event.pull_request.html_url }}
run: |
gh pr edit $PR --add-label 'invalid'
gh pr comment $PR --body 'It looks like you edited `package*.json`, `.github/CODEOWNERS`, or `.github/workflows/**`. We do not allow contributions to these files. Please review our [contributing guidelines](https://github.com/octo-org/octo-repo/blob/main/CONTRIBUTING.md) for what contributions are accepted.'
# 条件分岐
on:
issues:
types:
- labeled
jobs:
run_if_label_matches:
if: github.event.label.name == 'bug' # 条件分岐設定
runs-on: ubuntu-latest
steps:
- run: echo 'The label was bug'
# イベントタイプ情報による分岐
on:
issues:
types:
- closed
pull_request:
types:
- closed
jobs:
state_event_type:
runs-on: ubuntu-latest
steps:
- name: if_issue
if: github.event.issue # issueの場合のみ実施
run: |
echo An issue was closed
- name: if_pr
if: github.event.pull_request # pull_requestのみ実施
run: |
echo A pull request was closed
# job同士の依存関係の明示
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: build
run: |
echo 'building'
publish:
needs: [build] # build jobが実行されてから実行される
runs-on: ubuntu-latest
environment: production
steps:
- name: publish
run: |
echo 'publishing'
ワークフロー構文
ひたすらにコードを見ていきます。
# スケジュールでトリガー
on:
schedule:
# * is a special character in YAML so you have to quote this string
- cron: '30 5,17 * * *'
- cron: '30 5 * * 2,4'
# 分、時間、日、月、曜日で指定
# 複数トリガーも可能
jobs:
test_schedule:
runs-on: ubuntu-latest
steps:
- name: Not on Monday or Wednesday
if: github.event.schedule != '30 5 * * 1,3'
run: echo "This step will be skipped on Monday and Wednesday"
- name: Every time
run: echo "This step will always run"
# 再利用可能な形でのワークフロー定義
# トリガーをworkflow_callにすることで可能になる
# 事前に、必要となる変数を定義しておく必要がある
# 呼び出す際に、それらのパラメーターを渡す必要あり。
# 再利用可能なワークフローの例
name: Reusable workflow example
on:
workflow_call: # 必要となる変数の定義
inputs:
config-path:
required: true
type: string
secrets:
token:
required: true
jobs: # 変数とともに呼び出されたときに実施されるワークフロー
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
with:
repo-token: ${{ secrets.token }}
configuration-path: ${{ inputs.config-path }}
# 実際に呼び出す場合
jobs:
call-workflow-passing-data:
uses: octo-org/example-repo/.github/workflows/reusable-workflow.yml@main
# ワークフローファイルのパス@ブランチ名で指定
with: # inputsの入力
config-path: .github/labeler.yml
secrets: # secretsの入力
personal_access_token: ${{ secrets.token }}
# 変数で回して実施
jobs:
ReuseableMatrixJobForDeployment:
strategy: # 複数変数渡すときのやつ
matrix:
target: [dev, stage, prod]
uses: octocat/octo-repo/.github/workflows/deployment.yml@main
with:
target: ${{ matrix.target }}
# 入れ子関係でのシークレットの受け渡し
# ただし、入れ子関係は4つ連結までが限界
# 今、A→B→Cの関係で読み出している
# A→Bでの呼び出し(シークレット情報はA→Bですべて受け渡す)
jobs:
workflowA-calls-workflowB:
uses: octo-org/example-repo/.github/workflows/B.yml@main
secrets: inherit # pass all secrets
# B→Cでの呼び出し(シークレット情報はB→Cでtokenのみ受け渡す)
jobs:
workflowB-calls-workflowC:
uses: different-org/example-repo/.github/workflows/C.yml@main
secrets:
repo-token: ${{ secrets.personal_access_token }} # pass just this secret
# ワークフローでの変数を受け渡す
name: Reusable workflow
on:
workflow_call:
# Map the workflow outputs to job outputs
# 事前にoutputを定義
outputs:
firstword:
description: "The first output string"
value: ${{ jobs.example_job.outputs.output1 }}
secondword:
description: "The second output string"
value: ${{ jobs.example_job.outputs.output2 }}
jobs:
example_job:
name: Generate output
runs-on: ubuntu-latest
# Map the job outputs to step outputs
outputs: # 実際にoutputの出力
output1: ${{ steps.step1.outputs.firstword }}
output2: ${{ steps.step2.outputs.secondword }}
steps:
- id: step1
run: echo "firstword=hello" >> $GITHUB_OUTPUT
- id: step2
run: echo "secondword=world" >> $GITHUB_OUTPUT
# 変数の受け渡しを行う場合、$GITHUB_OUTPUTに投げるのが公式のやり方なので投げている。
# ワークフローを呼び出し、その結果を受け取るjob
name: Call a reusable workflow and use its outputs
on:
workflow_dispatch: # 手動実行
jobs:
job1:
uses: octo-org/example-repo/.github/workflows/called-workflow.yml@v1
job2:
runs-on: ubuntu-latest
needs: job1
steps:
- run: echo ${{ needs.job1.outputs.firstword }} ${{ needs.job1.outputs.secondword }}
# $GITHUB_OUTPUTに入れたが、引き出す際は、こんな感じで引き出す。
ワークフローテンプレート
ひたすらにコードを見ていきます。
ただ、サンプルがめちゃ多いので、それぞれ気になるやつだけ見ます。
ワークフローテンプレート CI編
まずはコンテナから
name: Docker Image CI
on:
push:
branches: [ $default-branch ]
pull_request:
branches: [ $default-branch ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 事前作成されたテンプレの使用(use)
- name: Build the Docker image
run: docker build . --file Dockerfile --tag my-image-name:$(date +%s)
# docker imageのビルド
actions/chechout@v4って何?
公式の出しているワークフローテンプレートの一つになります。 実際に何をしているかについて、gptくんに聞いてみました。✅ actions/checkout@v4 の主な役割
GitHub Actions のワークフローは、GitHub が提供する仮想マシン(ランナー)上で実行されます。このランナーは、デフォルトでは空の作業ディレクトリから開始されます。actions/checkout@v4 を使用することで、リポジトリのコードをランナーの作業ディレクトリ($GITHUB_WORKSPACE)にクローンし、後続のステップでコードにアクセスできるようにします。
これにより、ビルド、テスト、デプロイなどの処理を行うためのコードベースがランナー上に準備されます。🔍 具体的な動作
actions/checkout@v4 を使用すると、以下のような操作が行われます:
- リポジトリのコードをクローンします。
- デフォルトでは、ワークフローをトリガーしたイベント(例:push や pull_request)に関連する特定のコミットがチェックアウトされます。
- クローンされたコードは、ランナーの作業ディレクトリ($GITHUB_WORKSPACE)に配置されます。
これにより、後続のステップでリポジトリのコードにアクセスし、必要な処理を実行できるようになります。
とのことです。なので、基本的には空のランナーにコード情報を入れ込むため、と思っておいてよさそうです。(何ならおまじないって感じでOKっぽそう)
公式の出しているusageでは、変数の説明もされていたので記載しておきます。
https://github.com/actions/checkout
- uses: actions/checkout@v4
with:
# Repository name with owner. For example, actions/checkout
# Default: ${{ github.repository }}
# 使うrepositoryの名前
repository: ''
# The branch, tag or SHA to checkout. When checking out the repository that
# triggered a workflow, this defaults to the reference or SHA for that event.
# Otherwise, uses the default branch.
ref: ''
# Personal access token (PAT) used to fetch the repository. The PAT is configured
# with the local git config, which enables your scripts to run authenticated git
# commands. The post-job step removes the PAT.
#
# We recommend using a service account with the least permissions necessary. Also
# when generating a new PAT, select the least scopes necessary.
#
# [Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
#
# Default: ${{ github.token }}
token: ''
# SSH key used to fetch the repository. The SSH key is configured with the local
# git config, which enables your scripts to run authenticated git commands. The
# post-job step removes the SSH key.
#
# We recommend using a service account with the least permissions necessary.
#
# [Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
ssh-key: ''
# Known hosts in addition to the user and global host key database. The public SSH
# keys for a host may be obtained using the utility `ssh-keyscan`. For example,
# `ssh-keyscan github.com`. The public key for github.com is always implicitly
# added.
ssh-known-hosts: ''
# Whether to perform strict host key checking. When true, adds the options
# `StrictHostKeyChecking=yes` and `CheckHostIP=no` to the SSH command line. Use
# the input `ssh-known-hosts` to configure additional hosts.
# Default: true
ssh-strict: ''
# The user to use when connecting to the remote SSH host. By default 'git' is
# used.
# Default: git
ssh-user: ''
# Whether to configure the token or SSH key with the local git config
# Default: true
persist-credentials: ''
# Relative path under $GITHUB_WORKSPACE to place the repository
path: ''
# Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching
# Default: true
clean: ''
# Partially clone against a given filter. Overrides sparse-checkout if set.
# Default: null
filter: ''
# Do a sparse checkout on given patterns. Each pattern should be separated with
# new lines.
# Default: null
sparse-checkout: ''
# Specifies whether to use cone-mode when doing a sparse checkout.
# Default: true
sparse-checkout-cone-mode: ''
# Number of commits to fetch. 0 indicates all history for all branches and tags.
# Default: 1
fetch-depth: ''
# Whether to fetch tags, even if fetch-depth > 0.
# Default: false
fetch-tags: ''
# Whether to show progress status output when fetching.
# Default: true
show-progress: ''
# Whether to download Git-LFS files
# Default: false
lfs: ''
# Whether to checkout submodules: `true` to checkout submodules or `recursive` to
# recursively checkout submodules.
#
# When the `ssh-key` input is not provided, SSH URLs beginning with
# `git@github.com:` are converted to HTTPS.
#
# Default: false
submodules: ''
# Add repository path as safe.directory for Git global config by running `git
# config --global --add safe.directory <path>`
# Default: true
set-safe-directory: ''
# The base URL for the GitHub instance that you are trying to clone from, will use
# environment defaults to fetch from the same instance that the workflow is
# running from unless specified. Example URLs are https://github.com or
# https://my-ghes-server.example.com
github-server-url: ''
次に、python関連です。
ここでは、pushされたコードに対して、ランナーにconda環境を整備し、そこでコード解析用のパッケージ(flake8, pytest)を実行しています。
name: Python Package using Conda
on: [push]
jobs:
build-linux:
runs-on: ubuntu-latest
strategy:
max-parallel: 5
# matrixでとりうる値すべてに対してジョブが生成されるが、同時実行数は5に制限している
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: '3.10'
- name: Add conda to system path
run: |
# $CONDA is an environment variable pointing to the root of the miniconda directory
echo $CONDA/bin >> $GITHUB_PATH
- name: Install dependencies
run: |
conda env update --file environment.yml --name base
- name: Lint with flake8 # pythonコードの静的解析ツール
run: |
conda install flake8
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# countでエラーや警告の総数を出力
# selectで、エラーコードを指定してチェック
# E9:runtime error
# F63, F7, F82:不明(ヒットせんかった)
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
# exit-zero:エラーあってもstatus codeを0にする
# max-complexity:コード複雑さの制限
# 機械的テストのむずかしさ?らしい
# max_line_lengthで行あたりの文字数指定
# statisticsで統計情報の表示
- name: Test with pytest # pythonのテストフレームワーク
run: |
conda install pytest
pytest
complexityの参考資料
今回はこれしか触れませんが、サンプルコードはマジでたくさんあるので、ぜひ見てみてください。(node.jsとかandroidとか)
ワークフローテンプレート デプロイ編
次にデプロイ編です。ここではawsとterraformを見ていきます。
# This workflow will build and push a new container image to Amazon ECR,
# and then will deploy a new task definition to Amazon ECS, when there is a push to the $default-branch branch.
#
# To use this workflow, you will need to complete the following set-up steps:
#
# 1. Create an ECR repository to store your images.
# For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`.
# Replace the value of the `ECR_REPOSITORY` environment variable in the workflow below with your repository's name.
# Replace the value of the `AWS_REGION` environment variable in the workflow below with your repository's region.
#
# 2. Create an ECS task definition, an ECS cluster, and an ECS service.
# For example, follow the Getting Started guide on the ECS console:
# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun
# Replace the value of the `ECS_SERVICE` environment variable in the workflow below with the name you set for the Amazon ECS service.
# Replace the value of the `ECS_CLUSTER` environment variable in the workflow below with the name you set for the cluster.
#
# 3. Store your ECS task definition as a JSON file in your repository.
# The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`.
# Replace the value of the `ECS_TASK_DEFINITION` environment variable in the workflow below with the path to the JSON file.
# Replace the value of the `CONTAINER_NAME` environment variable in the workflow below with the name of the container
# in the `containerDefinitions` section of the task definition.
#
# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
# See the documentation for each action used below for the recommended IAM policies for this IAM user,
# and best practices on handling the access key credentials.
name: Deploy to Amazon ECS
on:
push:
branches: [ $default-branch ]
env:
AWS_REGION: MY_AWS_REGION # set this to your preferred AWS region, e.g. us-west-1
ECR_REPOSITORY: MY_ECR_REPOSITORY # set this to your Amazon ECR repository name
ECS_SERVICE: MY_ECS_SERVICE # set this to your Amazon ECS service name
ECS_CLUSTER: MY_ECS_CLUSTER # set this to your Amazon ECS cluster name
ECS_TASK_DEFINITION: MY_ECS_TASK_DEFINITION # set this to the path to your Amazon ECS task definition
# file, e.g. .aws/task-definition.json
CONTAINER_NAME: MY_CONTAINER_NAME # set this to the name of the container in the
# containerDefinitions section of your task definition
permissions:
contents: read
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
# awsへの接続情報を登録
# aws-session-tokenもここで登録可能
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
# ECRへのログイン
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} # ログインしたレジストリー名の取得
IMAGE_TAG: ${{ github.sha }} # 一位性のたんぽ
run: |
# Build a docker container and
# push it to ECR so that it can
# be deployed to ECS.
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
# docker imageをビルドしてプッシュし、imageの場所:tagをoutputに流す
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ env.ECS_TASK_DEFINITION }}
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ steps.build-image.outputs.image }}
# 新しいイメージを使用するように更新
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
# ECSサービスのデプロイ
次に、terraformです。
# This workflow installs the latest version of Terraform CLI and configures the Terraform CLI configuration file
# with an API token for Terraform Cloud (app.terraform.io). On pull request events, this workflow will run
# `terraform init`, `terraform fmt`, and `terraform plan` (speculative plan via Terraform Cloud). On push events
# to the $default-branch branch, `terraform apply` will be executed.
#
# Documentation for `hashicorp/setup-terraform` is located here: https://github.com/hashicorp/setup-terraform
#
# To use this workflow, you will need to complete the following setup steps.
#
# 1. Create a `main.tf` file in the root of this repository with the `remote` backend and one or more resources defined.
# Example `main.tf`:
# # The configuration for the `remote` backend.
# terraform {
# backend "remote" {
# # The name of your Terraform Cloud organization.
# organization = "example-organization"
#
# # The name of the Terraform Cloud workspace to store Terraform state files in.
# workspaces {
# name = "example-workspace"
# }
# }
# }
#
# # An example resource that does nothing.
# resource "null_resource" "example" {
# triggers = {
# value = "A example resource that does nothing!"
# }
# }
#
#
# 2. Generate a Terraform Cloud user API token and store it as a GitHub secret (e.g. TF_API_TOKEN) on this repository.
# Documentation:
# - https://www.terraform.io/docs/cloud/users-teams-organizations/api-tokens.html
# - https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets
#
# 3. Reference the GitHub secret in step using the `hashicorp/setup-terraform` GitHub Action.
# Example:
# - name: Setup Terraform
# uses: hashicorp/setup-terraform@v1
# with:
# cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
name: 'Terraform'
on:
push:
branches: [ $default-branch ]
pull_request:
permissions:
contents: read
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
environment: production
# Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
defaults:
run:
shell: bash
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout
uses: actions/checkout@v4
# Install the latest version of Terraform CLI and configure the Terraform CLI configuration file with a Terraform Cloud user API token
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
# terraformのセットアップをやってくれる事前定義ワークフロー
# Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
- name: Terraform Init
run: terraform init
# Checks that all Terraform configuration files adhere to a canonical format
- name: Terraform Format
run: terraform fmt -check
# Generates an execution plan for Terraform
- name: Terraform Plan
run: terraform plan -input=false
# On push to $default-branch, build or change infrastructure according to Terraform configuration files
# Note: It is recommended to set up a required "strict" status check in your repository for "Terraform Cloud". See the documentation on "strict" required status checks for more information: https://help.github.com/en/github/administering-a-repository/types-of-required-status-checks
- name: Terraform Apply
if: github.ref == 'refs/heads/$default-branch' && github.event_name == 'push'
run: terraform apply -auto-approve -input=false
terraformのinstall、そこそこめんどくさいんですが、それを事前定義してくださっているの、ありがたすぎる。
hashicorp/setup-terraform@v1に感謝
ワークフローテンプレート 自動化編
このパートは毛色が少し変わります。
name: Greetings
on: [pull_request_target, issues]
jobs:
greeting:
runs-on: ubuntu-latest
permissions: # 書き込み権限
issues: write
pull-requests: write
steps:
- uses: actions/first-interaction@v1
with: # オプションで指定することで、ifでeventタイプを見なくても適切に反応させられる。
repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: "Message that will be displayed on users' first issue"
pr-message: "Message that will be displayed on users' first pull request"
# This workflow will triage pull requests and apply a label based on the
# paths that are modified in the pull request.
name: Labeler
on: [pull_request_target]
jobs:
label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
次は、手動でのワークフロー実行サンプルです。
# This is a basic workflow that is manually triggered
name: Manual workflow
# Controls when the action will run. Workflow runs when manually triggered using the UI
# or API.
on:
workflow_dispatch: # 手動ワークフローのための定義
# Inputs the workflow accepts.
inputs:
name:
# Friendly description to be shown in the UI instead of 'name'
description: 'Person to greet'
# Default value if no value is explicitly provided
default: 'World'
# Input has to be provided for the workflow to run
required: true
# The data type of the input
type: string
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "greet"
greet:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Runs a single command using the runners shell
- name: Send greeting
run: echo "Hello ${{ inputs.name }}"
次は、一定期間アクティビティがなかったissueとpull-requestにコメントをつけるワークフローです。
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/actions/stale
name: Mark stale issues and pull requests
on:
schedule:
- cron: $cron-daily
# cronでアクティビティのなかった機関の長さを指定できる
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Stale issue message'
stale-pr-message: 'Stale pull request message'
stale-issue-label: 'no-issue-activity'
stale-pr-label: 'no-pr-activity'
最後に、AIを使ったsummaryの作成です。
name: Summarize new issues
on:
issues:
types: [opened]
jobs:
summary:
runs-on: ubuntu-latest
permissions:
issues: write
models: read
contents: read
# コンテンツを読み出す権限を与えないとうまくいかないっぽい
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run AI inference
id: inference
uses: actions/ai-inference@v1
with:
prompt: |
Summarize the following GitHub issue in one paragraph:
Title: ${{ github.event.issue.title }}
Body: ${{ github.event.issue.body }}
- name: Comment with AI summary
run: |
gh issue comment $ISSUE_NUMBER --body '${{ steps.inference.outputs.response }}'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
RESPONSE: ${{ steps.inference.outputs.response }}
actions/ai-inferenceとは?
変数については、以下の通りです。
https://github.com/actions/ai-inference/blob/main/action.yml
name: 'AI Inference'
description: Generate an AI response based on a provided prompt
author: 'GitHub'
# Add your action's branding here. This will appear on the GitHub Marketplace.
branding:
icon: 'message-square'
color: red
# Define your inputs here.
inputs:
prompt:
description: The prompt for the model
required: false
default: ''
prompt-file:
description: Path to a file containing the prompt
required: false
default: ''
model:
description: The model to use
required: false
default: 'openai/gpt-4o'
endpoint:
description: The endpoint to use
required: false
default: 'https://models.github.ai/inference'
system-prompt:
description: The system prompt for the model
required: false
default: 'You are a helpful assistant'
max-tokens:
description: The maximum number of tokens to generate
required: false
default: '200'
token:
description: The token to use
required: false
default: ${{ github.token }}
# Define your outputs here.
outputs:
response:
description: The response from the model
response-file:
description: The file path where the response is saved
runs:
using: node20
main: dist/index.js
基本的に、なにも指定しなくてもOKらしい。
(アクセストークンすら必須ではないので、本当に恵まれている・・・)
参考
高度なワークフロー機能
ここからは、高度な機能についてのサンプルです。
まずは、トークンなどの直接書きたくないものを乗せる場合のサンプルです。
jobs:
example-job:
runs-on: ubuntu-latest
steps:
- name: Retrieve secret
env:
super_secret: ${{ secrets.SUPERSECRET }}
# secrets.変数名で取り出せる
run: |
example-command "$super_secret"
次に、ジョブの依存関係の明示についてです。
jobs:
setup:
runs-on: ubuntu-latest
steps:
- run: ./setup_server.sh
build:
needs: setup # setupが終わってから実行される
runs-on: ubuntu-latest
steps:
- run: ./build_server.sh
test:
needs: build #buildが終わってから実行される
runs-on: ubuntu-latest
steps:
- run: ./test_server.sh
# 依存関係のあるstepから変数を取り出すには、need.step_id.output.変数キーでOK
次に、マトリックスについてです。
これを設定することで、変数の組み合わせに基づく複数ジョブ実行が出来ます。
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node: [14, 16] # バージョンを14と16の二パターンで実行する。
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
次に、キャッシュの利用についてです。
パッケージインストールなどの、繰り返しやるけど作業時間はかかる、みたいなものをキャッシュしておくことで高速化するためのものです。なので、基本的にはパッケージ依存管理ツールと関係しているみたいです。
基本的には、cacheアクションは、指定したkey
に基づいてキャッシュ復元をするそうです。key
と完全一致でキャッシュヒット、完全一致がなかったらキャッシュミス扱いとなり、path
で指定されたファイルのキャッシュを作成するそうです。
サンプルは以下の通り
jobs:
example-job:
steps:
- name: Cache node modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
name: Caching with npm
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache node modules
id: cache-npm
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
# linuxにおけるパッケージ管理アプリのnpmのキャッシュファイルの保存場所を指定
# keyがヒットしたら、ここに依存関係のキャッシュが復元される
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
# キャッシュファイルを探索するkeyを指定
# OS依存性、環境依存性を考慮
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
# keyにヒットしなかった場合に検索するためのもの
- if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }}
# keyでcacheが見つからなかった場合の処理
# cache-hitで見つかったかのフラグをとれる
name: List the state of node modules
continue-on-error: true
run: npm list
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Test
run: npm test
いまいちわからないので、なじみ深いpython pipでのキャッシュ利用コードをgpt-4oに出してもらいました。
name: Restore pip cache
on:
push:
branches: [main]
jobs:
restore-pip-cache:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
# キャッシュの作成or読み出し
- name: Restore pip cache
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
# 依存関係に基づいたinstall
# ここで依存関係の情報が必要なので、cache読み出しは前のステップ
- name: Install dependencies
run: pip install -r requirements.txt
関連事項として、成果物のダウンロード/アップロードコードを見ていきます。
name: Share data between jobs
on: [push]
jobs:
job_1:
name: Add 3 and 7
runs-on: ubuntu-latest
steps:
- shell: bash
run: |
expr 3 + 7 > math-homework.txt
- name: Upload math result for job 1
uses: actions/upload-artifact@v4
with:
name: homework_pre # artifactの名前(なくても可)
path: math-homework.txt # 実際の保存場所
job_2:
name: Multiply by 9
needs: job_1
runs-on: windows-latest
steps:
- name: Download math result for job 1
uses: actions/download-artifact@v4
with:
name: homework_pre # 名前を指定すればこれで引っ張れる
- shell: bash
run: |
value=`cat math-homework.txt`
expr $value \* 9 > math-homework.txt
- name: Upload math result for job 2
uses: actions/upload-artifact@v4
with:
name: homework_final
path: math-homework.txt
最後に、コンテナ化されたサービスを利用するサンプルを見ます。
jobs:
container-job:
runs-on: ubuntu-latest
container: node:20-bookworm-slim # docker hubからのimageを引っ張る
services:
postgres: # サービス名
image: postgres # 使うイメージ名
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Connect to PostgreSQL
run: node client.js
env:
POSTGRES_HOST: postgres
POSTGRES_PORT: 5432
こんな感じで、docker-compose.yml
みたいなノリで使えるみたいです。
ドキュメントを読もうの会まとめ
読みました。かなり自由度が高いようなので、いろいろなことが出来るみたいです。
また、actionsで定義されたものを使いまくるようにすれば、初期コストもかからずでいい感じですね
おててを使って遊ぶ(はんずおん!)
ここまでで実際にgithub actionsのコードをいろいろとみてきたので、実際に実装してみようと思います。
どんなコード?
今回は、githubにコードをアップロードし、mainブランチへのプルリクエストを行った段階でterraform plan(terraformで記述したリソースの起動前確認)を行うコードをgpt4.1とともに作りました。
作成するリソースは、VPC、サブネット、s3、ec2です。
コード自体はgithubに上げました。(が、エラーが出てまだ治っていないです。)
name: 'Terraform'
on:
pull_request:
branches:
- main
permissions:
contents: read
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
# Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
defaults:
run:
shell: bash
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
aws-region: ap-northeast-1
# awsへの接続情報を登録
# aws-session-tokenもここで登録可能
# Terraformのセットアップ(共通処理っぽい)
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.5.7 # ここは必要なバージョンに合わせて!
# Terraform init
- name: Terraform Init
run: terraform init
working-directory: ./environment/develop/
# Generates an execution plan for Terraform
- name: Terraform Plan
run: terraform plan -input=false
working-directory: ./environment/develop/
これらのコードによって、githubactionsを用いて、pull requestをトリガーとしてterraform plan
を実行させることに成功しました。
やったね!!!
まとめ
今回は、github actionsのコードをひたすら読んで、terraformのコードを実行させるということをしてみました。
構文的な面で慣れないというより、エラーが起きるまでの時間が長いことの方が個人的にはストレスでした。
なので、次からはもう少しエラーが起きないことを事前にチェックする部分に力を入れたいです。
(あとシンプルに、terraformでアプライできる状況になっていないので、それを直したい。)