【GitHub Actions】Next.js Bundle Analysisを使ったバンドルサイズの比較に失敗した時にコメントを残す【備忘録】

ジンです。

初めてNext.js Bundle Analysisを使ったCIの改修に取り組んだので、解決方法や調べてわかったことをまとめました。

# 何をやったのか、この記事は何の話なのか
PRを作成した時にそのPRのベースブランチをヘッドブランチとしたPRが存在しない場合、ヘッドブランチとベースブランチのバンドルサイズの比較ができずCIが落ちてしまう問題に対して、PRに解決方法のコメントを残すことによって担当者が自ら解決できるようにした。

# 目次
1. [CI改修で解決したいこと、CIが失敗する原因](https://qiita.com/JinA293/private/7d7e74b4da6cbb4db276#ci%E6%94%B9%E4%BF%AE%E3%81%A7%E8%A7%A3%E6%B1%BA%E3%81%97%E3%81%9F%E3%81%84%E3%81%93%E3%81%A8ci%E3%81%8C%E5%A4%B1%E6%95%97%E3%81%99%E3%82%8B%E5%8E%9F%E5%9B%A0)

1. [サンプルコードと見る、比較まわりの流れとCIが失敗するケース](https://qiita.com/JinA293/private/7d7e74b4da6cbb4db276#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89%E3%81%A8%E8%A6%8B%E3%82%8B%E6%AF%94%E8%BC%83%E3%81%BE%E3%82%8F%E3%82%8A%E3%81%AE%E6%B5%81%E3%82%8C%E3%81%A8ci%E3%81%8C%E5%A4%B1%E6%95%97%E3%81%99%E3%82%8B%E3%82%B1%E3%83%BC%E3%82%B9)

1. [解決方法](https://qiita.com/JinA293/private/7d7e74b4da6cbb4db276#%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95)

1. [記事を読むときの事前知識とか（おまけ）](https://qiita.com/JinA293/private/7d7e74b4da6cbb4db276#%E8%A8%98%E4%BA%8B%E3%82%92%E8%AA%AD%E3%82%80%E3%81%A8%E3%81%8D%E3%81%AE%E4%BA%8B%E5%89%8D%E7%9F%A5%E8%AD%98%E3%81%A8%E3%81%8B%E3%81%8A%E3%81%BE%E3%81%91)


# CI改修で解決したいこと、CIが失敗する原因
## 解決したいこと
Next.js Bundle Analysisを使ったCIが失敗した時、どう解消するべきかについてその都度チーム内で発生する会話を繰り返さず、担当者が解決できる仕組みを作りたい。


## よく起きる失敗の原因かつ、今回カバーする原因
PRを作成した時にそのPRのベースブランチをヘッドブランチとしたPRが存在しない場合、ベースブランチの計算されたバンドルサイズがGitHub ActionsのArtifactに存在しておらず、ヘッドブランチとベースブランチのバンドルサイズの比較ができず失敗して落ちる。

例）作業ブランチ→集約ブランチへ向けてPRを作成した時に、集約ブランチをベースブランチとしたPRが作られていない場合、集約ブランチの計算されたバンドルサイズがGitHub ActionsのArtifactに存在しておらず、作業ブランチと集約ブランチのバンドルサイズの比較ができず失敗して落ちる。

![スクリーンショット 2024-12-09 23.50.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2814969/7088e165-5909-bed3-6288-127e9f41a22e.png)

# サンプルコードと見る、比較まわりの流れとCIが失敗するケース

## バンドルサイズ計算〜バンドルサイズの比較まわりの流れ
1. ヘッドブランチのバンドルサイズを計算
1. 出力されたヘッドブランチのバンドルサイズのファイルをGitHub ActionsのArtifactにアップロード
1. GitHub ActionsのArtifactに保存されているベースブランチのバンドルサイズをダウンロード
1. ヘッドブランチのバンドルサイズとダウンロードしたベースブランチのバンドルサイズを比較

**nextjs-bundle-analysisのサンプルコード**
```yml
        ## 1. ヘッドブランチのバンドルサイズを計算
      - name: Analyze bundle 

        ## バンドルサイズを計算し、.next/analyze/__bundle_analysis.jsonというパスで出力
        ## ref: https://github.com/hashicorp/nextjs-bundle-analysis/blob/main/report.js
        run: npx -p nextjs-bundle-analysis report

        
        ## 2. 出力されたヘッドブランチのバンドルサイズのファイルをGitHub ActionsのArtifactにアップロード
      - name: Upload bundle 
        uses: actions/upload-artifact@v3
        with:
          name: bundle
          path: .next/analyze/__bundle_analysis.json


        ## 3. GitHub ActionsのArtifactに保存されているベースブランチのバンドルサイズをダウンロード
      - name: Download base branch bundle stats
        uses: dawidd6/action-download-artifact@v2
        if: success() && github.event.number
        with:
          workflow: nextjs_bundle_analysis.yml
          branch: ${{ github.event.pull_request.base.ref }}
          path: .next/analyze/base


        ##　4. ヘッドブランチのバンドルサイズとダウンロードしたベースブランチのバンドルサイズを比較
      - name: Compare with base branch bundle
        if: success() && github.event.number
        run: ls -laR .next/analyze/base && npx -p nextjs-bundle-analysis compare
```
https://github.com/hashicorp/nextjs-bundle-analysis/blob/main/template.yml

## CIが失敗するケース
今回カバーする失敗のケースは、3のベースブランチのバンドルサイズをダウンロードするステップで、事前にバンドルサイズの計算がなされていない（計算されたファイルが存在せずダウンロードできない）時に起きる。

**実際のGitHub Actions上のエラー**
```yml
Error: no matching workflow run found with any artifacts?
##[debug]Node Action run completed with exit code 1
##[debug]Set output found_artifact = false
##[debug]Finishing: Download base branch bundle stats
```

# 解決方法
CIが何らかの理由で失敗し、かつPRのベースブランチをヘッドブランチとしたPRが存在しない場合、CIが実行されたPRのベースブランチをヘッドブランチとしたPRを作成するよう促すコメントをPRに残す、以下のステップを実装した。

## 1. ベースブランチからのPRがあるかチェックするステップ

```yml
- name: Check if PR exists from base branch
  continue-on-error: true ## このステップが失敗してもCI実行を継続する
  run:
    echo "BASE_BRANCH=${{ github.base_ref }}" >> $GITHUB_ENV ## 環境変数としてベースブランチの名前をBASE_BRANCHに代入
    if [ ${{ github.base_ref }} = "develop" ]; then ## もしベースブランチがdevelopの場合このステップをスキップする
      echo "Skipping PR check for develop branch"
    else
      PR_NUMBER=$(gh pr list --head $BASE_BRANCH --json number -q ".[].number") ## ベースブランチがヘッドブランチとして作成されているPRの数を取得
      echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV ## 環境変数として上で取得したPRの数をPR_NUMBERに代入
      if [ -z "$PR_NUMBER" ]; then ## もしPR_NUMBER変数が空の場合、No PR foundと出力
        echo "No PR found"
      fi
    fi
```

## 2. ベースブランチからのPRの作成を促すステップ
```yml
- name: PR from base branch reminder comment
    if: ${{ failure() && env.PR_NUMBER == '' }} ## CIが失敗し、かつ環境変数PR_NUMBERも空の場合
    uses: marocchino/sticky-pull-request-comment@v2 ## PRにコメントするライブラリ
    with:
      header: next-bundle-analysis ## このヘッダーがついたコメントを更新する
      message: 'Error: マージ先のブランチ（ ${{ env.BASE_BRANCH }} ）からのPRが作成されていません。作成後、再実行してください。' ## PRのベースブランチをヘッドブランチとしたPRの作成を促すコメントを残す
```

# 記事を読むときの事前知識とか（おまけ）
## 1. GitHub Actions
> GitHub Actionsを使用すると、ワールドクラスのCI / CDですべてのソフトウェアワークフローを簡単に自動化できます。 GitHubから直接コードをビルド、テスト、デプロイでき、コードレビュー、ブランチ管理、問題のトリアージを希望どおりに機能させます。

GitHub上でワークフローを自動的に実行させることができるサービスです。

https://github.co.jp/features/actions

僕は社内のドキュメントや、チーム内で[『GitHub CI/CD実践ガイド ――持続可能なソフトウェア開発を支えるGitHub Actionsの設計と運用』](https://gihyo.jp/book/2024/978-4-297-14173-8)についての勉強会があったのでそれを主に参照して進めていました。

計算したバンドルサイズを出力した結果を保存するArtifactについては、ジョブ間でデータを共有することのできる機能です。

https://docs.github.com/ja/actions/writing-workflows/choosing-what-your-workflow-does/storing-and-sharing-data-from-a-workflow


## 2. Next.js Bundle Analysis
> Analyzes each PR's impact on your next.js app's bundle size and displays it using a comment. Optionally supports performance budgets.

それぞれのPRがバンドルサイズに与える影響を分析し、コメントを使用して表示します。
**※@next/bundle-analyzerとは別のツールです。僕はずっと混同していました。**

https://github.com/hashicorp/nextjs-bundle-analysis

わかりやすい記事：

https://product.st.inc/entry/2022/03/24/111758

### バンドル（bundle）とは
例えば開発体験向上のためなどコンポーネントごとにファイルを分割しますが、ブラウザ側がファイルを読み込む時には複雑な依存関係を紐解きながらブラウザに最適化された1ファイルへとひとまとめにします。そのプロセスがバンドルです。

## 3. sticky-pull-request-comment
> Create a comment on a pull request, if it exists update that comment. This library runs with GitHub Actions.

PRにコメントを作成し、もしすでに存在している場合はコメントを更新するという、GitHub Actions上で動作するツールです。

https://github.com/marocchino/sticky-pull-request-comment

わかりやすい記事：

https://qiita.com/yokawasa/items/f7fee20fd4a4220f25f8

## 4. WFの改修に取り掛かるまでのあれこれ
**Q. 実際にどう動いているのかを確認するには？**
A. workflowのymlファイルを確認する。読めない部分もあったので、Copilotと会話しながら読んでいました。

**Q. 実際にコケたWFのログを探すには？**
A. リポジトリのActionsタブ > 左のワークフローの一覧から確認したいものを選択 > 実行結果一覧右のステータスからfailureを選択

**Q. 気をつけたことは？**
A. CIも他のソースコード同様、既存のものと記法を揃える。一番楽だし、レビュアーの負担も減る。


# あとがき
シェルスクリプトの記法みたいですが、まだわからないことがちらほら。精進します。