初めてGitHub Actionsのワークフローを作成してみた

ビルドやテスト、デプロイなどを自動化するために使用するGitHub Actions。
今まで「既に作成されているもの」を使ったことしかなかったが、今回初めて自分で作成する機会があった。
CI/CDのプロセスを簡単に構築できるツールであるGitHub Actionsは使用する機会も多いと思うので、ワークフローの定義方法などの概要を理解しておきたいと思い、記事を作成してみた。

## 今回やった内容
以下を実現するためのワークフローを作成した。

- GitHub Actionsから本番/ステージング環境にそれぞれデプロイできるようにする
- 手動トリガーで実行する
- デプロイ先はワークフロー実行時に選択する

## ワークフローの追加方法
`.github/workflows` ディレクトリを作成し、その中にワークフローの処理を定義したymlファイルを配置する。
Actionsタブに追加したワークフローを表示するには、デフォルトブランチにマージする必要がある。

## 作成したもの(内容を一部抜粋)
```yaml: .github/workflows/deploy.yml
name: Deploy to AWS Lambda

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'デプロイ先: dev | prod'
        default: 'dev'
        required: true
        type: choice
        options:
          - dev
          - prod

jobs:
  build_and_deploy:
    runs-on: ubuntu-20.04

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'

      - name: Install dependencies
        run: yarn install

      - name: Build the project
        run: yarn build

      - name: Set up Serverless Framework
        run: yarn global add serverless@3

      - name: Set the value
        run: |
          if [ "${{ github.event.inputs.environment }}" == "prod" ]; then
            echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID_PROD }}" >> $GITHUB_ENV
            echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY_PROD }}" >> $GITHUB_ENV
            echo "ENV=production" >> $GITHUB_ENV
          else
            echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID_STG }}" >> $GITHUB_ENV
            echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY_STG }}" >> $GITHUB_ENV
            echo "ENV=development" >> $GITHUB_ENV
          fi

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Deploy with Serverless
        env:
          SERVERLESS_LICENSE_KEY: ${{ secrets.SERVERLESS_LICENSE_KEY }}
        run: |
          serverless deploy --stage ${{ github.event.inputs.environment }} --region ap-northeast-1 --force --verbose \
            --param "ENV=${{ env.ENV }}"

      - name: Deployment complete
        run: |
          echo "Deployed branch: ${{ github.ref }}"
```

### workflow_dispatch
手動実行するワークフローの場合は、 `workflow_dispatch` を指定する。
その他にも、リモートリポジトリへのpushをトリガーにする `push` や、プルリクの作成をトリガーにする `pull_request` などをよく使うイメージ。(ユニットテストとか)

### inputs
ワークフロー実行時の入力パラメータ。今回は、デプロイ先の環境を指定する `environment` というパラメータを定義し、実行時に「devまたはprod」を選択できるようにした。
処理の中で使用するときには `github.event.inputs.environment` と書く。

### secrets.キー名
secretにはAWSのシークレットキーやアクセスキーなど、他の人には見られたくない情報を設定した。

### $GITHUB_ENV
デプロイ先(本番/ステージング)によって使用するAWSアクセスキーなどが異なるため、選択した `environment` によって使用するsecretを切り替えている。
後続の処理は共通した実装にしたいので、`$GITHUB_ENV` という環境変数に設定しておき、 `env.AWS_ACCESS_KEY_ID` で利用できるようにしている。

```
if [ "${{ github.event.inputs.environment }}" == "prod" ]; then
  echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID_PROD }}" >> $GITHUB_ENV
  echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY_PROD }}" >> $GITHUB_ENV
  echo "ENV=production" >> $GITHUB_ENV
else
  echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID_STG }}" >> $GITHUB_ENV
  echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY_STG }}" >> $GITHUB_ENV
  echo "ENV=development" >> $GITHUB_ENV
fi
```

## その他覚えておきたいこと
### 直列にjobを実行する
jobを複数記載した場合、基本的には並行に実行される。直列で実行したい場合は、 `needs` を使った定義をすることで、特定のjobが終了してから開始することができる。
以下の例の場合は、「 `build` → `deploy`」の順に直列でjobが実行される。

```yaml:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Build complete
        run: |
          echo "Build branch: ${{ github.ref }}"

  deploy:
    needs: [build]
    runs-on: ubuntu-latest
    steps:
      - name: Deploy complete
        run: |
          echo "Deployed branch: ${{ github.ref }}"
```

## 所感
これまでは「とりあえず使っている」というだけの状態だったので、まずは内容や構造の理解ができてよかった。
こういう「画面から実行する」類のものは、「いかに簡単に、いかに実行時の間違いを減らせるか」といったことが重要だと改めて思った(パラメータが増えすぎないようにする、入力間違いを減らすためセレクトボックスにする、など)。
今回はデプロイの部分だけだったが、ユニットテストなどのワークフローも自分で定義を作成してみたい。