GitHub Actionsで日々の開発運用を楽にする取り組みの紹介

この記事はfreee Developers Advent Calendar 2021 15日目の記事です。

中部支社のチームのエンジニアの okoshi です。

みなさんいかがお過ごしですか。めっきり寒くなってまいりましたね。外部イベントでLTするときは下手うまな絵をスライドに差し込むのが好きなんですが、最近LTに出てないので所属しているチームのメンバーを絵にしてみました。

みそカツメンバー
自分の所属しているチームメンバーイメージ図

いつ書いても惚れ惚れする絵のタッチ。

それはさておき

今回は中部支社のエンジニアが裁量を以て仕事をしていることの一例としてGitHub Actionsで日々の開発運用を楽にしている取り組みを紹介します。

この記事に辿り着いていただいた方はGitHub Actionsに関する記事を期待している方も多いと思うので後半ではどういった設定をしたかも紹介します。

そして、こちらで紹介する内容はチームのメンバー全員で取り組んだものなので、尽力いただいた方々に心から敬意と感謝の意を表します。

中部支社は独立したGitHubリポジトリーを運用している

freeeでは本社に限らず支社でも開発を行っています。もちろん独立した機能の開発を担当させてもらっていて、我々のチームではGitHubで独立したリポジトリをもっています。

エンジニアが触るリポジトリなので、当然プログラムが格納されています。そうなると、当然のごとくCIをしたくなるわけです。

そこでGitHub Actionsですよ。

GitHub Actionsを選んだのは必然的だった

CIツールにはCircleCIだとかTravisCIだとか他にも使えるツールはありますが、我々はGitHub Actionsを選びました。

我々の開発のミッションの一つに、すばやい開発の実現をかかげています。 そのため、調達に時間がかかるものはできるだけ排除したいと考えており、CircleCIやTravisCIはそもそも検討することなく手元ですぐに使えるツールとしてGitHub Actionsを選択しました。

選ぶにあったっては学習コストも意識しましたが、以下の二点から問題ないと考えました。

  • 社内に知見が多くあった安心感
  • メンバーが知見を持っていた

例えば私、過去にGitHub Actionsに関する記事をQiitaに書いていたりして、実はGitHub Actionsが好きだったりします。

GitHub Actions+Firebase App DistributionでiOSアプリをAd hoc配布するための構成例 - Qiita

Firebase hosting preview channels

ワークフローが解決した課題

我々は日々、テストを行っています。静的コードチェックも行っています。しかし、これらを手で行うのは面倒です。 今時それらをCIで実現するのは当たり前だと思いますが、それらもGitHub Actionsで実現しています。

加えて、DependabotによるOSSのバージョンアップ通知のプルリクエスト対応があります。OSSのバージョンアップには大規模な修正もあれば小さな修正もあります。 新しいバージョンがあれば適用したいと思ってはいるものの、大きな修正が加わっている場合は破壊的変更が加わっている可能性があるので慎重にバージョンアップをしなければなりません。しかし我々の使っているOSSは毎日のように軽微な修正が入り、Dependabotからのプルリクエストが発生し適用しなければならないのはかなりの手間です。 そこで、パッチバージョンが上がっただけのプルリクエストは自動でマージしてしまうことにしました。

改めて、GitHub Actionsで実現していることは以下になります。

  • 単体・結合テスト
  • 静的コードチェック
  • E2Eテスト
  • Dependabotの作成するPRの自動マージ

我々が扱っているリポジトリーはフロントエンドに特化しています。

GitHub Actionsはプルリクエストの作成や更新に対して反応するように設定してあります。

ワークフローのジョブは以下のように動かしています。

GitHub Actionsのワークフロー。prepareのあと、tests, E2E, dangerを実行する。tests, E2Eのあとで、dependabotを実行する。

それぞれのジョブがやっていることは後述しますが、ちょっと複雑に見えるかもしれませんがこのワークフローは、日々の開発運用で作られるプルリクエストとDependabotが作るプルリクエストのどちらにも反応するようになっています。

人が作るプルリクエストには、tests、E2E、dangerが反応し、Dependabotが作るプルリクエストには、tests、E2E、dependabotが反応します。dependabotはtestsとE2Eが成功した場合にのみ動作します。

ワークフローの設定ファイル

せっかくこの記事に辿り着いていただいたので、どういった設定をワークフローのyamlファイルに書いたかを可能な範囲で公開します。 一部にはなりますが、十分に参考になると思います。

prepare

yarn installをした結果をキャッシュしています。キャッシュは後続のジョブと、次回のワークフローの実行に使用します。

  prepare:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: $NODE_VERSION
      - name: Install Yarn
        run: npm install -g yarn@$YARN_VERSION
      - name: Cache node_modules
        id: node_modules_cache_id
        uses: actions/cache@v2
        with:
          path: |
            node_modules
            src/*/node_modules
            /home/runner/.cache/Cypress
          key: node-v$NODE_VERSION-v1-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
      - name: Install npm modules
        run: yarn install
        if: steps.node_modules_cache_id.outputs.cache-hit != 'true'

tests

単体・結合テストとLintを使った静的コードチェックを行います。

  tests:
    runs-on: ubuntu-latest
    needs:
      - prepare
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: $NODE_VERSION
      - name: Cache node_modules
        id: node_modules_cache_id
        uses: actions/cache@v2
        with:
          path: |
            node_modules
            src/*/node_modules
            /home/runner/.cache/Cypress
          key: node-v$NODE_VERSION-v1-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
      - name: eslint
        run: yarn lint
      - name: Test
        run: yarn test

E2E

Cypressを使ったE2Eテストを行います。 yarn startではアプリが実行されるようになっています。

  E2E:
    runs-on: ubuntu-latest
    needs:
      - prepare
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: $NODE_VERSION
      - name: Cache node_modules
        id: node_modules_cache_id
        uses: actions/cache@v2
        with:
          path: |
            node_modules
            src/*/node_modules
            /home/runner/.cache/Cypress
          key: node-v$NODE_VERSION-v1-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
      - uses: cypress-io/github-action@v2
        with:
          working-directory: src/foo
          start: yarn start
          wait-on: 'http://localhost:3000'
          headless: true
          install: false

dependabot

Dependabotの作成するPRのうちパッチバージョンが上がっただけのものだけマージを行います。

我々の管理しているリポジトリのプルリクエストでは、レビューアがApproveしないとマージできないようになっています。

ジョブの定義にgh pr merge --auto --mergeがあるのは、Approveしたときに自動でマージするようにするためです。

  dependabot:
    runs-on: ubuntu-latest
    needs:
      - tests
      - E2E
    if: ${{ github.actor == 'dependabot[bot]' && contains(needs.*.result, 'success') && !(contains(needs.*.result, 'failure')) }}
    steps:
      - name: Dependabot metadata
        id: metadata
        uses: dependabot/fetch-metadata@v1.1.1
        with:
          github-token: '${{ secrets.GITHUB_TOKEN }}'
      - name: Enable auto-merge for Dependabot PRs
        if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-patch' }}
        run: gh pr merge --auto --merge "$PR_URL"
        env:
          PR_URL: ${{github.event.pull_request.html_url}}
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
      - uses: actions/checkout@v2
      - name: Approve for Dependabot PRs
        if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-patch' }}
        run: gh pr review $NUMBER --approve
        env:
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
          NUMBER: ${{ github.event.number }}

danger

Dangerを使った静的コードチェックを行います。 Dependabotの作ったプルリクエストには不要と考えて、Dependabotが作成したプルリクエストに対しては実行しません。

uses: danger/danger-js@x.x.xを使うこともできますが、こちらはDockerコンテナのビルドを行うため多少時間がかかります。せっかくyarn install済みのキャッシュがあるのでyarn danger ciを実行しています。

  danger:
    runs-on: ubuntu-latest
    needs:
      - prepare
    if: ${{ github.actor != 'dependabot[bot]' }}
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: $NODE_VERSION
      - name: Cache node_modules
        id: node_modules_cache_id
        uses: actions/cache@v2
        with:
          path: |
            node_modules
            src/*/node_modules
            /home/runner/.cache/Cypress
          key: node-v$NODE_VERSION-v1-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
      - name: Run danger
        run: yarn danger ci
        env:
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

まとめ

今回は裁量を持って開発環境が作れているということの一例を紹介させていただきました。この他にも中部支社では、社内初のアーキテクチャの採用するといったことも行っており、支社にも高いスキルとモチベーションを持ったメンバーがいます。

みそカツメンバー
自分の所属しているチームメンバーイメージ図(再掲)

freeeは中部支社でも本社に劣らない仕事ができます。面白い開発が待っています。是非freeeの中部支社も視野にいれて転職や就職を検討してください。待ってます!

明日のAdvent Calendarは、Tak H. さんです!お楽しみに!