🔔

GitHub Actionsでprivateなgolang packageのupdate PullRequestをいい感じに自動作成したい

2021/07/29に公開

記事のターゲット

  • GitHub Actionsで複数repositoryに対してPullRequestを作成したい人
  • private repositoyでgolangの共通ライブラリを取り扱いたい人

背景

  • 外部APIのラッパーをclientライブラリとしてgolangで実装した
  • 社内で使う用途なのでGitHubのprivate repositoryで管理した
  • clientライブラリを利用したいprivate repositoryは10以上存在している

private repositoryとは言えgo getできるような状態を作った上で、clientライブラリ側の更新時にいい感じに利用者に更新したことを伝えたい…
(とは言えshell芸を披露したいわけではない)

概要

いろいろ考えた結果、以下が良いのではないかという結論に至りました

  1. clientライブラリ側のGitHub Actionsでrepository dispatchを使うことで利用者側に変更があったことを伝える
  2. 利用者側のGitHub Actionsで変更を受け取ったらGOPRIVATEを使ってprivate repositoryでもgo getによるpackageの更新を行う
  3. 2.で更新した内容をPullRequestにする

GitHub Actions

GitHub利用者なら一度は聞いたことのあるCI/CDワークフローのツールです
GitHubの操作をトリガーに起動できるところに大きな特徴があります

社内で利用することもありGitHub Organizationと合わせると相性が良かったので採用しています
今回のユースケースだと1回の起動あたり1分もかからないので費用面での心配もなかったです

repository dispatch

基本的にGitHub Actionsでは他のrepositoryに影響するジョブを組むことが難しいのWebhookイベントとしてトリガーできるrepository_dispatchを利用することになります

https://docs.github.com/en/actions/reference/events-that-trigger-workflows#repository_dispatch

動作確認の際にもcurlで実行することもできるので今回のようなケースでおすすめです

実行例
$ curl -X POST -H "Authorization: token $TOKEN" -H "Accept: application/vnd.github.everest-preview+json" -d '{"event_type": "event_name"}' -i  https://api.github.com/repos/{user_name}/{repository_name}/dispatches

GOPRIVATE

golangv1.13から追加された環境変数でプライベートな環境に配置したモジュールを取得するために設定します
$GOPRIVATEにマッチするパスの場合、プロキシ(proxy.golang.org)もチェックサムのDB(sumdb)も使用されません
それ故にprivate repositoryのモジュールをgo getできるということです

実際の設定

基本的な設定項目については明らかになったので、実際に設定していきます
今回はclientライブラリ側と利用者側にGitHub Actionsのworkflowを設定することで「privateなgolang packageのupdate PullRequestをいい感じに自動作成」します

clientライブラリ側

タグ打ちを行った際に更新を通知するようなworkflowを組みます
repository_dispatchを行うためのactionはMarketplaceから提供されているのでそれを利用します

.github/workflows/dispatch-update.yaml
name: dispatch-update
on:
  push:
    tags:
      - 'v*'
jobs:
  release:
    strategy:
      matrix:
        repo: ['{repository1}','{repository2}','{repository3}']
    name: dispatch
    runs-on: ubuntu-latest
    steps:
      - name: dispatch update module
        uses: peter-evans/repository-dispatch@v1
        with:
          repository: {user_name}/${{ matrix.repo }}
          token: ${{ secrets.REPO_ACCESS_TOKEN }}
          event-type: update-client

注意点としてはREPO_ACCESS_TOKENにはpersonal access tokenを指定する必要がある点です
GITHUB_TOKENなどにはdispatchを行う権限がないため、repoの権限を付与したpersonal access tokenを用意してrepositoryのsecretsに登録します

利用者側

dispatchのイベントを受け取ってPRを作成するworkflowを組みます
pull requestを作成するためのactionもMarketplaceから提供されているのでそれを利用します

name: update-client

on:
  repository_dispatch:
    types:
      - update-client
jobs:
  create-pull-request:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-go@v2
        with:
          go-version: 1.16.3
      - name: Set up github token
        run: git config --global url."https://${{ secrets.GO_MODULES_TOKEN }}:x-oauth-basic@github.com/".insteadOf "https://github.com/"
      - name: Update client
        run: GOPRIVATE="github.com/{user_name}/*" go get github.com/{user_name}/{client_repository_name}@latest
      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v3
        with:
          title: "✨ Update API Client"
          base: staging
          branch: update-client

git config --global url."https://${{ secrets.GO_MODULES_TOKEN }}:x-oauth-basic@github.com/".insteadOf "https://github.com/"のコマンドを使用して
GitHub(https://github.com/)にアクセスする際にGO_MODULES_TOKENのpersonal access tokenを利用したアクセスに切り替えます

更にGOPRIVATEを設定することでprivate repositoryからgo getできるようにします

全体の流れ

  1. clientライブラリのprivate repositoryでタグ打ちを行う
  2. GitHub Actionが発火する(tag push trigger)
  3. jobから利用者側のrepositoryに対してrepository_dispatchを行う
  4. 利用者側のrepositoryのGitHub Actionが発火する(repository dispatch trigger)
  5. job内でgo getによりpackageのupdateを行い、PullRequestを作成する

所管

このシンプルな設定により利用者側のrepositoryに更新の通知をすることができました
PullRequestを作成することで更新後のCI実行も行えるのが良いと感じています
しかし、publisherであるclientライブラリ側が利用者一覧を管理しなければならないところはちょっとイケてない気がしています(いい方法があったら教えて欲しい…)

参考

Discussion