GitHub Actionsでワークフローを効率化してハッピーになろう

これは[Hubble Advent Calendar 2023](https://qiita.com/advent-calendar/2023/hubble)の14日目[^1]の記事です。

# はじめに
再びこんにちは！株式会社Hubbleでバックエンドのエンジニアをしている@okakeeです。
Hubble Advent Calendar 2023では[4日目の記事](https://qiita.com/taamoo2/items/cff36ba7a6e4ca46f4df)ぶりの2回目の投稿となります。

私たちが作っている[Hubble](https://hubble-docs.com/)というサービスは、契約書をGitのように自動でバージョン管理したい！という着想から始まりました。

そんな開発には欠かせないGitと代表的なホスティングサービスであるGitHub。
GitHubを有効的に使う手段として、GitHub Actionsによるワークフローの自動化は欠かせないです。

今回はHubbleで実際に使っているGitHub ActionsのWorkflowスクリプトの中から3つほど紹介したいと思います！

# Workflow 3選

## Hotfix PRのオートマージ
```yaml
name: Hotfix PR auto merge to staging and develop

on:
  pull_request:
    branches:
      - main
    types: [closed]

jobs:
  create_pr:
    runs-on: ubuntu-latest
    if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'hotfix/')
    steps:
      - name: Checkout Main Branch
        uses: actions/checkout@v3
        with:
          ref: main
          fetch-depth: 0

      - name: Generate token
        id: generate-token
        uses: tibdex/github-app-token@v1
        with:
          app_id: ${{ secrets.PR_APPROVER_APP_ID }}
          private_key: ${{ secrets.PR_APPROVER_PRIVATE_KEY }}

      - name: Create Master to Staging Pull Request
        id: create-master-to-staging-pull-request
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: PULL_REQUEST_URI=$(gh pr create -B staging -t "Master -> Staging" -b "PR created by GithubActions")

      - name: Approve Master to Staging Pull Request
        env:
          GH_TOKEN: ${{ steps.generate-token.outputs.token }}
        run: gh pr review ${{steps.create-master-to-staging-pull-request.outputs.PULL_REQUEST_URI}} --approve

      - name: Merge Master to Staging Pull Request
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: gh pr merge ${{steps.create-master-to-staging-pull-request.outputs.PULL_REQUEST_URI}} --merge --auto

      # これ以降は同じ手順をStaging to Developで繰り返すので割愛
```
### 解説
HubbleのBackendリポジトリではブランチ戦略として主に[Git-flow](https://dev.classmethod.jp/articles/introduce-git-flow/)でマルチステージ（Main, Staging, Develop）を採用しています。

（ちなみにFrontendチームではBranch戦略をGit-flowからトランクベースに変更しており、その話を今回のAdvent Calendarで @KOHETs さんが紹介しているので、気になる方はチェックしてみてください！）

https://qiita.com/KOHETs/items/58f087cad53c231f7198

緊急の修正対応でhotfixブランチをmainブランチにマージした際に、内容をStaging, Developに降ろしていく必要があります。
`hotfix/`から始まる名前のブランチがmainブランチにマージされた時に実行されるようにしています。
```yaml
if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'hotfix/')
```

コード以外のポイントとして、GitHub Appsで事前にPRをApproveするためのGitHub Appを用意しておくことと、リポジトリの設定でauto-mergeを有効化しておくことでApproveが必須の場合でも強制マージではなく、自然な流れでPRをマージすることが可能です。

またリポジトリのBranch rulesから`Require status checks to pass before merging`を設定しておくと、予め必須のステータスチェックを指定することができます。

改善点としては、コンフリクトが起きた場合や既にブランチ間のPRが存在する場合はエラーになるので、`failure()`したときに[rtCamp/action-slack-notify](https://github.com/rtCamp/action-slack-notify)などを利用してSlackに通知を流したりすると落ちたときに気づくことができるので使いやすいと思います。


## PRの自動アサイン
```yaml
name: PR Auto Assignment

on:
  pull_request:
    types: [opened]

jobs:
  Assignees:
    if: ${{ github.actor != 'dependabot[bot]' }}
    runs-on: ubuntu-latest
    timeout-minutes: 1
    steps:
      - uses: actions/checkout@v3
      - uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            github.rest.issues.addAssignees({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              assignees: `${{ github.actor }}`
            });
```

### 解説
PRを作成した際にAssigneesにPR作成者を自動で入れるスクリプトです。

このworkflowを入れるまでチームではAssigneesについてのルールはなかったので開発者によって入れる、入れないがバラバラの状態でしたが、Assigneesを入れておくとPRの一覧で絞り込みが容易で、統計にも使えるので全員に設定してほしいという思いがありました。

ただAssigneesを設定するだけスクリプトですが、そこをスムーズにクリアできたのがポイントです。

## rubocopのオートフォーマット
```yaml
formatter:
  name: formatter
  runs-on: ubuntu-latest
  steps:
    - name: Checkout
      uses: actions/checkout@v3
    - name: Set up Ruby
      uses: ruby/setup-ruby@v1
      with:
        ruby-version: '3.2.2'
    - name: Install dependencies
      run: bundle install
    - name: rubocop
      run: rubocop -a
    - name: Commit
      uses: stefanzweifel/git-auto-commit-action@v3.0.0
      with:
        commit_message: Apply Code Formatter Change
        ref: ${{ github.head_ref }}
```

### 解説
これはtestのjob内に含まれているものですが、rubocopのSafe auto-collect(`rubcoop -a`)を実行してコミットするようにしています。
もちろん別でrubocopの実行テスト自体は組み込んでいますが、事前のタイミングで実行することで手元での実行を忘れていたときのカバーになります。

# 最後に
今回紹介したスクリプトに共通するのは、**毎回発生していた作業を自動化する**というまさにワークフローの効率化に対してのアプローチとして有効だったものです。

小さい作業であっても毎回発生するものや、チーム内で共通させたいものは、規約的にルールを設定するよりも自動化によって浸透させたほうが効率的なので、まずは自分のチームで何を改善したいのか、どこが開発のスピードを下げる要因になっているのかを探ることが重要だと思います。

GitHub Actionsを使った自動化はその一部ですが、Hubbleでは開発体験を向上させる取り組みや仕組みづくりを目指しています！

明日は @ktkr-wks さんによる投稿です！

[^1]: 平日のみの投稿なので、投稿日は20日ですが14日目の記事としています。