【github actions】共通するワークフローにはreusable workflowを使おう

# 概要
普段CI/CD何使ってますか？
私はgithub actionsを使うことが多いのですが、その機能の中でreusable workflowという読んで字の如く、ワークフローを再利用できるものがあります。

よく使っているシーンとしては、開発環境と本番環境へそれぞれデプロイするのに、ワークフローは同じだけど環境変数が違うような場合です。
共通の部分(ワークフローの骨組み)だけをテンプレート化しておいて、必要な部分(環境変数)は呼び出し元で与える形になります。
ちょうどプログラムの関数と引数のようなイメージですね。

https://docs.github.com/ja/actions/using-workflows/reusing-workflows


# 使い方の例
`.env`の内容が開発環境と本番環境で違うのはよくあることだと思います。
開発環境用と本番環境用に次のようにenvファイルを用意します。
秘匿情報である`DB_PASSWORD`の値はgithub actionsのRepository secretsで指定するため、ファイル内には記載していません。

```.env.dev
# 開発環境用
DB_MS=mysql
DB_HOST=192.168.10.11
DB_NAME=test_dev
DB_USER=dev
DB_PASSWORD=
```
```.env.prd
# 本番環境用
DB_MS=mysql
DB_HOST=192.168.10.11
DB_NAME=test_prd
DB_USER=prd
DB_PASSWORD=
```

開発環境にデプロイする時は`.env.dev`を、
本番環境にデプロイする時は`.env.prd`を使います。

## 呼び出される側
で、早速テンプレート(呼び出される側)です。

```yaml:.github/workflows/_cd_template.yaml
name: Deploy Template

on:
  workflow_call:
    inputs:
      ENV_FILE:
        type: string
        required: true
    secrets:
      DB_PASSWORD:
        required: true

jobs:
  test-job:
    runs-on: ubuntu-latest

    steps:
      - name: Setup .env
        run: |
          cp ./${{ inputs.ENV_FILE }} ./.env
          sed -i -e 's/DB_PASSWORD=.*/DB_PASSWORD=${{ secrets.DB_PASSWORD }}' ./.env
```

まぁデプロイテンプレートと書きつつ、デプロイに関する内容は一切書いてないんですが、大事なのは再利用できるワークフローなのでそこは目を瞑ってもらって。
やっている内容は単純で、呼び出し元から指定されたenvファイルを`.env`という名前でコピーして、
`.env`内の`DB_PASSWORD=`を、呼び出し元から指定されたsecretsの値が含まれた形に置換します。

最初のこの部分が関数でいうところの言わば仮引数にあたります。
ワークフローの呼び出し元から、これらの値が渡されて`jobs`の中で使用されます。

```yaml
on:
  workflow_call:
    inputs:
      ENV_FILE:
        type: string
        required: true
    secrets:
      DB_PASSWORD:
        required: true
```


## 呼び出す側
呼び出す側は開発環境と本番環境で2つあります。
```yaml:.github/workflows/CD_dev.yaml
name: Dev Deploy

on:
  push:
    branches:
      - dev

jobs:
  CD:
    uses: ./.github/workflows/_cd_template.yaml
    with:
      ENV_FILE: .env.dev
    secrets:
      DB_PASSWORD: ${{ $secrets.DB_PASSWORD_DEV }}
```
```yaml:.github/workflows/CD_prd.yaml
name: Prd Deploy

on:
  push:
    branches:
      - main

jobs:
  CD:
    uses: ./.github/workflows/_cd_template.yaml
    with:
      ENV_FILE: .env.prd
    secrets:
      DB_PASSWORD: ${{ $secrets.DB_PASSWORD_PRD }}
```

両者の内容は実行するブランチとテンプレートに渡す値が違うだけです。
下記の部分が関数でいうところの言わば実引数にあたります。
```yaml
    with:
      ENV_FILE: .env.dev
    secrets:
      DB_PASSWORD: ${{ $secrets.DB_PASSWORD_DEV }}
```

ここで`DB_PASSWORD`は[github actionsのsecrets](https://docs.github.com/ja/actions/security-guides/using-secrets-in-github-actions)から読み込んだ値になるため、今回の場合はRepository secretsに`DB_PASSWORD_DEV`と`DB_PASSWORD_PRD`を設定しておけば、それらを読み込んで、テンプレート上では同名の`DB_PASSWORD`という変数名で統一できます。

# まとめ
ワークフローに追加や削除などの修正をしたい時、テンプレートにまとめておくことで、そのファイルのみの修正で済みます。

またAWSやGCPにデプロイするとなると、途端に環境別の変数が増えます。
CloudRunへのデプロイで言えば、プロジェクト名、リージョン、サービスアカウント、dockerイメージ名、CloudRun名などなど。
呼び出し元では「テンプレートを呼び出す」「引数を渡す」だけにしておけばそれらの見通しも良くなります。

## 補足
今回はRepository secrets前提で`DB_PASSWORD_DEV`と`DB_PASSWORD_PRD`を設定するような形にしていますが、[Environment secrets](https://docs.github.com/ja/actions/deployment/targeting-different-environments/using-environments-for-deployment#environment-secrets)を使うことで、命名を分けずとも`DB_PASSWORD`という同名にすることもできます([サンプル](https://docs.github.com/ja/actions/using-workflows/reusing-workflows#using-inputs-and-secrets-in-a-reusable-workflow))。

またCIもテンプレート化しておいて、CD前に必ずCIを実行するというようなCI/CDパイプラインも見通しが良くなります。
```yaml
jobs:
  CI:
    uses: ./.github/workflows/_ci_template.yaml
  CD:
    needs: [CI]
    uses: ./.github/workflows/_cd_template.yaml
    with:
      ENV_FILE: .env.prd
    secrets:
      DB_PASSWORD: ${{ $secrets.DB_PASSWORD }}
```

お試しあれ。