CodePipelineで開発ブランチを任意のステージング環境へデプロイする

# やりたいこと・前提条件
* アプリケーションのソースコードをGitHubで管理している
* ステージング環境用のアプリケーションがECSで稼働している
* DockerイメージのビルドやECSへのデプロイはCodePipelineを使っている
* 開発ブランチを指定し、好きなタイミングで任意のステージング環境へデプロイしたい

# 課題
開発フェーズではステージング環境へデプロイし、レビューやテストを実施することが一般的です。
CodePipelineではGitHubと連携してソースコードを取得できますが、リポジトリとブランチを事前に指定しておく必要があるため、開発中のブランチを動的に指定できないという制約があります。
また、私の所属する開発チームでは業務委託契約で参画しているメンバーもいるため、その方たちにはAWSアカウントを付与していません。
CodePipelineの設定をその都度変更する方法を取った場合、AWSアカウントを持っている社員が代行しなければならず、手間がかかって不便です。

# 解決策
今回ご紹介する解決策は、GitHub Actions を使って開発ブランチを指定し、任意のタイミングで好きなステージング環境へデプロイする方法です。
この方法なら、AWSアカウントを持っていない人でも、GitHub Actionsを実行する権限さえあれば誰でも簡単な操作でデプロイできます。

## デプロイ用ブランチの作成
GitHubにあらかじめデプロイ用のブランチを作成しておきます。
例えば「staging/deploy_st」です。私の所属する開発チームではステージング環境が複数あるため、ステージング環境分のデプロイ用ブランチを予め作っておきます。
## CodePipelineの作成
次にステージング環境毎にCodePipelineを作成しておきます。このとき、Sourceステージで指定するブランチは事前に作成しておいたデプロイ用ブランチです。（例えばstaging/deploy_st）
今回はCodePipelineの作成手順については割愛させていただきます。

## GitHubActionsの作成
大まかな流れとしては以下の通りです。
1. デプロイしたいブランチを指定する
1. デプロイ対象の環境を指定する
1. GitHub Actionsのワークフローを実行する
1. デプロイ先に対応してデプロイ用ブランチをチェックインする
1. デプロイ用ブランチをmainブランチの最新状態にリセットしてプッシュする
1. 指定したデプロイしたいブランチの内容をデプロイ用ブランチに上書きし、コミット＆プッシュする
1. デプロイ対象のCodePipelineをトリガーする

#### こちらがymlファイルの内容です。(一部内容をマスクしています。)
```
name: Manual Deploy to Staging

on:
  workflow_dispatch:
    inputs:
      environment:
        description: "デプロイ先ステージング環境"
        type: environment

jobs:
  update-branch-and-build:
    runs-on: ubuntu-latest
    environment: ${{ github.event.inputs.environment }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
        with:
          fetch-depth: 0

      - name: Set git user
        run: |
          git config --local user.email "******-deploy-staging@******.com"
          git config --local user.name ""******-deploy-staging"

      - name: Replace branch contents
        run: |
          mkdir -p /tmp/feature_branch_contents
          rsync -a --exclude='.git' $GITHUB_WORKSPACE/. /tmp/feature_branch_contents

          git checkout ${{ vars.STAGING_DEPLOY_BRANCH }}
          git reset --hard origin/main
          git push -f origin ${{ vars.STAGING_DEPLOY_BRANCH }}:${{ vars.STAGING_DEPLOY_BRANCH }}
          git rm -r .

          cp -a /tmp/feature_branch_contents/. .

          git add .
          git diff --cached --exit-code || git commit -m "Replace staging deploy branch: ${GITHUB_REF_NAME}"
          git push origin ${{ vars.STAGING_DEPLOY_BRANCH }}:${{ vars.STAGING_DEPLOY_BRANCH }}

      - name: Trigger AWS CodePipeline
        uses: zulhfreelancer/aws-codepipeline-action@v1.0.7
        with:
          aws-region: ap-northeast-1
          aws-access-key: ${{ secrets.AWS_PIPELINE_ACCESS_KEY }}
          aws-secret-key: ${{ secrets.AWS_PIPELINE_SECRET_KEY }}
          pipeline-name: ${{ secrets.PIPELINE_NAME }}
```

### デプロイするブランチとデプロイ対象の環境の入力

```
on:
  workflow_dispatch:
    inputs:
      environment:
        description: "デプロイ先ステージング環境"
        type: environment
```

上記の記述だけでプルダウンによるデプロイ先ステージング環境の選択が可能になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2503161/4e0ff726-b70d-4320-b37b-f56cc2d136e0.png)

### 環境変数の定義
デプロイ先のステージング環境を環境変数として定義しておきます。
この変数名が先ほどのプルダウンの選択肢と一致するという仕組みになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2503161/4314a589-4ac3-4cb8-a602-9633e4bdb55b.png)

定義した環境変数に対して、パイプライン名（PIPELINE_NAME ）をEnvironment secretsに、デプロイ用ブランチ名（STAGING_DEPLOY_BRANCH）をEnvironment variablesに設定ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2503161/2b236778-44a2-4d92-9914-4adcab1d73d2.png)

そうすることでYAMLファイル内では以下のようにアクセスできます。
ここで参照される各値はデプロイ先のステージング環境に応じた動的な値となるため、環境が増えてもメンテナンスが容易です。
`${{ secrets.PIPELINE_NAME }}`
`${{ vars.STAGING_DEPLOY_BRANCH }}`

AWS_PIPELINE_ACCESS_KEYとAWS_PIPELINE_SECRET_KEYについてはデプロイ先のステージング環境に関係なく共通なので、Repository secretsとして設定できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2503161/4f998ef7-a98d-43b3-b2cc-996797ecf266.png)

# おわりに
最後までご覧いただきありがとうございます。
CodePipelineを初めて触り、「動的なブランチの切り替え方法をどうするか？」と調べ始めたところ、CodePipelineでは直接ブランチを動的に指定できないとわかり、正直驚きました。
AWSは日々進化しているため、将来的には CodePipelineをトリガーする際にブランチを指定できるようになるかもしれません。
それまでは、今回ご紹介した方法がお役に立てれば幸いです。

## KIYOラーニング株式会社について
当社のビジョンは『世界一「学びやすく、分かりやすく、続けやすい」学習手段を提供する』ことです。革新的な教育サービスを作り成長させていく事で、オンライン教育分野でナンバーワンの存在となり、世界に展開していくことを目指しています。

https://kiyo-learning.com/

### プロダクト

* [スタディング](https://studying.jp/)：「学びやすく・わかりやすく・続けやすい」オンライン資格対策講座
* [スタディングキャリア](https://career.studying.jp/)：資格取得者の仕事探しやキャリア形成を支援する転職サービス
* [AirCourse](https://aircourse.com/)：受け放題の動画研修がついたeラーニングシステム（LMS）

### KIYOラーニング株式会社では一緒に働く仲間を募集しています

https://www.wantedly.com/companies/kiyo-learning/projects

https://herp.careers/v1/kiyolearning