GitHub の issue や pull request, commit にコメントを投稿する CLI ツールを作りました(結構前の話ですが)。

https://github.com/suzuki-shunsuke/github-comment

このブログの執筆時点で最新は v1.5.0 です。

Go 製なので、 GitHub Releases からダウンロードしてくれば簡単にインストールできます。

想定している主な用途は、 CI/CD の 結果をコメントで通知することで DX を向上することです。 例えば CI がこけたらこけたコマンドとエラーメッセージを通知するなどです。

github-comment には

  • init: 設定ファイルの雛形を生成する
  • post: コメントを投稿する
  • exec: 外部コマンドを実行し、その結果を元にコメントを投稿する

という 3 つのサブコマンドがあります。

コメントの投稿には GitHub の Access Token が必要です。 コマンドライン引数 -token でも渡せますが、環境変数として設定しましょう。

$ export GITHUB_TOKEN=xxx # GITHUB_ACCESS_TOKEN も可

post コマンド

こんな感じでコメントを投稿できます。

$ github-comment post -org suzuki-shunsuke -repo github-comment -pr 1 -template test

パラメータの数が多いですが、いくつかの Platform では環境変数から自動でパラメータを補完してくれます。

  • Drone
  • CircleCI
  • GitHub Actions

そうするとこれでよくなります。

$ github-comment post -template test

コメントは Go の text/template で処理されます。 {{.Org}} {{.Repo}} といった感じでパラメータを参照できます。

$ github-comment post -template "{{.Org}}/{{.Repo}} test"
  • PRNumber
  • Org
  • Repo
  • SHA1
  • TemplateKey
  • Vars

exec コマンド

コマンドの実行結果(標準出力、標準エラー出力、 exit code) を元にコメントを投稿したい場合に、 exec コマンドが使えます。

$ github-comment exec -template "{{.ExitCode}} {{.Stdout}}" -- echo hello

コマンドを実行した上でコメントを投稿します。テンプレートの変数として、 post でも渡されるパラメータの他にコマンドの実行結果が渡されます。

  • ExitCode
  • Stdout
  • Stderr
  • CombinedOutput
  • Command: exec.Command.String
  • JoinCommand: コマンド引数(配列)をスペース " " でつないだ文字列

github-comment exec の標準入力は実行するコマンドに渡されますし、 github-comment exec の exit code は実行したコマンドの exit code になります。

設定ファイル

上記の例では投稿するコメントを -template で渡していましたが、ごく短いコメント以外は設定ファイルに記述したほうが良いでしょう。 設定ファイルは github-comment init で雛形を生成できます。

$ github-comment init

こんな感じで複数のテンプレートを記述できます。

post:
  default: |
    foo
  hello: |
    hello
  ...

そして post 実行時に -template-key (-k) で使用するテンプレートを指定します。 -template-k 両方指定しない場合、デフォルトで default テンプレートが使用されます。

$ github-comment -k hello

exec の設定

exec の設定はもう少し複雑です。 それはコマンドの実行結果によって使用するテンプレートを変えたり、あるいはコメントを投稿しなかったりできるようにするためです。 一つのテンプレートキーに対し、複数のテンプレートを配列で設定します。

exec:
  default:
    - when: ExitCode == 0
      template: |
        success
    - when: ExitCode != 0
      template: |
        failed
    ...
  ...

そしてテンプレート文字列とは別に when という、そのテンプレートを使う条件を設定します。 この条件は antonmedv/expr によって処理されます。 テンプレートの text/template とはまた別の構文なのがややこしいですね。 条件の評価結果は当然真偽値ではないといけません。 評価結果が true ならばそれを使ってコメントし、あとは無視されます。 false なら次のテンプレートを評価します。 全部マッチしなければコメントは投稿されませんし、エラーにもなりません。 dont_comment: true とすると、その条件にマッチした場合はコメントを投稿せずに終了します(後続のテンプレートも無視されます)。

exec:
  hello:
    - when: ExitCode != 0
      dont_comment: true
    - when: true
      template: |
        Hello, world

テンプレートの再利用

templates を使うと複数のテンプレートでヘッダーなどを共通化して再利用できます。

templates:
  header: "# {{.Org}}/{{.Repo}}"
post:
  default: |
    {{template "header" .}}

Go の text/template に馴染みがないとわかりにくいかと思いますが、

templates:
  テンプレート名: テンプレート

でテンプレートを定義して

{{template "テンプレート名" .}}

でテンプレートを参照できます。

テンプレートの変数をコマンドライン引数で渡す

-var 変数名:値 でパラメータを渡せます。 {{.Var.変数名}} で参照できます。

$ github-comment post -var name:foo -template "Hello, {{.Var.name}}"

設定ファイルで変数を定義する

設定ファイルで変数を定義できます。任意の型の変数を定義できます。 {{.Var}}.変数名 で参照できます。

vars:
  foo: bar
  zoo:
    foo: hello

{{.Var.zoo.foo}}

post コマンドの標準入力でテンプレートを渡す

github-comment post の標準入力でテンプレートを渡せます。 exec の場合はそうはならない(実行するコマンドに渡される)ので注意してください。

$ echo foo | github-comment post

パラメータの補完

先に述べたとおり、いくつかの Platform では環境変数から自動でパラメータを補完してくれます。

CircleCI

プラットフォームの判別: CIRCLECI の有無

パラメータ ソース
.Org CIRCLE_PROJECT_USERNAME
.Repo CIRCLE_PROJECT_REPONAME
.PRNumber CIRCLE_PULL_REQUEST
.SHA1 CIRCLE_SHA1

Drone

プラットフォームの判別: DRONE の有無

パラメータ ソース
.Org DRONE_REPO_OWNER
.Repo DRONE_REPO_NAME
.PRNumber DRONE_PULL_REQUEST
.SHA1 DRONE_COMMIT_SHA1

GitHub Actions

プラットフォームの判別: GITHUB_ACTIONS の有無

パラメータ ソース
.Org GITHUB_REPOSITORY
.Repo GITHUB_REPOSITORY
.PRNumber GITHUB_EVENT_PATH
.SHA1 GITHUB_SHA1

exec のパラメータの Command と JoinCommand

実行したコマンドを示すパラメータとして Command と JoinCommand があります。 これらは似てますが、微妙に違います。

Command は exec.Cmd の .String() から取得されるのですが、コマンドが絶対パスになったりするので、あまり望ましくないこともあるでしょう。

例えば echo hello の場合 /usr/local/opt/coreutils/libexec/gnubin/echo foo となったりします。

一方 JoinCommand はコマンド文字列を単にスペースでつないだものになります。

exec.Cmd の .String() のドキュメントに書いてあるとおり、 .Command と .JoinCommand はそのまま shell で実行するのには適さない形式なので注意してください。

String returns a human-readable description of c. It is intended only for debugging. In particular, it is not suitable for use as input to a shell. The output of String may vary across Go releases.

設定ファイルのパス

設定ファイルのパスは --config -c オプションで指定できます。 何も指定しない場合、カレントディレクトリからルートディレクトリに向かって .github-comment.yml, .github-comment.yaml を探索し、最初に見つかったものを使います。

設定ファイルで .Org, .Repo を指定する

設定ファイルでコメント先のリポジトリを指定できます。 Platform で補完される場合や、明示的にパラメータで指定する場合は不要です。

base:
  org: suzuki-shunsuke
  repo: github-comment