🐁

Repository Dispatch を GitHub Apps トークンで動かす GitHub Actions を作った

2024/03/17に公開

はじめに

GitHub Actions を使ったピタゴラスイッチを構築する上で repository_dispatch は非常に重要です。

https://docs.github.com/ja/actions/using-workflows/events-that-trigger-workflows#repository_dispatch

これまで repository_dispatch を GitHub Actions のジョブで利用する際 peter-evans/repository-dispatch を活用してきました。
トークンは Personal Access Token (PAT) を使っていたのですが GitHub Apps のトークンに移行することにしました。
しかし、うまく動かず ...
今回 GitHub Apps トークンで repository_dispatch を動かすために試行錯誤し最終的に GitHub Actions Marketplace を作るところまで実装してみたのでその紹介記事です。

https://github.com/marketplace/actions/github-repository-dispatch

peter-evans/repository-dispatch

前述していたとおり repository_dispatch を GitHub Actions で呼び出す際は peter-evans/repository-dispatch を利用していました。

https://github.com/marketplace/actions/repository-dispatch

Personal Access Token (PAT) は管理がめんどくさいので GitHub Apps トークンへと移行するモチベーションがありました。

https://zenn.dev/tmknom/articles/github-apps-token

よし、GitHub Apps トークンの準備はできた! peter-evans/repository-dispatch に渡して実行しよう! ... ん??? ... 動かない ... ???

Run peter-evans/repository-dispatch@v2
  with:
    token: ***
    repository: owner/name
    event-type: event-type
    client-payload: {"payload": "xxxxxxxxxx"}
Error: Resource not accessible by integration

なんだろう。権限かな ... ???
GitHub Apps に対しては必要とされる権限渡しているんだけどな ... ???

そういえば前に外部から repository_dispatch 起動させるために Go の実装[1]したことあったけどそれだと動いたよな ... う〜ん peter-evans/repository-dispatch のドキュメントを確認しよう。

GITHUB_TOKEN (permissions contents: write) or a repo scoped Personal Access Token (PAT). See token for further details.

え ... GitHub Apps トークンに対応していない ... !!!
なるほど、そりゃ動かないわけです。

Go で CLI を実装

ちょっと前に repository_dispatch 起動のコードを Go で実装[1:1]していたのでそれを起点に作り直します。
Go で CLI を実装するといえば 標準ライブラリを使うか spf13/cobra を使うかになるかと思います。標準ライブラリだと機能が不足しているのでちょっとしんどいな ... spf13/cobra でも機能が豊富すぎて手軽に実装するにはちょっとしんどいな ...
ということで urfave/cli を利用することにしました。公式ドキュメントにサンプルコードもあったので結構手軽に実装できました。

ある程度形ができたあと GitHub Actions から呼び出して調整し最終的に以下のような CLI となりました。

NAME:
   repository-dispatch - Repository Dispatch a GitHub Actions workflow

USAGE:
   repository-dispatch [global options] command [command options] 

DESCRIPTION:
   Dispatch a GitHub Actions workflow for a repository. Please specify github apps token or github apps app id and github apps app private key.

COMMANDS:
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --client-payload value, -p value    GitHub Client Payload
   --event-type value, -e value        GitHub Event Type
   --id value, -i value                GitHub Apps ID
   --private-key value, -k value       GitHub Apps Private Key
   --repository-name value, -n value   GitHub Repository Name
   --repository-owner value, -o value  GitHub Repository Owner
   --token value, -t value             GitHub Apps Token
   --help, -h                          show help

GitHub Apps トークンだけを渡して実行するパターンと GitHub Apps ID と GitHub Apps Private Key を渡して実行するパターンに対応してみました。

go install github.com/otakakot/repository-dispatch@latest

上記コマンドでインストールし実行可能なので気になった方はお試しください。

せっかくだから、 repository_dispatch をパッケージとしてインポートしてコードから手軽に呼び出せるような構成にもしてみました。が、動作は未確認です ...

GitHub Actions で CLI を呼び出す

めでたく go install で動かせるようになりました。 CI 上で go install して呼び出すことで目的は達成できそうです。でもせっかくなので GitHub Actions で手軽に呼び出せるようにしてみるかと思いもう少し手を動かしてみました。
独自のアクションは以下の公式ドキュメントを参考にすることで作成できます。

https://docs.github.com/ja/actions/creating-actions

おや、 Docker もしくは JavaSrcipt でしか実装できない ... ???
いや、そんなことはなかったです。よくよくみると複合アクションというのを使えばもうちょっと自由度高く実装できそうです。

最終的には以下のようにして jobs 設定で呼び出せるように調整しました。

jobs:
  dispatch:
    runs-on: ubuntu-latest
    steps:
      - name: Generate token
        id: generate-token
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ secrets.GITHUB_APPS_ID }}
          private-key: ${{ secrets.GITHUB_APPS_PRIVATE_KEY }}
          owner: owner
          repositories: repository
      - name: Dispatch
        uses: otakakot/repository-dispatch@v1.0.0
        with:
          token: ${{ steps.generate-token.outputs.token }}
          repository: owner/repository
          event-type: event-type
          client-payload: '{"payload": "xxxxxxxxxx"}'

渡すパラメータについては peter-evans/repository-dispatch を参考にしています。 with で渡す値が同じになるようにしています。
Go で作成した CLI ツールとの整合性を取るために bash で受け取った値を整形していたりします。

独自アクションの実装として必要となる action.yaml は以下のようになりました。

name: 'GitHub Repository Dispatch'
description: 'Create a repository dispatch event'
inputs:
  token:
    description: 'GitHub Apps Token'
    required: true
  repository:
    description: 'The repository to send the dispatch event to'
    default: ${{ github.repository }}
  event-type:
    description: 'A custom webhook event name.'
    required: true
  client-payload:
    description: 'JSON payload with extra information about the webhook event that your action or worklow may use.'
    default: '{}'
runs:
  using: 'composite'
  steps:
    - name: Set GitHub Path
      run: echo "$GITHUB_ACTION_PATH" >> $GITHUB_PATH
      shell: bash
      env:
        GITHUB_ACTION_PATH: ${{ github.action_path }}
    - name: Setup Go
      uses: actions/setup-go@v5
      with:
        go-version: 1.22.1
        cache: false
    - name: Repository Dispatch
      run: entrypoint.sh
      shell: bash
      env:
        TOKEN: ${{ inputs.token }}
        REPOSITORY: ${{ inputs.repository }}
        EVENT_TYPE: ${{ inputs.event-type }}
        CLIENT_PAYLOAD: ${{ inputs.client-payload }}
#!/bin/bash -ue

go install github.com/otakakot/repository-dispatch@latest

repos=(`echo ${REPOSITORY} | tr '/' ' '`)

repository-dispatch --token ${TOKEN} --event-type ${EVENT_TYPE} --repository-owner ${repos[0]} --repository-name ${repos[1]} --client-payload "${CLIENT_PAYLOAD}"

shell 内で go install を使い CLI をインストールしています。
ツールへのパスを通すために setup-go で Go の環境をセットアップしています。

これでめでたく Go による GitHub Actions を作ることができました!88888888

おわりに

今回の成果物は以下においておきます。

https://github.com/otakakot/repository-dispatch

https://github.com/marketplace/actions/github-repository-dispatch

本格的に使ってもらうために README.md とかバージョン管理とかちゃんとしないとです ...
というかそもそも別の方がライブラリが存在するのであれば教えていただきたいです。

脚注
  1. https://github.com/otakakot/playground-github-actions/blob/main/cmd/dispatch/main.go ↩︎ ↩︎

Discussion