GitHub ActionsでPull Requestのタイトルや詳細情報を自動更新する

 GitHubでは、Pull Requestというレビューのための機能が存在していますが、Jiraや、他のプロジェクト管理ツールを使ったりしていると、チケットとPull Requestを行き来する必要があり、些か面倒です。

 参照の手間を軽減するために、毎回プロジェクト管理ツールのチケットへのリンクを貼ったり、タイトルをコピーして貼り付けるなどの作業が発生しているかもしれません。ひとつひとつの手間は無視出来る程度かもしれませんが、1000回、2000回と繰り返していくうちに膨大な時間が失われていることに気づくはずです。

 自動的にタイトルを外部のプロジェクト管理ツールの内容で書き換え、詳細情報にそのチケットへのリンクが入れば、塵も積もればなんとやら、莫大なリソースが浮くはずです。

 本稿では、それを実現するための手段について考えてみます。

GitHub Actions

 GitHub Actionsでは、GitHub上でのイベントをトリガーとして、ワークフローを起動できます。

 詳細は割愛しますが、このワークフローのが意外と柔軟で、awk、sedや、curl、果てはjqなどのlinuxコマンドベースでの操作が可能となっています。他、先述したトリガーに応じて、例えばコミットのハッシュ、メッセージ、Pull RequestのタイトルやBody(詳細情報)を取得することもできます。

 今回はこれを使って、Pull Requestのタイトルと詳細情報を更新していきます。

前提条件

 タイトルや詳細情報を自動記述したいとはいえ、Pull Requestやブランチ、コミットなどからチケットを特定出来る必要があります。そこで今回は、ブランチの名前に、チケット番号を含めるルールとします。

A-123 をチケット番号とするなら、 xxx/yyy-A-123 など、チケット番号が含まれるようにします。ここのルールはご自身の環境に合わせてアレンジしてください。

ファイルを作成する

 適用させたいリポジトリの、 .github/workflows/ 配下に任意の名前のymlファイルを作成します。 pull_request_edit.yml などでも良いでしょう。

 以下の手順は、全てこのファイルに追記していきます。

トリガーを記述する

 今回は、Pull RequestのOpen時にトリガーさせたいので、その旨を記述しましょう。

on:  
  pull_request:  
    types: [opened]

 トリガーに関する詳細はワークフローをトリガーするイベントを御覧ください。

envを記述する

 GitHub CLIをワークフロー内で使いたいので、以下のようにenvを記述します。このtokenは自動的に生成されるようです。

env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

 詳細は、Automatic token authenticationに記載されています。

Jobを記述する

jobs:  
  auto_update_pull_request:
    name: Auto update the just opened pull request
    runs-on: ubuntu-latest

repositoryをcheckoutする

 GitHub CLIのコマンドを利用するためには、checkoutが必要です。 Checkout v3という便利なものがあるので、それを使います。

    steps:  
      - name: checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

チケット情報を取得する

 まず、ブランチ名からチケット番号を取得します。 ${{ github.head_ref }} は、ブランチ名を指します。取得できる情報については、コンテキスト をご参照ください。

      - name: Extract issue key
        env:
          ISSUE_KEY_MATCHER: "[A-Z]+-[0-9]+"
        run: |
          if [[ ${{ github.head_ref }} =~ ${ISSUE_KEY_MATCHER} ]]; then
            issue_key=${BASH_REMATCH[0]}
          else
            exit 0
          fi
          echo "ISSUE_KEY=${issue_key}" >> $GITHUB_ENV

 無事取得できたら、 ISSUE_KEY というKEYのenvに値を格納しておきます。こうすることによって、後続のstepで値を参照することができます。

タイトルを変更する

 タイトルを変更します。curlやsed、jqなどの便利なコマンドが使用できるので、大抵のサービスからは情報を取得できるはずです。

      - name: Update pull request title
        if: ${{ env.ISSUE_KEY }} # ISSUE_KEYが存在しない場合は処理を行わない
        run: |
          title=$(curl https://example.com/path/to/rest/api/${ISSUE_KEY} | jq -r .title)
          gh pr edit ${{ github.event.pull_request.number }} --title "${title}"

 curl及びjqコマンドについては、ご自身の環境に合わせて変更してください。大抵のプロジェクト管理ツールでは、こういったニーズを見越して、APIの用意があるはずです。

 secret(API Token)が必要な場合は、ご自身のプロジェクト管理ツールでTokenを取得した後、GitHubの、 settings -> Secrets -> Actions とクリックし、 New repository secret をクリックすることで追加することができます。

 このsecretは、 ${{ secrets.NAME_OF_YOUR_TOKEN }} で利用することができます。

 

絶対にAPI Tokenを直接コードに書いたりしないでください。流出した場合、プロジェクト管理ツールへのアクセスが可能となってしまい、セキュリティ事故へと繋がります。

チケットへのリンクを貼る

 同様に、詳細情報についても更新します。

      - name:URL to Pull Request description
        if: ${{ env.ISSUE_KEY }} # ISSUE_KEYが存在しない場合は処理を行わない
        run: |
          gh pr edit ${{ github.event.pull_request.number }} --body "http://example.com/path/to/project_management_tool/${ISSUE_KEY}"

こちらも、ご自身の環境に合わせてください。

まとめ

 GitHub Actionsを利用すれば、タグの付与やAuto Formattなど、GitHub上で大抵の自動化が出来るようになります。是非研究してみては如何でしょうか。

 最終版として結合したymlを記載しておきますが、このままでは動かないと思われますので、必ずご自身の環境に合わせて変更をお願いします。

on:  
  pull_request:  
    types: [opened]
env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:  
  auto_update_pull_request:
    name: Auto update the just opened pull request
    runs-on: ubuntu-latest
    steps:  
      - name: checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Extract issue key
        env:
          ISSUE_KEY_MATCHER: "[A-Z]+-[0-9]+"
        run: |
          if [[ ${{ github.head_ref }} =~ ${ISSUE_KEY_MATCHER} ]]; then
            issue_key=${BASH_REMATCH[0]}
          else
            exit 0
          fi
          echo "ISSUE_KEY=${issue_key}" >> $GITHUB_ENV
      - name: Update pull request title
        if: ${{ env.ISSUE_KEY }} # ISSUE_KEYが存在しない場合は処理を行わない
        run: |
          title=$(curl https://example.com/path/to/rest/api/${ISSUE_KEY} | jq -r .title)
          gh pr edit ${{ github.event.pull_request.number }} --title "${title}"
      - name:URL to Pull Request description
        if: ${{ env.ISSUE_KEY }} # ISSUE_KEYが存在しない場合は処理を行わない
        run: |
          gh pr edit ${{ github.event.pull_request.number }} --body "http://example.com/path/to/project_management_tool/${ISSUE_KEY}"