[GitHub Actions] リリースノートの自動化の仕組み

## はじめに
- この記事では、`GitHub Actions`を使用して、特定のブランチ（この場合は`prod`ブランチ）にマージされたPRからリリースノートを自動生成し、ログ保管する方法について説明します
- このプロセスは、PRがマージされると自動的にトリガーされ、リリースプロセスを効率化します
- 保管する意図として、後で誰が何をいつリリースしたかを検索できるようにすることです
- 例えばAというリポジトリで該当ブランチにPRを作成してマージしたとします
- そのPRの内容は後で見返す時にGitHub上で検索して調べる時に結構めんどくさいです
- PRの内容がどこかに日付ごとに記載されていればすぐ分かります

## 結論と成果物
- 別のリポジトリのPRの内容を日付ごとに記載して保管したファイル
- [該当ファイル](https://github.com/atsutama2/auto_release_note_log/blob/main/releases/2023/12.md)

![スクリーンショット 2023-12-22 15.32.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/8ff5962f-8fc0-6346-088d-76acfc322915.png)


## 基本的な設定
- 今回使用するリポジトリは下記です
- [auto-release-pr](https://github.com/atsutama2/auto-release-pr)
  - リリースPRを自動生成するActions
- [auto_release_note_log](https://github.com/atsutama2/auto-release-pr)
  - リリースPRの内容をMarkdown形式にして保管 
- メインのブランチを決めます。今回は`prod`ブランチですが、`main`、`develop`と自分が使用しているメインブランチをチェックしてください
- 今回2つのブランチ間でActionsの実行をしますので、[GitHub Actions でのシークレットの使用](https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)こちらの設定を2つのブランチに設定する必要があります
- `Settings`から`Actions permissions`の変更する部分も忘れずに

![スクリーンショット 2023-12-22 17.16.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/56e66206-3701-40dd-1e27-412ab5fbf2f8.png)

## GitHub Actions でのシークレットの設定方法
- 異なるリポジトリ間では、`secrets.GITHUB_TOKEN`は使用できなく、これは単一リポジトリでしか使用できません
- よって、`secrets.<自分で設定したもの>`を使用することで`Actions`を使用することができます
- 2つのリポジトリで設定している図
![スクリーンショット 2023-12-22 15.38.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/0bc855d9-2773-6b8c-7cfe-be52705c09b9.png)
![スクリーンショット 2023-12-22 15.41.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/f24e607b-5a65-6c5b-455a-a1177fd58f38.png)
- SecretKey自体は同じ値を設定

## 自動リリースPR作成について
- [該当コード](https://github.com/atsutama2/auto-release-pr/blob/develop/.github/workflows/create-release-pr.yml)
- `git-pr-release`を利用してPRを作成しています
- 対象は`develop`への`pullreq`をマージしたタイミングです
- git-pr-release
  - https://github.com/x-motemen/git-pr-release

```yml:.github/workflows/create-release-pr.yml
name: Create Release Note

on:
  pull_request:
    branches:
      - prod
    types:
      - closed

jobs:
  call-create-release-note-workflow:
    runs-on: ubuntu-latest
    steps:

      - name: Create Release Note Call
        if: github.event.pull_request.merged == true
        env:
          GITHUB_TOKEN: ${{ secrets.ACTIONS_TOKEN }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
          RELEASED_REPOSITORY: "auto-release-pr"
        run: |
          curl \
          -X POST \
          -H "Accept: application/vnd.github.v3+json" \
          -H "Authorization: token $GITHUB_TOKEN" \
          -d "{\"ref\": \"main\", \"inputs\": {\"prNumber\": \"$PR_NUMBER\", \"releasedRepository\": \"$RELEASED_REPOSITORY\"}}" \
          https://api.github.com/repos/atsutama2/auto_release_note_log/actions/workflows/make-release-note-call.yml/dispatches
```

- `develop`へのマージが完了すると自動生成されたPRが作成されます。(prod向け)
- イメージとしてはこんな感じ

![スクリーンショット 2023-12-22 17.08.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1022988/fda8fa6c-82a7-c6d0-dd8e-ff5525715e85.png)

- このPRをマージしたタイミングで走るリリースノート生成`Actions`を呼ぶのがこちら
- 別のリポジトリで管理しているリリースノートの生成・更新をします
```yml:.github/workflows/create-release-note.yml
name: Create Release Note

on:
  pull_request:
    branches:
      - prod
    types:
      - closed

jobs:
  call-create-release-note-workflow:
    runs-on: ubuntu-latest
    steps:

      - name: Create Release Note Call
        if: github.event.pull_request.merged == true
        env:
          GITHUB_TOKEN: ${{ secrets.ACTIONS_TOKEN }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
          RELEASED_REPOSITORY: "auto-release-pr"
        run: |
          curl \
          -X POST \
          -H "Accept: application/vnd.github.v3+json" \
          -H "Authorization: token $GITHUB_TOKEN" \
          -d "{\"ref\": \"main\", \"inputs\": {\"prNumber\": \"$PR_NUMBER\", \"releasedRepository\": \"$RELEASED_REPOSITORY\"}}" \
          https://api.github.com/repos/atsutama2/auto_release_note_log/actions/workflows/make-release-note-call.yml/dispatches
```

## リリースノートの生成/更新について
- [該当コード](https://github.com/atsutama2/auto_release_note_log/blob/main/.github/workflows/make-release-note-call.yml)
- こちらでPRの詳細からリリースノートを自動生成し、Markdownファイルとして保管するためのものです

```yml:.github/workflows/make-release-note-call.yml
name: Make Release Note Call

on:
  workflow_dispatch:
    inputs:
      prNumber:
        type: string
        description: "release pr number"
        required: true
      releasedRepository:
        type: string
        description: "released repository name"
        required: true

concurrency: make-release-note
jobs:
  make-release-note:
    runs-on: ubuntu-latest
    env:
      RELEASED_REPOSITORY: ${{ github.event.inputs.releasedRepository }}
      PR_NUMBER: ${{ github.event.inputs.prNumber }}
      ORGANIZATION: "atsutama2"
      DIRECTORY_NAME: "releases"
    steps:
      - uses: actions/checkout@v4

      - name: Create Date and Directory Path
        run: |
          YEAR=$(TZ=Asia/Tokyo date +'%Y')
          MONTH=$(TZ=Asia/Tokyo date +'%m')
          echo "YEAR=$YEAR" >> $GITHUB_ENV
          echo "MONTH=$MONTH" >> $GITHUB_ENV
          echo "FILE_PATH=${DIRECTORY_NAME}/$YEAR/$MONTH.md" >> $GITHUB_ENV

      - name: Make File
        id: make-file
        run: |
          if [ ! -d "${DIRECTORY_NAME}/$YEAR" ]; then
            mkdir -p "${DIRECTORY_NAME}/$YEAR"
          fi

          if [ ! -f "$FILE_PATH" ]; then
            touch "$FILE_PATH"
          fi

      - name: Get Pull Request Details
        run: |
          API_URL="https://api.github.com/repos/${ORGANIZATION}/${RELEASED_REPOSITORY}/pulls/${PR_NUMBER}"

          PR_DATA=$(curl -H "Authorization: token ${{ secrets.ACTIONS_TOKEN }}" $API_URL)
          echo PR_TITLE=$(echo "$PR_DATA" | jq -r '.title') >> $GITHUB_ENV
          PR_BODY=$(echo "$PR_DATA" | jq -r '.body')

          # すべてのラベル名をカンマ区切りの文字列として保存
          LABEL_NAMES=$(echo "$PR_DATA" | jq -r '.labels | map(.name) | join(", ")')
          echo "LABEL_NAMES=$LABEL_NAMES" >> $GITHUB_ENV

          IFS=$'\n' PR_NUMBERS=($(echo "$PR_BODY" | grep -oP '(?<=#)\d+'))
          echo "PR_NUMBERS=$(IFS=,; echo "${PR_NUMBERS[*]}")" >> $GITHUB_ENV
          echo "API_URL=$API_URL" >> $GITHUB_ENV

      - name: Fetch and Store PR Details as Sets
        if: ${{ env.PR_NUMBERS != '' }}
        run: |
          IFS=',' read -r -a PR_NUMBERS_ARRAY <<< "${PR_NUMBERS}"

          for PR_NUM in "${PR_NUMBERS_ARRAY[@]}"; do
            PR_API_URL="https://api.github.com/repos/${ORGANIZATION}/${RELEASED_REPOSITORY}/pulls/${PR_NUM}"
            PR_DETAILS=$(curl -H "Authorization: token ${{ secrets.ACTIONS_TOKEN }}" $PR_API_URL)
            PR_TITLE=$(echo "$PR_DETAILS" | jq -r '.title')
            PR_URL=$(echo "$PR_DETAILS" | jq -r '.html_url')
            PR_AUTHOR=$(echo "$PR_DETAILS" | jq -r '.user.login')
            PR_DETAILS_SETS+=("$PR_TITLE###$PR_URL###$PR_AUTHOR")  # '###'を区切り文字として使用
            echo "Added to PR Details Sets: $PR_TITLE ### $PR_URL ### $PR_AUTHOR"
          done

          echo "PR_DETAILS_SETS=$(IFS='@@@'; echo "${PR_DETAILS_SETS[*]}")" >> $GITHUB_ENV

      - name: Create Markdown Content
        run: |
          # PR_DETAILS_SETSを配列に変換
          IFS='@@@' read -r -a PR_DETAILS_SETS_ARRAY <<< "${PR_DETAILS_SETS}"

          RELEASE_DATETIME=$(TZ=Asia/Tokyo date +'%Y/%m/%d %H:%M:%S')

          # 一時ファイルにMarkdown文字列を作成
          echo "## [${RELEASE_DATETIME}](https://github.com/${ORGANIZATION}/${RELEASED_REPOSITORY}/pull/${PR_NUMBER})" > temp_markdown_content.txt
          echo "Labels: $LABEL_NAMES" >> temp_markdown_content.txt
          for PR_SET in "${PR_DETAILS_SETS_ARRAY[@]}"; do
            # '###'を区切り文字として使用
            TITLE=$(echo "$PR_SET" | awk -F '###' '{print $1}')
            URL=$(echo "$PR_SET" | awk -F '###' '{print $2}')
            AUTHOR=$(echo "$PR_SET" | awk -F '###' '{print $3}')

            # Markdownフォーマットに従ってファイルに書き込み
            echo "- (${AUTHOR}) ${TITLE}" >> temp_markdown_content.txt
            echo "  - ${URL}" >> temp_markdown_content.txt
          done

          # 既存のファイル内容の先頭に新しい内容を追加
          if [ -f "$FILE_PATH" ]; then
              cat temp_markdown_content.txt "$FILE_PATH" > temp_file && mv temp_file "$FILE_PATH"
          fi

      - name: Commit Change
        run: |
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add "$FILE_PATH"
          git commit -m "make release note: ${FILE_PATH}" -a

      - name: Push Changes
        uses: ad-m/github-push-action@v0.6.0
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.ref }}
```

## まとめ
`GitHub Actions`を使ってリリースノートを自動生成する便利な方法をご紹介しました。PRがマージされるとリリースノートが出来上がり、整理されたログが手元に残ります。めんどうな手作業から解放されて、どんな変更がいつ行われたかをパッと見ることができるようになります。このやり方を取り入れれば、プロジェクトの進行状況をスムーズに追跡できるようになり、チームの作業効率もアップすると思います。