🖨️

そのリリースノート、自動化しませんか?

2023/07/08に公開

TL;DR

mainブランチマージ時にGitHub ActionsにてNotionに自動でリリースノートを生成し、Slackへ通知を行いましょう。
用意するのは2ファイルだけ。
コピペで簡単、今日から時短!(韻)

作成したリポジトリはこちら
https://github.com/Akihide-Tsue/auto_release_note

出来上がりのサンプル(お好みで変更可能)

Notionに生成されたリリースノート
ブランチ名のprefix(fix/~~)などに合わせたアイコンを表示します。

開くと、Pull Request作成時のコメントが表示されます。

Slackの通知
リポジトリ名、PR名、GitHubのURL、NotionのURLを表示しています。

使用するライブラリ

適宜npmyarnに置き換えてください。(初めてpnpm使った!)
pnpm add @notionhq/client @tryfabric/martian ts-node

必要なコードについて

Next.jsのApp Routerを使用し ./src/app/api/releaseNote.mjs に Notion APIを叩くファイルを配置しています。Next.jsでない場合は、ディレクトリを変えたら動くと思います。

package.json
  "scripts": {
    // 以下を追記。pathは必要に応じて書き換えてください。
    "release-note": "ts-node ./src/app/api/releaseNote.mjs",
  },

実行するactions

release-note.yml
name: release note & slack

on:
  pull_request:
    types: [closed]
    branches:
      - main

jobs:
  create-release-tag:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    if: github.event.pull_request.merged == true

    env:
      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      TZ: "Asia/Tokyo"

    strategy:
      matrix:
        node-version: [18.x]

    steps:
      - uses: actions/checkout@v3

      # 前回のリリースタグを取得する
      - name: Get previous tag
        id: pre_tag
        run: |
          echo "::set-output name=pre_tag::$(curl -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name)"

      # タグを生成する 「{YYYY.MM.DD}-{当日リリース回数}」
      - name: Generate release tag
        id: release_tag
        run: |
          today=$(date +'%Y.%m.%d')
          pre_release_date=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $1}')
          pre_release_count=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $2}')
          if [[ ! $pre_release_date = $today ]]; then
            echo "init count"
            pre_release_count=0
          fi
          echo "::set-output name=release_tag::$today-$(($pre_release_count + 1))"

      # 環境変数に情報を保存
      - name: Register PR detail
        id: pr_detail
        # https://docs.github.com/ja/github-ae@latest/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request
        run: |
          echo "REPOSITORY_NAME=${{ github.event.repository.name }}" >> $GITHUB_ENV
          echo "ASSIGNEE=${{ github.event.pull_request.user.login }}" >> $GITHUB_ENV
          echo "PR_URL=${{ github.event.pull_request.html_url}}" >> $GITHUB_ENV

      # ブランチ名のprefixからラベル名を取得する
      - name: Get label name
        id: get_label_name
        run: |
          branch_type=$(echo ${{github.head_ref}} | cut -d "/" -f1)
          if [ $branch_type == 'feature' ]; then
            label_name=$(echo "enhancement")
          elif [ $branch_type == 'fix' ] || [ $branch_type == 'hotfix' ]; then
            label_name=$(echo "bug")
          else
            label_name=""
          fi
          echo "::set-output name=label_name::$label_name"
          echo "PREFIX_LABEL=$label_name" >> $GITHUB_ENV

      # リリースノート作成
      - name: Create release note
        id: release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ steps.release_tag.outputs.release_tag }}
          release_name: ${{ github.event.pull_request.title }}
          body: ${{ github.event.pull_request.body }}
          draft: false
          prerelease: false

      # リリースノートの内容を格納
      - name: Dump release note
        id: release_note
        run: |
          EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
          echo "RELEASE_NOTE<<$EOF" >> $GITHUB_ENV
          curl -X GET -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' -H 'X-GitHub-Api-Version: 2022-11-28' https://api.github.com/repos/${{ github.repository }}/releases/latest >> $GITHUB_ENV
          echo "$EOF" >> $GITHUB_ENV

      # インストール(cacheを使用):pnpmの場合は下記を使用してください
      - name: Install Node.js
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}

      - uses: pnpm/action-setup@v2
        name: Install pnpm
        id: pnpm-install
        with:
          version: 7
          run_install: false

      - name: Get pnpm store directory
        id: pnpm-cache
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

      - uses: actions/cache@v3
        name: Setup pnpm cache
        with:
          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile

      # インストール(cacheを使用):yarn の場合は下記を使用してください
      # - name: Use Node.js ${{ matrix.node-version }}
      #   uses: actions/setup-node@v3
      #   with:
      #     node-version: ${{ matrix.node-version }}
      #     cache: yarn
      # - name: yarn install
      #   run: yarn install --frozen-lockfile


      # releaseNote実行
      - name: Generate release note
        run: pnpm run release-note
        env:
          NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }}
          NOTION_DATABASE_ID: ${{ secrets.NOTION_DATABASE_ID }}

      # slack通知
      - name: Slack notification
        uses: slackapi/slack-github-action@v1.24.0
        with:
          channel-id: ${{ secrets.RELEASE_SLACK_CHANNEL }}
          payload: |
            {
              "text": "リリースしました🎉\n${{ github.event.repository.name }}",
              "attachments": [{
                "color": "#10AA39",
                "fields": [
                  {
                    "title": "${{ github.event.pull_request.title }}",
                    "value": "${{ github.event.pull_request.html_url }}"
                  },
                  {
                    "title": "${{ steps.release_tag.outputs.release_tag }}",
                    "value": "${{ secrets.NOTION_DB_PAGE_URL }}"
                  }
                ]
              }]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_FOR_RELEASE_NOTE }}
          SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

Notion APIを叩くファイル

releaseNote.mjs
import { Client } from "@notionhq/client";
import { markdownToBlocks } from "@tryfabric/martian";

const MemberList = {
  "Akihide-Tsue": "津江",
  sample_user_name: "適宜追加してください",
};

async function main() {
  const RELEASE_NOTE = process.env.RELEASE_NOTE || '{"body": "中身"}';
  const ASSIGNEE = process.env.ASSIGNEE;
  const PREFIX_LABEL = process.env.PREFIX_LABEL;
  const emoji = () => {
    switch (PREFIX_LABEL) {
      case "enhancement":
        return "🚀";
      case "bug":
        return "💊";
      default:
        return "🔧";
    }
  };

  try {
    const notion = new Client({ auth: process.env.NOTION_TOKEN });
    const release_status = JSON.parse(RELEASE_NOTE);
    const date = new Date();
    date.setTime(date.getTime() + 1000 * 60 * 60 * 9); // JSTに変換

    const params = {
      parent: {
        database_id: process.env.NOTION_DATABASE_ID,
      },
      icon: {
        type: "emoji",
        emoji: emoji(),
      },
      properties: {
        // NotionのDBの各項目名と一致する必要があります
        Title: {
          title: [
            {
              text: {
                content: release_status.name,
              },
            },
          ],
        },
        "Release date": {
          date: {
            start: date,
            time_zone: "Asia/Tokyo",
          },
        },
        Assignee: {
          rich_text: [
            {
              text: {
                content: MemberList[ASSIGNEE] || ASSIGNEE,
              },
            },
          ],
        },
        URL: {
          url: process.env.PR_URL,
        },
      },
      children: markdownToBlocks(release_status.body),
    };

    // @ts-ignore
    await notion.pages.create(params);
  } catch (e) {
    console.error("error:", e);
  }
}

main();

必要なsecret(環境変数)について

以下のsecretを該当リポジトリの Settings > secrets and variables > New repository secret から登録してください。

  • NOTION_TOKEN
    Notion のAPI token
  • NOTION_DB_PAGE_URL
    → 更新するNotionのDBのURL
  • NOTION_DATABASE_ID
    → NOTION_DB_PAGE_URLのid部分: 参考
  • RELEASE_SLACK_CHANNEL
    → 通知したいSlackチャンネルのid
  • SLACK_WEBHOOK_FOR_RELEASE_NOTE
    → インテグレーションのincoming-webhookのid

Notionのコネクトの設定はこちらの記事に詳しく説明があります。
Notion API を使用してデータベースを操作する

必要な設定

こちらの記事を参考にしました。
Notionのコネクトをお忘れなく。

また、Actionsの権限も変更が必要です。
Settings > Actions > General > Workflow permissions は Read and write permissions を選択

VSCodeのおすすめ拡張機能

GitHub Actions
コード補完と作業リポジトリでsecretsが登録されていない場合に教えてくれます。

最後に

Notionに画像が文字列のURLで表示されるので、画像として表示したいです。どなたか対応方法ご存知でしたら教えてください。
GitHub ActionsもApp Routerも今回始めて書いたので、使い方間違っていたらすみません。
参考記事だとtsファイルを実行してNotion API叩いてますが、エラーだったのでmjsファイルにしています。どうしたらいいのか...

なにかありましたらコメントください。

それでは Happy release note!! 🎉


参考記事

Discussion