Github Actionsによる継続的デリバリーの構築

CTO室所属の高橋と申します。皆からはニャンさんと呼ばれております。そして私は特に無茶振りはされていません。

さて、developブランチに変更が入ったらdev環境にリリース、mainブランチに変更が入ったら本番環境にリリースを行う仕組み、いわゆる継続的デリバリーを構築しているプロジェクトは多いことと思う。我々のチームではCircle CIで行なっていたが事情があってGithub Actionsに移行することになった。ところがGithub Actionsではdevリリースと本番リリースの手順をどう共通化すればいいのか分からず迷ってしまった。

近年のソフトウェア開発の現場ではCI/CDは当たり前になっているが、もはや当たり前すぎて逆にまとまった情報がネット上にないのかもしれない。であれば得られた知見は共有しておくべきだろう。

結論から言うとジョブの共通化には再利用可能なワークフロー用いるのが良さそうだ。

Github Actionsでやりたいことは以下の3点。

  • featureブランチの変更時にテストを実行する
  • developブランチの変更時にテストを実行し、dev環境にデプロイ
  • mainブランチの変更時にテストを実行し、本番環境にデプロイ

.github/workflowsに以下5つののyamlファイルを用意した。

  • test.yml: featureブランチの変更時、テストを実行
  • release-dev.yml: devlopブランチの変更時、テストとdev環境へのデプロイを実行
  • release-prd.yml:mainブランチの変更時、テストと本番環境へのデプロイを実行
  • reusable-test.yml: テスト手順を記述した再利用可能なワークフロー
  • reusable-release.yml: リリース手順を記述した再利用可能なワークフロー

以下、各ファイルの詳細を見ていこう

再利用可能なワークフロー

再利用可能なワークフローのファイル名には reusable-shared-common-など、それとわかる接頭辞をつけておくといいかもしれない。

reusable-test.yml

lintとtestのジョブ手順を定義する。onにworklflow_callを設定すると再利用可能なワークフローとなる

name: test
on:
  workflow_call:

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm run test  

reusable-deploy.yml

リリースジョブの手順を定義する。このプロジェクトではmakeで以下のタスクを実行してdockerイメージの作成とCloud runへのデプロイを行なっている。

  1. docker build
  2. docker push(artifact registryにpush)
  3. gcloud run deploy

以下のコマンドでdevリリース、本番リリースを実行している

make deploy TARGET=dev
make deploy TARGET=prd

見ての通りTARGETによってリリース先の環境を切り替えているのだが、Github Actionsではinputsという記述で再利用可能なワークフローの呼び出し元で指定可能な変数にできる。これが共通化のポイントその1。

もう一つのポイントはGoogle Cloudのサービスアカウントキーの切り替え方法。サービスアカウントキーは機密情報なのでsecretsに登録してあり、dev用と本番用で鍵を切り替えなければならない。githubのsecretsは環境という名前空間のような機能を持っており、ワークフローのenvironmentという記述でどの環境を使用するか指定できる。ここではTARGETと同じ名前で環境(devとprd)を作成し、それぞれの環境にGCP_CREDENTIALSという名前でサービスアカウントキーを登録する。そしてinputsをenvironmentに引き渡してsecretsを実行時に切り替える方法を採った。

name: deploy

on:
  workflow_call:
    inputs:
      target:
        required: true
        type: string
        description: 'リリースターゲット dev or prd'

jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    environment:
      name: ${{ inputs.target }}
    steps:
      - uses: actions/checkout@v4
      - uses: google-github-actions/auth@v2
        with:
          credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
      - uses: google-github-actions/setup-gcloud@v2

      - name: Configure Docker
        run: gcloud auth configure-docker asia-northeast1-docker.pkg.dev --quiet

      - name: Run make deploy
        run: |
          make deploy TARGET=${{ inputs.target }}  

pushで実行するワークフロー

jobsは再利用可能なワークフローを呼び出すだけ。実行の条件指定(on)の記述が主な内容となる。

test.yml

テスト実行、mainブランチとdevelopブランチはリリースジョブ内で行うので除外する。

name: Lint and Test code

on:
  push:
    branches-ignore:
      - main
      - develop

jobs:
  test:
    uses: ./.github/workflows/reusable-test.yml

release-dev.yml

devリリース、ここでのポイントは

  • secrets: inherit、secretsを再利用可能なワークフローに引き渡す設定。
  • workflow_dispatch、actionsのweb画面上から手動で実行できるようにする設定。任意のブランチをdev環境にリリースすることができる
name: Release to the development environment

on:
  push:
    branches:
      - develop
  workflow_dispatch:

jobs:
  test:
    uses: ./.github/workflows/resuable-test.yml
  
  deploy:
    needs: [test]
    uses: ./.github/workflows/reusable-deploy.yml
    with:
      target: dev
    secrets: inherit

release-prd.yml

本番リリース、devとはブランチ指定とtargetが違うだけ。本番は手動リリースしないのでworkflow_dispatchはなし

name: Release to the production environment

on:
  push:
    branches:
      - main

jobs:
  test:
    uses: ./.github/workflows/resuable-test.yml
  
  deploy:
    needs: [test]
    uses: ./.github/workflows/reusable-deploy.yml
    with:
      target: prd
    secrets: inherit

重複する記述を完全に削除できるわけではないが、見通しのよいyamlになったのではないかと思う。