出力を入力へ

プログラミングに関する自分が考えた事を中心にまとめます

GitHubのプルリクエストにRedmineチケットへのリンクがあったら対象チケットにプルリク情報をコメントする

RedmineとGitHubの連携を実現するためにいろいろ試行錯誤したのでそのまとめ。

課題トラッカーとしてRedmineを利用する

課題トラッカーとしてGitHub issueではなくRedmineを利用する場合、 ソフトウェア開発との連携が課題になる。 具体的にはプルリクエストの作成状況とRedmineのチケットを連動させたい。

GitHub issueであれば単位issue番号を記載すれば自動的にリンクになるし、 issue側にもプルリクエストへのリンクが生成される。 単にこれだけではあるが、これをRedmineでも実現したい。

RedmineとGitHubの連携方法

リポジトリ同期

一番単純なのはRedmine上のリポジトリと連動させる方式。 GitHub webhookでredmineにpushしてあげればリポジトリを同期させることができる。

ただし、この方法ではコミットにredmineチケット番号を埋め込めば連動できるが プルリクエストの内容は連動しない。 プルリクエストがマージされればマージコミットのコメントとして埋め込むことはできるが プルリクエストがオープンした段階でも連動して欲しいし、 マージコミットには標準ではプルリク番号しか埋め込まれないので Redmineチケット番号を別途埋め込む必要が出てくる。 redmine側でもリポジトリを持たなくてはいけないのも若干面倒。

webhook連携

GitHubのプルリクの操作を契機としてwebhookが飛ばせるので これを利用してRedmineのチケットにコメントを追記する方法がある。

webhookは単にイベントを飛ばすだけなので直接RedmineのAPIを叩くわけにはいかない。 このため、よくある例としては AWS API Gateway + Lambdaなどの構成により、 webhookを受け取ってコメント内容を整形してからRedmine APIを叩く方法がある。

この方法であれば(おそらく)上手くいくが、わざわざコメントを記載するためだけに API Gateway+Lambdaを構築するのは若干大掛かりな気がする。 単にコメント以外にもいろんな通知等を行うのであればよいが 今回の目的はシンプルなのでもっと簡単に実現したい。

GitHub Actions連携

ということで、よりシンプルな構成で実現する方法としてGitHub Actionsを利用する。 GitHub Actionsでもプルリクのイベントを契機として動作させることができ、 GItHub のAPIやRedmineのAPIを叩くようにCIを構築すればよいので より簡単に構成できる。

GitHub Actionsの構築

実際に構築したGitHub Actionsのyamlファイルが以下の通り。 シークレットとしてREDMINEのAPIキーを設定しておく。

name: Redmien

on:
 pull_request:
   types: [opened, closed, reopened]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v4
        id: pr-details
        with:
          github-token: ${{secrets.GITHUB_TOKEN}}
          script: |
            pr = await github.pulls.get({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.payload.pull_request.number
            })
            return pr.data
      - name: parse redmine issue number
        id: pr-number
        run: |
          number=$(echo '${{steps.pr-details.outputs.result}}' | jq -r .body | sed -r 's|.*https://redmine.example.com/issues/([0-9]+).*|\1|')
          echo "redmine_issue=$number" >> $GITHUB_ENV
          title=$(echo '${{steps.pr-details.outputs.result}}' | jq -r .title)
          echo "pr_title=$title" >> $GITHUB_ENV
      - name: add note to redimne issue
        if: env.redmine_issue != ''
        run: curl -X PUT -H "Content-Type:application/json" -H "X-Redmine-API-Key:${{secrets.REDMINE_API_KEY}}" -d '{"issue":{"notes":"pull request [${{env.pr_title}}](${{github.event.pull_request._links.html.href}}) ${{github.event.action}}"}}' https://redmine.example.com/issues/${{env.redmine_issue}}.json

このスクリプトは以下の3つのステップで構成されている。

プルリク詳細の取得

プルリクのオープン/クローズでは対象のプルリク番号等は取得できるが、その内容は取得できない。 今回はプルリクの本文にredmineチケットへのリンクがあったら対象のredmineチケットにコメントしたいので、 プルリク本文を取得する必要がある。 また、単にリンクを張るだけではチケット側の情報が不足するので、せめてプルリクのタイトルを記載したい。

これを実現するのがgithub-scriptで、GitHub Actions内でGitHub APIを叩くためのactionである。 これを用いてプルリクの詳細を取得して返してあげる。 プルリク本文の取得方法は、README記載の通り octokit/rest.jsの pulls-getを利用すればよい。

octokit.github.io

注意点として、github-scriptのREADMEの通り、 result-encoding: string で情報を返そうとすると タイトルと本文の両方が取得できない。 このため、取得したプルリク情報をjsonのまま返してあげて、次の工程でタイトルと本文を取得することにした。

プルリクタイトルとRedmineチケット番号の取得

プルリク詳細が取得できたので、ここからプルリクのタイトルとRedmineのチケット番号を抽出してあげる。 プルリクのタイトルは単にjsonをパースすればよく、チケット番号はプルリクの本文から正規表現で抽出すればよい。 抽出した結果はそれぞれ別の環境変数へと格納している。 ワークフローコマンドを使うのが一般的(?)だが、 今回は環境ファイルを使うことで対応した。

この程度であれば1つ目のステップで合わせて実行すればよいかとも考えたが、 Redmineチケットへのリンクが無い場合や複数ある場合の対処を考え始めると複雑になりそう。 現時点ではリンクがない場合のみ考慮し複数ある場合は対象外としたが、 そういった対応のためステップを分けた方がよさそうと判断した。

Redmineへのコメントの通知

RedmineのAPIを叩いてコメントを追記する。 この時点で対象のRedmineのチケット番号もコメントとして記載するプルリク番号もあるので 単にRedmineのAPIを叩けばよい。 対象プルリクへのリンクはコンテキスト情報から取得している。 最初コンテキストにどのような情報が含まれているかわからなかったので 公式ドキュメントに従ってログ出力するのがお手軽だった。 (最初ここからプルリク本文が取れることを期待していた)。

Redmine APIを叩く際には各種SDKやCLIを使うことも考えた。 例えばGo実装npm実装なんかがある。 ただ、今回のように1行コメントを書くには不要と判断し、curlで直接叩いてやることにした。 具体的なコメント例が以下の通り。

f:id:thaim:20210515141806p:plain

とりあえずこれでやりたいことは実現できたはず。