🔍

ブランチの作成者を追跡するための GitHub Actions ワークフロー

5 min read

課題

誰がブランチやタグを作成(プッシュ)したのか知りたいときってありますよね?
ですが Git ではコミット単位でしか Author が管理されないためブランチやタグをプッシュしたユーザーを調べる方法はありません

解決策

GitHub で push イベントが発生したときに各ブランチ初回の Pusher 情報を DB へ保存します。
専用の DB を用意するのは手間なので Git リポジトリを DB 代わりにします。
Gist でも良かったのですが actions/checkout が Gist に対応していなかったのでやめました。

ワークフローを実行するリポジトリとは別の空リポジトリを用意するか、ワークフローを実行するリポジトリを使用します。
後者は履歴に残りますので注意してください。
Personal Access Token を発行しなくて済むメリットはあります。
今回のサンプルコードは後者で実装します。

Gist を使う場合

Gist を使う場合

README.md だけ追加した Gist を用意してクローン URL を取得します。
[Embed] のプルダウンから [Clone via HTTPS] または [Clone via SSH] を選択することで URL をコピーできます。

Personal Access Token も発行してワークフローを実行するリポジトリの [Settings] > [Secrets] に PAT で登録しておきます。

ワークフロー内で actions/checkout にあたる処理を自前で書きましょう。

GitHub Actions ワークフロー

まずは全体のワークフローです。説明は後述します。
実際の動作は SnowCait/branch-authors-example をご参照ください。

.github/workflows/branch-authors.yml
name: Branch authors

on:
  push:
  delete:

jobs:
  push:
    runs-on: ubuntu-20.04
    if: github.event_name == 'push'

    steps:
      - run: cat $GITHUB_EVENT_PATH

      - uses: actions/checkout@v2
        with:
          ref: main # DB 代わりにするブランチ

      - name: Config
        run: |
          git config user.name github-actions
          git config user.email github-actions@github.com

      - name: Pusher
        run: |
          if [ ! -d $REF ]
          then
            mkdir -p $REF
            echo $AUTHOR >> ${REF}/author
            git add .
            git commit -m "${REF} is pushed"
          fi
        env:
          REF: ${{ github.event.ref }}
          AUTHOR: ${{ github.event.pusher.name }}

      - name: Push
        run: git push

  delete:
    runs-on: ubuntu-20.04
    if: github.event_name == 'delete'
    
    steps:
      - run: cat $GITHUB_EVENT_PATH

      - uses: actions/checkout@v2
        with:
          ref: main # DB 代わりにするブランチ

      - name: Config
        run: |
          git config user.name github-actions
          git config user.email github-actions@github.com

      - name: Delete branch
        run: |
          if [ -d $REF ]
          then
            git rm -r $REF
            git commit -m "${REF} is deleted"
          fi
        env:
          REF: refs/heads/${{ github.event.ref }}
        if: github.event.ref_type == 'branch'

      - name: Delete tag
        run: |
          if [ -d $REF ]
          then
            git rm -r $REF
            git commit -m "${REF} is deleted"
          fi
        env:
          REF: refs/tags/${{ github.event.ref }}
        if: github.event.ref_type == 'tag'

      - name: Push
        run: git push

共通

pushdelete でワークフローを分けても良いのですが関連付けが分かりにくくなるため1つのファイルで管理し jobs.<job_id>.if で実行を制御しています。

別のリポジトリを使う場合は actions/checkoutrepositorytoken を追加します。
Personal Access Token も発行してワークフローを実行するリポジトリの [Settings] > [Secrets] に PAT で登録しておいてください。

      - uses: actions/checkout@v2
        with:
          ref: main # DB 代わりにするブランチ
          repository: SnowCait/branch-authors
          token: ${{ secrets.PAT }}

push

主要部分だけ抜粋
      - name: Pusher
        run: |
          if [ ! -d $REF ]
          then
            mkdir -p $REF
            echo $AUTHOR >> ${REF}/author
            git add .
            git commit -m "${REF} is pushed"
          fi
        env:
          REF: ${{ github.event.ref }}
          AUTHOR: ${{ github.event.pusher.name }}

github.event.pusher.name の情報を DB に記録します。
今回は Git リポジトリなので ref でディレクトリ階層を作って author というファイルに出力しています。
初回だけ記録できれば良いので既にディレクトリがある場合はスキップします。

github.event.pusher.name の代わりに github.event.sender.login を使っても良いと思うのですが違いはよく分かっていません。
ご存知の方がいましたらコメントにでも書いておいていただけると助かります。

プッシュの履歴を見たい場合は if [ ! -d $REF ] を外して常に追記します。

ドキュメント: イベント, ペイロード

delete

同名ブランチ/タグが作成されることもあるので削除されたときには DB からも消しておきます。

主要部分だけ抜粋
      - name: Delete branch
        run: |
          if [ -d $REF ]
          then
            git rm -r $REF
            git commit -m "${REF} is deleted"
          fi
        env:
          REF: refs/heads/${{ github.event.ref }}
        if: github.event.ref_type == 'branch'

push で作られたディレクトリを削除します。
途中から導入したりすることを考えて if [ -d $REF ] で囲ってあります。

push イベントと異なり完全な ref 情報がないため ref_type で分岐、生成しています。

ドキュメント: イベント, ペイロード

まとめ

出来そうで出来なかったことの1つを解決できたのではないかと思います。
一覧性を重視するなら1つの CSV ファイルにまとめるといいかもしれませんが少し面倒くさそう。