AWS Copilotのpipelineで複数ブランチを扱う

この記事は **[リンクバルアドベントカレンダー2023](https://qiita.com/advent-calendar/2023/linkbal)** の16日目の記事です。

昨日は @quanganhk62 さんの記事です！

https://qiita.com/quanganhk62/items/0bdbe62fc4a77cb6ad5c


## はじめに
[AWS Copilot](https://aws.github.io/copilot-cli/ja/)の[Pipeline](https://aws.github.io/copilot-cli/ja/docs/concepts/pipelines/)はすぐにデプロイパイプラインが作れて便利ですが、一つのブランチしか使えないCodePipelineの制約に私は困っていました。

対象のサービスの運用が、stg環境などでテストするときに各ブランチ単位でデプロイしています。
この運用とブランチ一つしか使えないCodePipelineの相性はすこぶる悪いです。
悪いというか、AWS CopilotのPipelineの思想と合ってないと思っています。

合ってないですが、この問題を割とかんたんに解決できたので紹介します。


## 結論
GitHubやGitLabのCIで、CodePipelineのブランチを変えます。

具体的には、CIで以下を行います。

1. Pipelineの `manifest.yml` の `source.properties.branch` をデプロイ対象ブランチに書き換える 
2. `copilot pipeline deploy -n xxx --yes` でpipelineを更新します。

コードを交えつつ説明します。

### Copilot Pipeline の `manifest.yml`
まず、pipelineのmanifest.ymlを次のようにします。
この例は、GitLabを（しぶしぶ）CodeCommitにミラーリングしているとします。
stg環境用の設定です。

ポイントは以下の2点です。

- `source.properties.branch`
- `stages` には一つのenvだけ指定する


```yaml:copilot/pipelines/stg-deploy/manifest.yml
name: stg-deploy

version: 1

source:
  provider: CodeCommit # GitLabは v1.32.1 時点で未対応なので、CodeCommitにミラーリングする
  properties:
    branch: __target-branch__ # この文字列をCIで書き換える
    repository: https://ap-northeast-1.console.aws.amazon.com/codesuite/codecommit/repositories/xxx/browse # 各自の値を設定してください

build:
  # ARM版のイメージを指定しています
  # お好みに変えてください
  # CodeBuildのイメージ一覧 https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-env-ref-available.html
  image: aws/codebuild/amazonlinux2-aarch64-standard:3.0

stages:
  - name: stg # あえて一つの環境しか指定しない
```

そして、pipelineを事前に作成しておきます。

`copilot pipeline init -n stg-deploy -u 'https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/xxx' -p Workloads`

### GitLabの `.gitlab-ci.yml`
dev環境とstg環境があるとした場合のサンプルコードです。
本番は、mainブランチなどに固定しているでしょうから、その場合はCIでの操作は不要です。

あとは、GitLabでMRを作ってこのジョブを動かせば、Copilotが作成したCodePipelineのCodeCommitの対象ブランチを変わります！

```yaml:.gitlab-ci.yml
# stagesなどは省略
# ファイルを分けて include するとよいと思います

# 共通の処理を作っておく
.copilot_deploy:
  image: alpine:latest # 軽量イメージならなんでもよい
  variables:
    AWS_REGION: ap-northeast-1
  before_script:
    - apk add --update-cache --no-cache curl
    # copilotコマンドをインストール
    - curl -LsSo /usr/local/bin/copilot https://github.com/aws/copilot-cli/releases/latest/download/copilot-linux && chmod +x /usr/local/bin/copilot
    - echo ${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME}
  script:
    # sedで `source.properties.branch` の値をMRのブランチに書き換える
    - sed -i "s/__target-branch__/${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME}/g" copilot/pipelines/${DEPLOY_TARGET_ENV}-deploy/manifest.yml
    # 変わっているか確認
    - grep ${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} copilot/pipelines/${DEPLOY_TARGET_ENV}-deploy/manifest.yml
    # Pipelineの更新
    - copilot pipeline deploy -n ${DEPLOY_TARGET_ENV}-deploy --yes
  # お好みで
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      when: manual

deploy-dev:
  extends: .copilot_deploy
  stage: deploy
  variables:
    # さぼってIAMユーザーを使ってしまっていますが、できるだけIAMロールを使ってください
    AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID_DEV
    AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY_DEV
    DEPLOY_TARGET_ENV: dev

deploy-stg:
  extends: .copilot_deploy
  stage: deploy
  variables:
    AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID_STAGING
    AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY_STAGING
    DEPLOY_TARGET_ENV: stg
```

## 注意点
CodePipelineの数が増えるので、2023年12月23日時点では以下の料金が追加でかかります。

> V1 タイプのパイプラインの場合: アクティブなパイプライン(30 日以上存在していて、その月に少なくとも 1 つのコード変更が発生したパイプライン) ごとに1か月あたり 1.00 USD を支払います。その月に新しいコード変更がないパイプラインに対しては、料金は発生しません。アクティブなパイプラインは、1 か月に満たない分に対して按分計算されません。パイプラインは、作成後の最初の 30 日間は無料です。

> V2 タイプのパイプラインの場合: アクション実行時間 1 分あたり 0.002 USD を支払います。アクション実行時間は、パイプライン内のアクションが実行開始してからそのアクションが完了状態に達するまで分単位で計算され、最も近い分単位に切り上げられます。手動承認およびカスタムアクションタイプを除くすべてのアクションタイプに対して課金されます。   

> V1 タイプのパイプラインの場合: 1 か月あたり 1 つの無料のアクティブなパイプライン。
> V2 タイプのパイプラインの場合: 1 か月あたり 100 分の無料のアクション実行。無料のアクション実行時間は、アカウント内の V2 パイプラインの数に関係なく、アカウント内のすべての V2 パイプライン間で共有されます。これらは各暦月の初めに自動的にリセットされます。未使用の分が翌月に繰り越されることはありません。

https://aws.amazon.com/jp/codepipeline/pricing/

## おわりに
おわり