📝

GitHub ActionsのCI/CDを華やかにするactions/github-scriptを使いこなす

2022/12/02に公開

イントロダクション

皆の者〜、おはこんハロチャオ〜!
medibaではバックエンド・インフラ・フロントエンド・SRE兼任で、OSSのコントリビュートもたまにしている雑用エンジニアの菅原です。

この記事は、mediba Advent Calendar 2022の3日目にエントリしています。前回のアドベントカレンダーもGitHub Actionsネタでしたが、今回も同じ系統のネタです。

さて皆さん、GitHub ActionsでCI/CDを作っていると、やりたいことがたくさん出てきませんか?
その度に目的に見合ったactionsを探すのは非効率ですし、メンテナンスがされない可能性のあるactionsを使うのもセキュリティ等の観点から懸念がありますよね。

そこで、皆さんに紹介したいのがactions/github-scriptactionです。
これさえあれば、GitHubに対してほぼすべての操作を行えます。
とはいえ、有能なactionにも関わらず、ググっても実用例がなかなか出てきません。
なので、この記事では、actions/github-scriptactionの実用例をご紹介します。

actions/github-scriptとは何者なんじゃ?

actions/github-scriptとは、簡単に説明すると、Actionsのワークフロー内でJavaScriptで記述できてGitHub APIをコールできるすごい奴です。

actions/github-scriptの実用例

プルリクエストがopenされたときにメンション付きでコメントする

name: Comment opening announce
on:
  pull_request:
    branches:
      - 'staging'
    types:
      - opened
jobs:
  comment_opening_announce:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
    steps:
      - name: Set up opening announce
        id: opening_announce
        run: |
          lf='\n'
          message="@${{ github.event.pull_request.user.login }} さん、デプロイプルリクエストの作成ありがとうございます! :tada:"
          message+="${lf}${lf}ステージング環境へのデプロイは下記の手順で行ってください。"
          message+="${lf}1. リリース予定の\`feature\`ブランチを\`release\`ブランチにマージします。"
          message+="${lf}1. \`ready-for-build\`ラベルを付けます。"
          message+="${lf}1. \`github-actions\`ボットのプルリクエストに対するアクションを待ちます。"
          message+="${lf}    - \`approve\`された場合、\`staging\`ブランチにマージします。"
          message+="${lf}    - \`ready-for-build\`ラベルが剥がされた場合、原因を特定して手順2からやり直してください。"
          message+="${lf}${lf}プルリクエストをマージすると、ステージング環境にデプロイが行われます。 :rocket:"
          message+="${lf}\`github-actions\`ボットから\`approve\`された後にプルリクエストを更新した場合は、"
          message+="${lf}\`ready-for-build\`ラベルを付ける手順からやり直してください。"
          echo "message=${message}" >> $GITHUB_OUTPUT
      - name: Comment opening announce
        uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const params = {
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.payload.pull_request.number,
              body: '${{ steps.opening_announce.outputs.message }}'
            }
            await github.rest.issues.createComment(params)
ワークフロー実行後イメージ

プルリクエストにコメントしてcloseする

name: Close pull request
on:
  pull_request:
    branches:
      - 'production'
    types:
      - opened
      - synchronize
      - reopened
jobs:
  close_pull_request:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
    steps:
      - name: Set up closed announce
        id: closed_announce
        run: |
          lf='\n'
          message="おや?ステージング環境にデプロイしていないようです :worried:"
          message+="${lf}商用環境へのデプロイプルリクエストは、ステージング環境で検証してから作成しましょう! :smiley:"
          echo "message=${message}" >> $GITHUB_OUTPUT
      - name: Comment closed announce
        uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const params = {
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.payload.pull_request.number,
              body: '${{ steps.closed_announce.outputs.message }}'
            }
            await github.rest.issues.createComment(params)
      - name: Closed pull request
        uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const params = {
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.payload.pull_request.number,
              state: 'closed',
            }
            await github.rest.pulls.update(params)
ワークフロー実行後イメージ

プルリクエスト更新時に特定のラベルを剥がす

name: Remove special label from pull request
on:
  pull_request:
    branches:
      - 'staging'
    types:
      - synchronize
jobs:
  remove_special_label:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
    steps:
      - name: Remove special label
        uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            for (const label of context.payload.pull_request.labels) {
              if (!label.name.startsWith("ready-for-build")) {
                continue
              }
              const params = {
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.payload.pull_request.number,
                name: label.name
              }
              await github.rest.issues.removeLabel(params)
            }
ワークフロー実行後イメージ

プルリクエストをApproveする

name: Approved pull request 
on:
  pull_request:
    branches:
      - 'staging'
    types:
      - synchronize
jobs:
  approved_pull_request:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
    steps:
      - name: Set up pull request approved comment
        id: pr_approved_comment
        run: |
          base_ref="${{ github.event.pull_request.base.ref }}"
          head_sha="${{ github.event.pull_request.head.sha }}"
          lf='\n'
          message="## :pushpin: 実行結果"
          message+="${lf}:white_check_mark: ビルドに成功しました。"
          message+="${lf}## :information_source: 詳細情報"
          message+="${lf}### :pencil: ビルドに使用したコミットハッシュ"
          message+="${lf}[${head_sha}](${base_ref}...${head_sha})"
          echo "message=${message}" >> $GITHUB_OUTPUT
      - name: Approved pull request
        uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const params = {
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.payload.pull_request.number,
              event: 'APPROVE',
              body: '${{ steps.pr_approved_comment.outputs.message }}'
            }
            await github.rest.pulls.createReview(params)
ワークフロー実行イメージ

プルリクエストにRequest Changesする

name: Request changes pull request 
on:
  pull_request:
    branches:
      - 'staging'
    types:
      - synchronize
jobs:
  approved_pull_request:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
    steps:
      - name: Set up request changes comment
        id: request_changes_comment
        run: |
          lf='\n'
          message="プルリクエストの内容が更新されたので、特殊なラベルをすべて外しました! :smile:"
          message+="${lf}\`ready-for-build\`ラベルを付け直してください。 :pray:"
          echo "message=${message}" >> $GITHUB_OUTPUT
      - name: Change requested special label
        uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const params = {
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.payload.pull_request.number,
              event: 'REQUEST_CHANGES',
              body: '${{ steps.request_changes_comment.outputs.message }}'
            }
            await github.rest.pulls.createReview(params)
ワークフロー実行イメージ

参考になるサイト

actions/github-scriptaction 関連

  • マーケットプレイスのページ
  • ソースコードがあるページ
  • actions/github-scriptactionが使っているGitHub APIライブラリのリファレンス

GitHub Actions 関連

  • Actionsで使うgithub.event.*actions/github-scriptで使うcontext.payload.*の内容を調べたいときのリファレンス
  • bashがきちんと動くか確認するとき(ただし、一部のコマンドを除く)のテストサイト

最後に

mediba ではエンジニア職を含めて幅広い業種を絶賛募集中です。
OSS コントリビューターと一緒に働きたい方からの沢山の応募お待ちしております。

明日の担当

4日目は、私と同じバックエンドエンジニアの井上さんです。
今年のやらかしの供養という面白そうな記事なので、みなさんも見てみてくださいね。

Discussion