【CodeCommit + CodeBuild + CodePipeline】ビルド成果物を別のgitリポジトリに自動でコミットする

## 今回作成したもの

#### 「CodeCommit + CodeBuild + CodePipeline」を使ったビルド成果物の自動生成とバージョン管理の仕組み

1. CodeCommit のビルド対象リポジトリにタグがプッシュされると、CodePipeline の実行が開始される。
2. CodePipeline のソースステージで、CodeCommit からコードが取得される。
3. CodePipeline のビルドステージで、CodeBuild によってプロジェクトのビルドが行われ、ビルド成果物が静的ファイルとして出力される。
4. 出力されたファイルが、CodeCommit のビルド成果物バージョン管理用リポジトリにプッシュされる。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/31986c4c-4afb-65b9-d906-fa8d422fd6b3.png)
::: note
今回は、**Next.js** のプロジェクトに対して上記の仕組みを作成したため、それに沿って説明していく。
:::


## 使用したサービス
* [AWS CodeCommit](https://aws.amazon.com/jp/codecommit/)
* [AWS CodeBuild](https://aws.amazon.com/jp/codebuild/)
* [AWS CodePipeline](https://aws.amazon.com/jp/codepipeline/)
* [Amazon EventBridge](https://aws.amazon.com/jp/eventbridge/)
* [AWS Systems Manager](https://aws.amazon.com/jp/systems-manager/)


## 前提
* AWS アカウントがあること。
* CodeCommit にビルド対象となる Next.js のプロジェクトが存在していること。
* ビルド対象リポジトリの`package.json`の`scripts`に以下の内容が設定されていること（`npm run export-production`を実行すると、ビルドが行われ、ビルド成果物が静的HTMLとして出力される状態であること）。
  ※ [`next-export-optimize-images`](https://next-export-optimize-images.vercel.app/)を使用していない場合は、`&& next-export-optimize-images`は必要はない。
    ```json
    "export-production": "next build && next export && next-export-optimize-images"
    ```
* [ AWS CodeCommit のセットアップ ](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/setting-up.html)を完了していること。


## 手順

### 1. CodeCommit でビルド対象のリポジトリのクローンURLをコピーする。

1.  [「GitLab」から「AWS CodeCommit」への移行方法 ](https://qiita.com/A_N_0052/items/e878f13469bfc858a13a#1-codecommit%E3%81%A7%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B:~:text=%E3%80%8CURL%E3%81%AE%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%B3%E3%80%8D%E3%81%8B%E3%82%89%E3%80%8CHTTPS%E3%81%AE%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%B3%E3%80%8D%E3%82%92%E9%81%B8%E6%8A%9E%E3%81%97%E3%80%81%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%B3%E7%94%A8%E3%81%AEURL%E3%82%92%E3%82%B3%E3%83%94%E3%83%BC%E3%81%99%E3%82%8B%E3%80%82)の手順1.5などを参考に、ビルド対象リポジトリのクローン用URLをコピーする。
   後の手順で使うため、コピーしたURLはメモしておく。


### 2. CodeCommit でビルド成果物バージョン管理用のリポジトリを作成する

1. [「GitLab」から「AWS CodeCommit」への移行方法 ](https://qiita.com/A_N_0052/items/e878f13469bfc858a13a#1-codecommit%E3%81%A7%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)の手順1.1～1.5などを参考に、ビルド成果物バージョン管理用のリポジトリを作成し、クローン用のURLをコピーする。
   後の手順で使うため、コピーしたURLはメモしておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/1a023768-e5c8-6504-217a-0ed3efe4486c.png)

### 3. 2.1で作成したリポジトリの初回コミットを行い、ビルド成果物出力用のリモートブランチを作成する

* この対応を行う目的は、ビルド成果物を出力するブランチの名前を明確にし、ビルド成果物をプッシュする際の「Error: src refspec ${ ブランチ名 } does not match any 」エラーを防ぐこと。

1. 2.1のリポジトリ作成後の画面右下にある「**ファイルの追加 > ファイルの作成**」を選択する。
![バージョン管理リポジトリ-ファイル追加.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/cf6b01dc-2017-b0cc-f4bd-39d581b9acfa.png)

2. 「**作成者名**」と「**Eメールアドレス**」、任意の「**ファイル名**」、必要に応じて「**メッセージのコミット — オプショナル**」欄にコミットコメントを記入した上で、「**変更のコミット**」をクリックする。
![sample2-初回コミット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/4220aa51-443b-bc68-ad5e-a61022e1c889.png)

3. 初回コミットにより、ファイルが追加され、CodeCommit のデフォルトブランチである「**main**」ブランチが作成された。
![sample2-ブランチ作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/52dca198-8072-ee6d-e114-b525bd4dcc7d.png)

::: note
初回コミットが行われ、リモートブランチが作成されれば良いため、以下の対応を行うのでも問題ない。
* ローカルにクローンしたリポジトリに対して`git commit --allow-empty -m "[ コミットコメント ]"`を実行し、空コミットを作成した上でプッシュする。
* ローカルにクローンしたリポジトリで何かしらのファイルを作成し、コミット&プッシュする。

ローカルへのクローン方法は、[ AWS CodeCommit リポジトリに接続する ](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/how-to-connect.html)を参照。
:::


### 4. Gitでのコミットに必要な情報を パラメータストア に追加する

1. [**Systems Manager** ](https://ap-northeast-1.console.aws.amazon.com/systems-manager/home?region=ap-northeast-1)にアクセスし、ナビゲーションバーからパラメータを作成するAWSリージョンを選択する。

2. 「**パラメータストア**」を開く。
![パラメータストアトップ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/8e263b09-d0e5-94f6-a55d-77f5ac1656ed.png)

3. Gitに設定するメールアドレスをパラメータとして追加する。
  「**パラメータの作成**」をクリックし、パラメータ名を「**名前**」に、メールアドレスを「**値**」に、必要に応じてパラメータの説明を「**説明 — Optional**」欄に記入した上で、「**パラメータを作成**」をクリックする。
![git-user-email.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/b3615788-576e-e549-70da-c25d1ca8f8e5.png)

4. Gitに設定するユーザー名をパラメータとして追加する。
   再度「**パラメータの作成**」をクリックし、パラメータ名を「**名前**」に、ユーザー名を「**値**」に、必要に応じてパラメータの説明を「**説明 — Optional**」欄に記入した上で、「**パラメータを作成**」をクリックする。
![git-user-name.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/ddff5c67-bce7-ed9d-3f40-78605d0c9760.png)

5. 2つのパラメータが作成された。
   作成したパラメータの「**名前**」は、後の手順で使うため、メモしておく。
![パラメータ作成完了.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/1cb86ee6-6a26-1b24-c106-65e7fb09039c.png)


### 5. CodeCommit でビルド対象リポジトリに `buildspec.yaml` ファイルを追加する

1. [**CodeCommit** ](https://ap-northeast-1.console.aws.amazon.com/codesuite/codecommit/repositories?region=ap-northeast-1)にアクセスし、ナビゲーションバーからビルド対象となるリポジトリが存在するAWSリージョンを選択する。

2. ビルド対象リポジトリを選択し、ビルド対象となるブランチを選択する。
![ブランチの切り替え.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/c77ce687-a5e1-5b73-2d9a-25547cc0e932.png)

3. 「**ファイルの追加 > ファイルの作成**」を選択する。
![ファイルの追加.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/3ab643b8-caad-d8d4-9761-f72f290dd8cf.png)

3. ブランチが正しいことを確認し、リポジトリのルートディレクトリに、以下の`buildspec.yaml`を追加する。
  ファイルの環境変数名やコミットコメントは、必要に応じて書き換えて良い。
   `buildspec.yaml`についての詳細は、[ CodeBuild のビルド仕様に関するリファレンス ](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)を参照。ファイル内で使われている Git コマンドについての詳細は、[ 基本的な Git コマンド ](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/how-to-basic-git.html)や[ Git公式リファレンス ](https://git-scm.com/docs/)を、Linux コマンドについての詳細は、[ Linux［基本］コマンドQuickリファレンス ](https://gihyo.jp/assets/pdf/book/2021/978-4-297-12024-5/LinuxCmdQuickReference.pdf)や[ rsyncのサンプル例集 ](https://qiita.com/shnagai/items/7403411b4aa193d0fa0d)を参照。
![ファイルの追加&ブランチ確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/5a5ff817-33ce-a552-7b1e-bc2afd115cd0.png)

    ``` buildspec.yaml
    version: 0.2
    
    env:
      parameter-store:
        user-email: "${ 4.3で作成したパラメータの名前 }"
        user-name: "${ 4.4で作成したパラメータの名前 }"
      git-credential-helper: yes
    
    phases:
      install:
        runtime-versions:
          nodejs: 18
        commands:
          - npm install --force
      build:
        commands:
          - rm -rf out/*
          - npm run export-production
          - ls -la out
      post_build:
        commands:
          # gitでのコミットに必要な情報を設定
          - git config --global user.email $user-email
          - git config --global user.name $user-name
          # 最新のタグ名を取得
          - rm -rf ${ ビルドリポジトリをクローンする任意のディレクトリ }/*
          - git clone ${ 1.1でコピーしたURL } ${ ビルドリポジトリをクローンするディレクトリ }
          - cd ${ ビルドリポジトリをクローンしたディレクトリ }
          - git checkout ${ 当ファイルを追加するブランチ }
          - git branch --contains
          - GIT_TAG="$(git describe --tags --abbrev=0)"
          - cd ..
          # ビルド成果物をコミット&プッシュ
          - rm -rf ${ バージョン管理リポジトリをクローンする任意のディレクトリ }/*
          - git clone ${ 2.1でコピーしたURL } ${ バージョン管理リポジトリをクローンするディレクトリ }
          - ls -la ${ バージョン管理リポジトリをクローンしたディレクトリ }
          - rsync -av --delete --exclude='.git/' out/ ${ バージョン管理リポジトリをクローンしたディレクトリ }/
          - cd ${ バージョン管理リポジトリをクローンしたディレクトリ }
          - git remote set-url origin ${ 2.1でコピーしたURL }
          - git remote -v
          - git add --all
          - git status
          - 'git commit -m "Tag : $GIT_TAG"'
          - COMMIT_ID="$(git rev-parse HEAD)"
          - git push -u origin ${ 3.2で作成されたブランチ }
          # 最新のタグ名とartifactのコミットIDを出力
          - 'echo tag_name: $GIT_TAG'
          - 'echo commit_id: $COMMIT_ID'
    ```

::: note
`buildspec.yaml`が追加できれば良いので、以下の対応を行うのでも問題ない。
* ローカルにクローンしたビルド対象リポジトリに、ファイルを追加しプッシュする。
* 「**ファイルの追加 > ファイルのアップロード**」から、ファイルをアップロードする。
:::


### 6. CodeBuild でビルドプロジェクトを作成する

1. [**CodeBuild** ](https://ap-northeast-1.console.aws.amazon.com/codesuite/codebuild/start?region=ap-northeast-1)にアクセスし、ナビゲーションバーからビルドプロジェクトを作成するAWSリージョンを選択する。

2. 「**プロジェクトの作成**」をクリックする。

3. 以下の通りに設定を行い、「**ビルドプロジェクトを作成する**」をクリックする。
   表に書かれている項目については、説明欄を読んでから設定を行うこと。
    | 分類 | 項目 | 説明 |
    |:--|:--|:--|
    | プロジェクトの設定 | プロジェクト名 | 任意のプロジェクト名を設定 |
    | プロジェクトの設定 | 説明 - オプショナル | 必要に応じてプロジェクトの説明を設定 |
    | ソース | リポジトリ | ビルド対象のリポジトリ名を設定 |
    | ソース | ブランチ | ビルド対象のブランチ名を設定 |
    | 環境 | ロール名 | AWS 側が作成した名前を使用するのでも、自分で命名するので、どちらでも可 |

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/da03ffc7-e1d7-2f25-8d74-b21396041086.png)
    ![codebuild-source.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/a53a2c5d-f131-a581-43d1-5195fd73c69b.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/ff274cfa-1270-fec9-7bfa-4ba67b6d51f8.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/429c7fd6-5327-26ec-745a-cf31af09bf65.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/ad4d07a6-e11a-280a-2a98-2fcd40fa6a0d.png)

4. ビルドプロジェクトが作成された。
![ビルドの詳細.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/a8f24d88-e4be-e6c6-9baf-fd698569fa1f.png)

5. 「**ビルドの詳細 > 環境 > サービスロール**」にあるロールを選択する。
![ビルドの詳細-環境.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/9fc2e056-99f9-1929-4b47-7134fd5eb507.png)

6. ロールの画面に飛んだら、「**許可を追加 > ポリシーをアタッチ**」を選択する。
![ポリシーをアタッチ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/e3100588-6276-1037-f884-450d98a91528.png)

7. 「**AmazonSSMReadOnlyAccess**」と「**AWSCodeCommitFullAccess**」を選択し、「**許可を追加**」をクリックする。

8. 2つのポリシーがアタッチされた。
![許可を追加後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/0131e632-9210-7fa2-30b5-0a148f538cec.png)


### 7. CodePipeline でパイプラインを作成する

1. [**CodePipeline** ](https://ap-northeast-1.console.aws.amazon.com/codesuite/codepipeline/start?region=ap-northeast-1)にアクセスし、ナビゲーションバーからビルドプロジェクトを作成するAWSリージョンを選択する。

2. 「**パイプラインの作成**」をクリックする。

3. 以下の通り設定を行い、「**パイプラインを作成する**」をクリックする。
   表に書かれている項目については、説明欄を読んでから設定を行うこと。
    | 分類 | 項目 | 説明 |
    |:--|:--|:--|
    | パイプラインの設定 | パイプライン名 | 任意のパイプライン名を設定 |
    | パイプラインの設定 | ロール名 | AWS 側が作成した名前を使用するのでも、自分で命名するので、どちらでも可 |
    | ソース | リポジトリ名 | ビルド対象のリポジトリ名を設定 |
    | ソース | ブランチ名 | ビルド対象のブランチ名を設定 |
    | 構築する - オプショナル | リージョン | 任意のリージョンを設定 |
    | 構築する - オプショナル | プロジェクト名 | 6.3で作成したビルドプロジェクト名を設定 |
    | デプロイステージ | -- | 設定をスキップ |

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/22261299-dd87-d943-27a8-1cc80bc6df3c.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/f765201c-5714-43d1-3221-1bbbb3c8184c.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/645567a0-f4a0-4680-45b7-fdd336178a4a.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/5b7d5b89-0115-fadf-3fd8-268142441d4c.png)

4. パイプラインが作成された。
   作成時にパイプラインが自動で実行される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/8830c478-98dd-1dcb-21f8-af5861226fb4.png)


### 8. EventBridge の ルール からパイプラインのトリガーを変更する

1. [**EventBridge** ](https://ap-northeast-1.console.aws.amazon.com/events/home?region=ap-northeast-1#/)にアクセスし、ナビゲーションバーからパイプラインを作成したAWSリージョンを選択する。

2. 「**ルール**」を開く。

3. 5.3で作成したパイプラインに対応したルールを探す。
  「**ルールを検索**」と書かれた検索窓に「**codepipeline-[ ビルド対象リポジトリ(記号を除く6文字目まで) ]-ビルド対象ブランチ(記号を除く6文字目まで) ]**」を入力し、該当するルールを選択する。
   ※ ルールの命名規則が変わり、前述の方法では検索できない場合があるので注意。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/4ce87ad3-601d-5411-b11a-8b48c60f9a2a.png)

4. イベントパターンのデフォルトは画像のような内容になっている。
   次の2点を確認し、選択したルールが6.3で作成したパイプラインに対応したものであることを確認する。
      * 「resources」がビルド対象リポジトリの ARN であること。
        ARN の見方は、[CodeCommit リポジトリの詳細を表示する ](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/how-to-view-repository-details.html)を参照。
      * 「referenceType」が「branch」であり、「referenceName」がビルド対象のブランチ名であること。
![ルール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/484c3d78-73e0-0f3e-529b-c13117650485.png)

5. 「**イベントパターン**」の画面右にある「**編集**」をクリックする。

6. 「**パターンを編集**」をクリックし、ファイルを以下のように書き換える。
![ルール-編集.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/651e5e77-f012-e72c-ddcb-320706a17e30.png)

    ```diff_json
    {
      "source": ["aws.codecommit"],
      "detail-type": ["CodeCommit Repository State Change"],
      "resources": ["arn:aws:codecommit:ap-northeast-1:${ アカウントID }:${ ビルド対象リポジトリ }"],
      "detail": {
        "event": ["referenceCreated", "referenceUpdated"],
    -   "referenceType": ["branch"],
    -   "referenceName": ["${ ビルド対象ブランチ }"]
    +   "repositoryName": ["${ ビルド対象リポジトリ }"],
    +   "referenceType": ["tag"],
    +   "referenceName": [{
    +     "exists": true
    +   }]
      }
    }
    ```

7. 編集できたら、「**次へ**」を何回か選択し、「**ルールの更新**」をクリックする。

8. イベントパターンが変更された。
![ルール-編集後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/3cfdc001-87ad-ff46-4cf7-536afa111547.png)


## 9. 作成したものを動かしてみる

1. ローカルにクローンしたビルド対象リポジトリにタグを付け、プッシュする。
   タグ付けの方法は、[ AWS CodeCommit で Git タグを作成する ](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/how-to-create-tag.html)を参照。
![タグの付与.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/4a37b837-10fb-fbef-f940-d066446c3c23.png)

2. バージョン管理用リポジトリに、「Tag : ${ プッシュされたタグ名}」をコミットコメントとして、ビルド成果物が出力された。
![タグの付与によるコミット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/a8feff53-e572-7533-6187-69890cff90f5.png)
![ビルド成果物.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322404/387db316-4917-6284-48f5-c94bb756532c.png)


## 参考サイト

* [AWS CodeCommit](https://docs.aws.amazon.com/ja_jp/codecommit/?icmpid=docs_homepage_devtools)
  * [Git および の開始方法AWS CodeCommit](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/getting-started.html)
  * [AWS CodeCommit リポジトリを作成する](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/how-to-create-repository.html)
  * [AWS CodeCommit リポジトリに接続する](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/how-to-connect.html)
* [AWS CodeBuild](https://docs.aws.amazon.com/ja_jp/codebuild/?icmpid=docs_homepage_devtools)
  * [ビルドプロジェクトの作成 (コンソール)](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/create-project-console.html)
  * [CodeBuild のビルド仕様に関するリファレンス](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)
* [AWS CodePipeline](https://docs.aws.amazon.com/ja_jp/codepipeline/?icmpid=docs_homepage_devtools)
  * [CodeBuild を使用するパイプラインを作成する (CodePipeline コンソール)](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/how-to-create-pipeline-console.html)
* [Amazon EventBridge](https://docs.aws.amazon.com/ja_jp/eventbridge/?icmpid=docs_homepage_appintegration)
  * [Amazon EventBridge のイベントパターン](https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-event-patterns.html#eb-create-pattern)
  * [Tag-based Deployment Trigger for AWS CodePipeline](https://medium.com/@dania.ref96/tag-based-deployment-trigger-for-aws-codepipeline-38ace7498fcd)
* [AWS Systems Manager](https://docs.aws.amazon.com/ja_jp/systems-manager/?icmpid=docs_homepage_mgmtgov)
  * [Systems Manager パラメータを作成する (コンソール)](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/parameter-create-console.html)
* [Git 公式リファレンス](https://git-scm.com/docs/)
* [Linux［基本］コマンド Quickリファレンス](https://gihyo.jp/assets/pdf/book/2021/978-4-297-12024-5/LinuxCmdQuickReference.pdf)
* [rsyncのサンプル例集](https://qiita.com/shnagai/items/7403411b4aa193d0fa0d)