GitHub Actionsにて、Jenkinsでできるような処理にかかった時間や反映されたコミット一覧も取得してSlackに通知したい

# 対象ユーザー

JenkinsでSlack Notification Pluginを利用しているユーザーがGithub Actionsに乗り換える場合
かつ、限りなくJenkinsからの通知に似せたい場合


## Bitbucet + Jenkins から Github + Github Actionsに移行した

今まで開発していたプロジェクトはすべてBitbucetで管理され、Webhooksを利用してJenkinsからタスクを発動してた。
これは過去、Github（の無料版）のプライベートリポジトリや、コラボレーターの数に制限があったからで、これがいつの間にかなくなっていた模様。
逆にBitbucetの5人制限がプロジェクトによってはキツくなってきた。
また、Github Actionsも無料版で2000分/月と普段遣いでは十分な枠があるため、思い切って移行作業をしてみた。（BitbucketのPipelineは50分/月しかない）

## プロジェクト（ソース）の移行自体は全く問題なかったが・・・

とりあえずソースの移行は

1. BitbucketにGithubからアクセスできるトークンを発行
    1.  [設定 → Personal Bitbucket settings → アプリ パスワード](https://bitbucket.org/account/settings/app-passwords/)
    1. アプリパスワードの作成
    1. リポジトリの読み込み権限にチェックをして発行
    1. トークンを控える
1. Githubからインポート
    1. 上部メニュー「＋」から [Import Repository](https://github.com/new/import)
    1. Bitbucekの情報を入力
        - **The URL for your source repository:** https://bitbucket.org/owner/project_name/
        - **Your username for your source repository:** Bitbucketのユーザー名
        - **Your access token or password for your source repository:** 発行したトークン
        - **Your new repository details:** 適切なオーナー及び新しいリポジトリ名、Private（デフォルトがPublicなので注意）
1. しばらく待てば完了

以上、全く困ることはなかった

## JenkinsからGithub Actionsへの以降

これはもう頑張るしか無い。
ただ、ビルドしてSSHで転送程度であれば探すといくらでも出てくる。
ついでにSlack通知も簡単に出てくるので割愛。

## Slack Notification Pluginでできていた機能が再現できない

簡単に検索して設置し、無事に動作したものの、Jenkinsからの通知で便利だった機能が2つほど不足していた

- 処理にかかった時間が取得できない
- 反映されたコミットログが取得できない（最終コミットだけは標準で確認できる）
    - ただし、Jenkinsは失敗→成功の場合はログを吐かない（失敗の方にログが出る）

どちらもJenkinsからは標準で吐き出されるものである。
実行ログへアクセスすればすぐに分かるのだが、できればSlack上で知りたい。
特に成功した場合、自分が実行したものでなければ気づかない可能性が高いため、欲しい機能である

## 処理にかかった時間を取得してみる

「github.xxxx」にはなかったので自前でタイムスタンプを発行して計算する必要があった

```yml

- name: 処理開始時間を記録
  id: start_time
  run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT

# ビルド, デプロイ処理

- name: 処理終了時間を記録
  id: duration
  run: |
    end_time=$(date +%s)
    start_time=${{ steps.start_time.outputs.start_time }}
    duration=$((end_time - start_time))
    echo "duration=${duration}" >> $GITHUB_OUTPUT
 
 # これで steps.duration.outputs.duration から処理にかかった秒数を出力できる

- name: Slack通知例
  uses: rtCamp/action-slack-notify@master
  env:
    SLACK_TITLE: デプロイが完了しました
    SLACK_COLOR: good
    SLACK_MESSAGE: |
      経過時間: ${{ steps.duration.outputs.duration }}秒
```

### 注意点

見ても分かる通り、Github Actionsの実行時間ではなく、記録開始から記録終了までの時間になるため、stepの最初に書いたとしてもサーバーを立ち上げる時間は含まれない。

## 反映されたコミットの一覧を取得する

これもなかなか難しく、最後に成功したワークフローの最終コミットハッシュをAPIを通じて取得し ([仕様](https://docs.github.com/ja/rest/actions/workflow-runs?apiVersion=2022-11-28#list-workflow-runs-for-a-repository))、`git log` コマンドの実行結果を変数に入れる必要があった。
`git log` コマンドもいい感じにフォーマットしないと無闇に長くなってしまうので注意。
また、改行を含む変数を `$GITHUB_OUTPUT` に格納するためにも工夫が必要だった。

```yml

# 冒頭にパーミッション設定を追記（ActionsのパーミッションがWriteの場合は不要）
permissions:
  contents: read
  actions: read

# APIを利用して最後に成功したワークフローの最終コミットのハッシュを取得する
# 初回実行やログが見つからないときは null が返ってくる
# API実行部分の「production.yml」が自身のファイル名になる
- name: 最後に成功したワークフローのコミットSHAを取得
  id: last_successful
  run: |
    LAST_SUCCESS=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
    "https://api.github.com/repos/${{ github.event.repository.owner.name }}/${{ github.event.repository.name }}/actions/workflows/production.yml/runs?status=success&per_page=1" | jq -r '.workflow_runs[0].head_sha')

    if [ "$LAST_SUCCESS" = "null" ] || [ -z "$LAST_SUCCESS" ]; then
    LAST_SUCCESS="null"
    fi

    echo "last_success_sha=${LAST_SUCCESS}" >> $GITHUB_OUTPUT

# last_successfulから今回のコミットまでの変更点をdiff.messaageに記録
# 前回成功したコミットSHAがない場合は、初回実行とだけ格納
- name: 変更点を表示
  id: diff
  run: |
    if [ "${{ steps.last_successful.outputs.last_success_sha }}" = "null" ]; then
      DIFF_MESSAGE="初回実行"
    else
      # ハッシュ: コミット内容（ユーザー）のフォーマットで1行ずつにする
      # また、マージコミットは含めない
      DIFF_MESSAGE=$(git log --no-merges --pretty=format:" %h: %s (%an)" ${{ steps.last_successful.outputs.last_success_sha }}..${{ github.sha }})
    fi

    # 改行を含む変数を格納するためにヒアドキュメントにする
    echo "commits<<EOF" >> $GITHUB_OUTPUT
    echo "$DIFF_MESSAGE" >> $GITHUB_OUTPUT
    echo "EOF" >> $GITHUB_OUTPUT

 # これで steps.diff.outputs.commits からコミットログを出力できる
 
- name: Slack通知例
  uses: rtCamp/action-slack-notify@master
  env:
    SLACK_TITLE: デプロイが完了しました
    SLACK_COLOR: good
    SLACK_MESSAGE: |
      対象コミット一覧:
      ${{ steps.diff.outputs.commits }}
```

## 結果

コミットメッセージがダサいのは気にしない（Github Copilotが勝手に作った）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104999/7cb51a95-c4c0-4c45-af7a-45d8eda021d1.png)

これで大分運用が楽になった。
なお、これらを作るに当たり Github Copilotはほとんど役に立たなかった。
というか普通にGoogle検索でも見つからなかった。
やってる人殆どいないのか。
みんなあんまり気にならないの？