この記事は GitHub Actions Advent Calendar 2021 8日目の記事です。

GitHub をお使いの皆さん、GitHub Actions を使っているでしょうか。

  • 開発中のコードフォーマットチェックやテストを自動化する
  • 特定のタイミングでリリースを行う
  • 一定期間ごとに処理する
  • 特定ブランチに変更が push された場合のみ処理する
  • 特定パスの変更時のみ処理する

など、色々できてとても便利ですよね。

とはいえ、やれることが多い分設定ファイルはカオスになりやすいです。
それを解決するために処理をひとまとめにした action という仕組みがあり、公開されている action を組み合わせて使うことで設定ファイルをスッキリさせることができます。
管理しているリポジトリが多数ある場合でも同じように利用できることもメリットですね。
今回は、私が業務・プライベート問わずよく利用している action を紹介します。

  • 依存モジュールをキャッシュしたい
  • 特定のサーバに ssh 接続したい
  • terraform plan した結果を PR に付与したい
  • 結果を Slack へ通知したい

1つずつ順に紹介します。

依存モジュールをキャッシュしたい

フォーマットチェックや test を用意して、日々コードの品質を担保することは大切です。
これは人力でやらずとも、GitHub Actions 上で実行することで修正漏れや属人化を防げるわけですが、同時に GitHub Actions の実行時間についても気になるところではあります。

  • プライベートリポジトリの場合一定時間を超えると有料になってしまうこと
  • 「特定ジョブをパスした場合のみメインブランチにマージできるようにする」といった運用をしているが故に GitHub Actions の完了待ちになってしまうこと

上記のような問題を解決する方法はいくつかありますが、今回は依存モジュールをキャッシュ化することで時間短縮させる方法についてご紹介します。

actions/cache を利用します。
どんな言語でコードを書くにしても、大抵はパッケージ管理システムなどを利用して依存ライブラリを管理しているのではと思います。
以下は php と composer の例ですが、言語やツールが変わっても基本的なやり方は同じです。

on: push # Push 時にワークフローを実行
jobs:
  use_action:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Cache modules
        uses: actions/cache@v2
        id: vendor_cache # このタスクの処理結果を後タスクで参照できるよう、処理結果を格納する先を指定する
        with:
          path: vendor
          key: ${{ runner.OS }}-vendor-${{ hashFiles('**/composer.lock') }} # キャッシュの識別子を指定
      - name: Install dependencies
        if: ${{ steps.vendor_cache.outputs.cache-hit != 'true' }} # 前タスクで格納した結果を参照して実行するかを判断する
        run: composer install

依存モジュールに変更がなく、GitHub Actions 上でキャッシュされている場合、このワークフローが実行されると Install dependencies はスキップされます。

キャッシュはホストランナー上に格納されるため、複数人の開発時などに複数のワークフローが並走した場合に、

  • Aさんの作業ブランチで実行したワークフローでキャッシュされた結果が
  • Bさんの作業ブランチで実行したワークフローで参照できる

といった流れでもキャッシュが利用できます。

注意点として、キャッシュするファイルのキーには lock ファイルのような 「依存関係が変わると差分が出るもの」 を指定する必要があります。
上記の例だと lock ファイルのハッシュ値を使うことでこれを実現しています。
また、ホストランナーが変わるとキャッシュも破棄されるので、久しぶりにコードを修正してワークフローを動かした場合などもキャッシュにはヒットしません。

特定のサーバに ssh 接続したい

「特定ブランチへマージされた場合、自動で特定環境へデプロイしたい」というケースは多いと思います。
昨今はコンテナが使われることも多いのであまり出番はないかも知れませんが、オンプレミスで自社サーバを運用していたり、AWS の EC2 のようにクラウド上でサーバを運用しているケースはまだまだあります。
そういった場合に役立つのが shimataro/ssh-key-action です。

on:
  push:
    branches:
      - main # main ブランチへの Push 時にワークフローを実行
jobs:
  connect_ssh:
    runs-on: ubuntu-latest
    steps:
      - name: Install SSH Key
        uses: shimataro/ssh-key-action@v2
        with:
          key: ${{ secrets.SECRET_KEY }} # サーバへの接続情報は secrets へ保持するようにしています
          name: id_rsa-target
          known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }}
          # 以下は ~/.ssh/config と同じフォーマット
          config: |
            Host *
              StrictHostKeyChecking no
              UserKnownHostsFile=/dev/null
            Host target
              HostName ${{ secrets.SSH_HOST }}
              User ${{ secrets.SSH_USER }}
              Port ${{ secrets.SSH_PORT }}
              IdentityFile ~/.ssh/id_rsa-target
      - name: Connect SSH
        run: ssh target echo "test"

SSH するにあたり必要な接続情報は secrets に設定しているものとしています。
上記の例だと、サーバに ssh した後サーバ上で echo "test" が実行されます。

terraform plan した結果を PR に付与したい

Terraform、便利ですよね。
何それ?という方は以前書いた記事を見ていただけると幸いです。

Terraform は IaC (Infrastructure as Code) ツールで、システムインフラの構成管理をコードベースで行えます。
手順は大きく3つあります。

  1. コードを修正する
  2. terraform plan で差分を確認する
  3. terraform apply で変更を反映する

上記2の手順を GitHub Actions で実行し、出力された差分を Pull Request に貼り付けてくれる action が terraform-pr-commenter です(実は私もひっそり contribute してます)。

on: pull_request # Pull Request 作成時にワークフローを実行
jobs:
  terraform:
    name: Terraform
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Terraform Plan
        run: terraform plan
      - name: Post Plan
        uses: robburger/terraform-pr-commenter@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          commenter_type: plan
          commenter_input: ${{ format('{0}{1}', steps.plan.outputs.stdout, steps.plan.outputs.stderr) }}
          commenter_exitcode: ${{ steps.plan.outputs.exitcode }}

前セクション同様、GITHUB_TOKEN については secrets に設定しているものとします(発行方法についてはこちらをご覧ください)。
Pull Request 作成時に terraform plan した結果がコメントされるようになります。

結果を Slack へ通知したい

ワークフローが終了した段階で結果を Slack に通知したい、そういうケースは多いのではと思います(まさかブラウザ上でワークフローが終わるのを見つめ続ける…なんてことはしないでしょうし)。
CircleCI だと webhook url を設定するだけでこれができたのですが(今は Orbs を使うやり方に変わっているので手順は少し増えましたが)、GitHub Actions だと標準ではできないようです。
これを解決するのが 8398a7/action-slack です。
GitHub Actions は複数ジョブを並走させることもできますが、その場合上記 action だけではうまくステータスを判断できないため、workflow-conclusion-action と合わせて利用します。

on: push # Push 時にワークフローを実行
jobs:
  # 成功するジョブ
  test_success:
    runs-on: ubuntu-latest
    steps:
      - name: test
        run: exit 0
  # 失敗するジョブ
  test_failed:
    runs-on: ubuntu-latest
    steps:
      - name: test
        run: exit 1
  notify:
    if: ${{ always() }} # 前のジョブが失敗した場合でも発火させたいので always() としている
    needs:
      - test_success
      - test_failed
    runs-on: ubuntu-latest
    steps:
      - uses: technote-space/workflow-conclusion-action@v1
      - name: Notify to Slack
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ env.WORKFLOW_CONCLUSION }} # 最終ステータス(複数ジョブが実行されていた場合、1件でもキャンセルや失敗したジョブがあればそれが採用される)
          author_name: GitHub Actions
          fields: message,ref # 必要なフィールド
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GitHub のトークンを設定
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # Slack の Webhook URL

最後に

いかがだったでしょうか。
すでにご存知のものもあったかもしれません。
今回あげたもの以外にも便利なもの、業務で利用できるものはたくさんあるので、気になった方はぜひ GitHub Marketplace を調べてみてください。