💭

コピペでも使える!? GitHub Actionsのワークフロー

2024/10/15に公開

はじめに

こんにちは。フロントエンドエンジニア 2 年生の柿です。
今回は GitHub Actions を使ってすぐに品質管理の CI/CD を行うためのワークフローをご紹介しようと思います。
(CI/CD と言いたくなりますよね、なんとなく響きがいいですし!)

GitHub Actions とは

そもそも GitHub Actions とは何なんでしょうか?
私はカッコつけるためだけに認識していた程度です。

柿「あー、あれね?イケてるよね」

ここで改めて知っておきましょう。

GitHub Actions とは、GitHub が提供する自動化ツールです。
例えば、ビルドやテスト、デプロイなどの作業を自動で実行することができます。

GitHub Actions は何をするのか

GitHub Actions は、特定のイベントが発生したときに特定のアクションを実行することができます。
例えば、プルリクエストを作成したら、GitHub Actions が「よし、テストしとくぜ!」とばかりに動き出します。

特定のイベント

特定のイベントとは、例えば以下のようなものです。

  • プルリクエストの作成
  • プッシュ
  • ラベル付与

などです。

特定のアクション

特定のアクションとは、例えば以下のようなものです。

  • テストの実行
  • ビルド
  • 自動デプロイ
  • 静的解析

など、日常的な作業を代わりにやってくれる頼れるやつです。

これらのアクションを組み合わせることで、以下のようなことが実現できます。

  • テストの自動実行: プルリクエストが作成された瞬間に、テストが自動で実行されます。手元でpnpm test:uiとしていたのが懐かしいですね。
  • 静的解析: プルリクエストを作成したときに、ESLint や Prettier があなたのコードをしっかりチェック。「このコード、ちょっと整ってないですよ?」とやんわり指摘してくれます。
  • 自動デプロイ: "main" もしくは "master" ブランチにプッシュした瞬間、デプロイ作業が自動で進行。「スッコココ、デプロイ完了!」とお知らせが来るのを待つだけになります。

さらに、設定次第ではテストや静的解析に合格しないとマージできないようにすることも可能です。
少し大げさですが、あなたのコードを守る鉄壁の防御ラインを作ることができます。少しは魅力が伝わったでしょうか?

CI/CD とは?

GitHub Actions を理解するうえで欠かせない概念が CI/CD です。
スタバでも「今日は CI 回した?」なんて会話が聞こえてきそうです。(まともならスタバにはいない)
でも、なんとなく「CI/CD ってカッコいい!」と思っていた皆さん、一緒にちゃんと理解していきましょう。
私は雰囲気だけ人間でした!!

CI (継続的インテグレーション: Continuous Integration)

プログラマーがコードの変更を GitHub にプッシュしたときに、自動でテストやビルドを実行し、問題が無いかを確認してくれます。まるであなたのコードがいつも安全かどうかチェックしてくれる警備員のようです。
テストが失敗しているのにマージされてしまうといった問題を事前に防ぐことができるので、プロジェクトの品質を保つことができますね!

CD (継続的デリバリー: Continuous Delivery) / (継続的デプロイ: Continuous Deployment)

テストが成功したコードを自動的に本番環境や STG 環境にデプロイすることです。
CD には 2 つのパターンがあります。

  • 継続的デリバリー (Continuous Delivery): テストがパスした後、手動で本番環境にデプロイする状態を自動で整えることです。
  • 継続的デプロイ (Continuous Deployment): テストがパスしたコードを自動的に本番環境にデプロイすることです。

デプロイを自動化するかどうかが 2 つの違いです。
もうデプロイのたびに夜勤をする必要は無いかもしれません!

GtiHub Actions を使った CI/CD のメリット

GitHub Actions を使った CI/CD のメリットは以下の通りです。

  • コードの品質向上: テストや静的解析を自動で実行することができ、問題が発生した場合にすぐに気付き、結果としてコードの品質が向上します。
  • 効率的な開発: テストやビルド、デプロイなどの作業を自動で行うことができるので、開発者は本来の開発に集中することができます。
  • チーム開発の促進: 複数の開発者が同じブランチに対して作業をしても、CI/CD でテストや政敵解析を行うので、開発者間でズレが生じることを防ぐことができます。

GitHub Actions の競合サービス

GitHub Actions は GitHub でしか使えません。
競合サービスとしては以下のものがあります。

  • BitBucket Pipelines
    • BitBucket 用の GitHub Actions のようなものです。
  • GitLab CI
    • GitLab 用の GitHub Actions のようなものです。

GitHub Actions の例

GitHub Actions のワークフローは、YAML ファイルで記述します。

ワークフローとは

GitHub Actions では、処理の流れを「ワークフロー(workflow)」と呼びます。
ワークフローは、自動で実行したい一連の処理を定義したものです。

例えば、以下は、プルリクエストを作成したときに、テストを自動で実行するワークフローの例です。

name: Test

on:
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
  workflow_dispatch:

jobs:
  test:
    name: 'Run tests'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'

      - name: Install dependencies
        run: npm install

      - name: Run tests
        run: npm run test

この例は、プルリクエストが作成されたときにテストを自動で実行するという一連の作業を「ワークフロー」として定義しています。

以下に、このワークフローの各部分について簡単に説明をします。

name

name はワークフロー全体の名前をつける部分です。
上記の例では、Test という名前をつけています。

この名前は GitHub Actions の UI 上に表示されるので、今なんのワークフローが実行されているかが分かりやすくなります。

on

on はワークフローが実行されるタイミングを指定する部分です。
上記の例では、2 つのイベントがトリガーとなっています。

  • pull_request
    • プルリクエストが作成または更新されたときにワークフローが実行されます。
    • branchesmain ブランチへのプルリクエストのみが対象となるように指定しています。
    • typesopenedsynchronize のイベントがトリガーとなるように指定しています。
      • opened はプルリクエストが作成されたとき
      • synchronize はプルリクエストがコミットなどで更新されたとき
  • workflow_dispatch
    • 手動でワークフローをトリガーすることができるようにしています。
    • GitHub の UI からワークフローを手動で起動できます。

jobs

jobs はワークフローの中で実行する一連の作業をまとめたものです。
上記の例では、test という名前のジョブを定義しています。
ジョブは 1 つ以上のstepsを含まなければなりません。

runs-on

runs-onは、ジョブがどの環境で実行されるかを指定します。
上記の例では、ubuntu-latest と指定しているので、Ubuntu の最新バージョンの実行環境でジョブが実行されることになります。

これは、テストやビルドなどを実行するための仮想環境になります。

steps

steps はジョブの中で、どの手順で作業を進めるかを示す部分です。
ステップは順番に実行され、上記の例では以下の 4 つのステップが定義されています。

  1. Checkout source code

    • リポジトリのソースコードをチェックアウト(取得)します
    • actions/checkout@v4は GitHub が提供するアクションで、ソースコードをワークフロー内で利用可能にします。
  2. Set up Node.js

    • プロジェクトで Node.js を使っているため、Node.js のセットアップを行います。
    • actions/setup-node@v4は GitHub が提供するアクションで、Node.js のセットアップを行います。上記の例では、Node.js のバージョンを 18 に指定しています。
  3. Install dependencies

    • npm install を実行して、プロジェクトで必要な依存関係をインストールします。
  4. Run tests

    • npm run test を実行して、テストを実行します。

以上が、GitHub Actions のワークフローの例です。

すぐに使えそうなワークフローのご紹介

さぁ、ここまで GitHub Actions とは一体なんぞやという話や一例のご紹介をしておさらいをしました。
ここからはほぼコピペで使えそうなワークフローをご紹介していきたいと思います。

開発環境としては、以下のものが導入されていることを前提のものを紹介していくので、適宜必要な部分を取捨選択してください。

  • TypeScript
  • ESLint
  • Prettier
  • Biome
  • Vitest
  • pnpm

TypeScript

TypeScript の型チェックをするときのワークフローです。

まずは package.json に以下のスクリプトを追加します。

package.json
{
  "scripts": {
    "ci:typecheck": "tsc --noEmit",
  }
}

次に、.github/workflows/typecheck.yml を作成します。

typecheck.yml
name: Type Check

on:
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
  workflow_dispatch:

env:
  NODE_VERSION: 20.17.0
  PNPM_VERSION: 9

jobs:
  typecheck:
    name: 'TypeScript Type Check'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: ${{ env.PNPM_VERSION }}

      - name: Set Node.js version to ${{ env.NODE_VERSION }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: pnpm

      - name: Install dependencies
        run: pnpm install

      - name: Run TypeScript type check
        run: pnpm ci:typecheck

Vitest

Vitest でテストを実行するときのワークフローです。

まずは package.json に以下のスクリプトを追加します。

package.json
{
  "scripts": {
    "ci:test": "vitest",
  }
}

次に、.github/workflows/test.yml を作成します。

test.yml
name: Run Tests

on:
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
  workflow_dispatch:

env:
  NODE_VERSION: 20.17.0
  PNPM_VERSION: 9

jobs:
  test:
    name: 'Run Tests'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: ${{ env.PNPM_VERSION }}

      - name: Set Node.js version to ${{ env.NODE_VERSION }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: pnpm

      - name: Install dependencies
        run: pnpm install

      - name: Run Tests
        run: pnpm ci:test

ESLint

ESLint で静的解析をするときのワークフローです。

まずは package.json に以下のスクリプトを追加します。

package.json
{
  "scripts": {
    "ci:lint": "eslint .",
  }
}

次に、.github/workflows/lint.yml を作成します。

lint.yml
name: Run ESLint

on:
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
  workflow_dispatch:

env:
  NODE_VERSION: 20.17.0
  PNPM_VERSION: 9

jobs:
  test:
    name: 'Run ESLint'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: ${{ env.PNPM_VERSION }}

      - name: Set Node.js version to ${{ env.NODE_VERSION }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: pnpm

      - name: Install dependencies
        run: pnpm install

      - name: Run ESLint
        run: pnpm ci:lint

Prettier

Prettier でフォーマットが適切か確認するときのワークフローです。

まずは package.json に以下のスクリプトを追加します。

package.json
{
  "scripts": {
    "ci:prettier": "prettier --check .",
  }
}

次に、.github/workflows/prettier.yml を作成します。

prettier.yml
name: Run Prettier

on:
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
  workflow_dispatch:

env:
  NODE_VERSION: 20.17.0
  PNPM_VERSION: 9

jobs:
  test:
    name: 'Run Prettier'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: ${{ env.PNPM_VERSION }}

      - name: Set Node.js version to ${{ env.NODE_VERSION }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: pnpm

      - name: Install dependencies
        run: pnpm install

      - name: Run Prettier
        run: pnpm ci:prettier

Biome

Biome でコンポーネントのテストを実行するときのワークフローです。

まずは package.json に以下のスクリプトを追加します。

package.json
{
  "scripts": {
    "ci:biome": "biome check",
  }
}

次に、.github/workflows/biome.yml を作成します。

biome.yml
name: Run Biome

on:
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
  workflow_dispatch:

env:
  NODE_VERSION: 20.17.0
  PNPM_VERSION: 9

jobs:
  test:
    name: 'Run Biome'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: ${{ env.PNPM_VERSION }}

      - name: Set Node.js version to ${{ env.NODE_VERSION }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: pnpm

      - name: Install dependencies
        run: pnpm install

      - name: Run Biome
        run: pnpm ci:biome

Build

ビルドを実行するときのワークフローです。

.github/workflows/build.yml を作成します。

build.yml
name: Run Build

on:
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
  workflow_dispatch:

env:
  NODE_VERSION: 20.17.0
  PNPM_VERSION: 9

jobs:
  test:
    name: 'Run Build'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: ${{ env.PNPM_VERSION }}

      - name: Set Node.js version to ${{ env.NODE_VERSION }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: pnpm

      - name: Install dependencies
        run: pnpm install

      - name: Run Build
        run: pnpm build

以上、GitHub Actions のワークフローの例をご紹介しました。

これらの品質管理関連のワークフローを一つにまとめることもできます。
また、共通している処理を外部ファイルに切り出すこともできます。

例えば以下のようにできます。

.github/actions/install/action.yml という依存関係をインストールする部分を外部ファイルに切り出してみます。

name: 'Install'
description: 'Setup Node.js, install pnpm, and install project dependencies'

inputs:
  pnpm-version:
    description: 'The version of pnpm to install'
    required: false
    default: 'lts/*'

  node-version:
    description: 'The version of Node.js to use'
    required: false
    default: 'lts/*'

  husky:
    description: 'Whether to disable Husky hooks during CI/CD'
    required: false
    default: 0

runs:
  using: 'composite'
  steps:
    - name: Install pnpm
      id: install-pnpm
      uses: pnpm/action-setup@v4
      with:
        version: ${{ inputs.pnpm-version }}

    - name: Set Node.js version to ${{ inputs.node-version }}
      id: setup-node
      uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}
        cache: 'pnpm'

    - name: Install dependencies
      shell: bash
      run: pnpm install
      env:
        HUSKY: ${{ inputs.husky }}

inputsの部分は Props のようなもので、外部から値を受け取ることができます。
受け取らなかった場合のデフォルト値も設定可能です。

今回の共通処理では husky のフックを無効化して、影響を受けないようにしています。

この共通処理を使うと、各ワークフローの冗長な部分を削減することができます。

name: Run Tests

on:
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
  workflow_dispatch:

jobs:
  test:
    name: 'Run Tests'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Install dependencies
        uses: ./.github/actions/install

      - name: Run Tests
        run: pnpm ci:test

ワークフローの可読性を向上させることができましたね。

では、最後に品質管理用としてまとめたワークフローをご紹介します。

  • 型チェック
  • テスト
  • Biome のチェック
  • Prettier のチェック
  • Build が通るか

これらを確認するワークフローを例としてご紹介します。

.github/actions/install/action.yml
name: 'Install'
description: 'Setup Node.js, install pnpm, and install project dependencies'

inputs:
  # 使用する pnpm のバージョン
  pnpm-version:
    description: 'The version of pnpm to install'
    required: false
    default: 'lts/*'

  # 使用する Node.js のバージョン
  node-version:
    description: 'The version of Node.js to use'
    required: false
    default: 'lts/*'

  # CI/CD で Husky のフックを無効にするかどうか
  husky:
    description: 'Whether to disable Husky hooks during CI/CD'
    required: false
    default: 0

runs:
  using: 'composite'
  steps:
    # pnpm のインストール
    - name: Install pnpm
      id: install-pnpm
      uses: pnpm/action-setup@v4
      with:
        version: ${{ inputs.pnpm-version }} # 入力された pnpm のバージョンを使用

    # Node.js のバージョンを設定
    - name: Set Node.js version to ${{ inputs.node-version }}
      id: setup-node
      uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }} # 入力された Node.js のバージョンを使用
        cache: 'pnpm' # pnpm 用のキャッシュを有効化

    # プロジェクトの依存関係をインストール
    - name: Install dependencies
      shell: bash
      run: pnpm install # pnpm を使って依存関係をインストール
      env:
        HUSKY: ${{ inputs.husky }} # Husky のフックを無効にするための環境変数を設定
.github/workflows/quality.yml
name: Quality Check

on:
  # プルリクエストが「main」または「develop」ブランチに対して開かれたり、追加でコミットされたときに実行
  pull_request:
    branches:
      - main
      - develop
    types:
      - opened
      - synchronize
  # 手動でワークフローを実行する場合のトリガー
  workflow_dispatch:

env:
  # Node.js と pnpm のバージョン設定
  NODE_VERSION: 20.17.0
  PNPM_VERSION: 9

jobs:
  # TypeScript の型チェックを行うジョブ
  typecheck:
    name: 'TypeScript Type Check'
    runs-on: ubuntu-latest
    steps:
      # ソースコードをチェックアウト(GitHub Actions がリポジトリのコードを取得)
      - name: Checkout source code
        uses: actions/checkout@v4

      # Node.js と依存関係をインストール
      - name: Setup Node.js and install dependencies
        uses: ./.github/actions/install
        with:
          pnpm-version: ${{ env.PNPM_VERSION }} # 環境変数で設定した pnpm のバージョンを使用
          node-version: ${{ env.NODE_VERSION }} # 環境変数で設定した Node.js のバージョンを使用

      # TypeScript の型チェックを実行
      - name: Run TypeScript type check
        run: pnpm ci:typecheck

  # ユニットテストを実行するジョブ
  test:
    name: 'Run Unit Tests'
    runs-on: ubuntu-latest
    needs: typecheck # TypeScript の型チェックが成功した後に実行
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Setup Node.js and install dependencies
        uses: ./.github/actions/install
        with:
          pnpm-version: ${{ env.PNPM_VERSION }}
          node-version: ${{ env.NODE_VERSION }}

      # Vitest を使ってユニットテストを実行
      - name: Run Vitest
        run: pnpm ci:test
        env:
          CI: true # CI 環境用に設定

  # コードスタイルチェックを行うジョブ
  biome:
    name: 'Biome Code Style Check'
    runs-on: ubuntu-latest
    needs: typecheck # 型チェックが成功した後に実行
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Setup Node.js and install dependencies
        uses: ./.github/actions/install
        with:
          pnpm-version: ${{ env.PNPM_VERSION }}
          node-version: ${{ env.NODE_VERSION }}

      # Biome(コードフォーマット & スタイルチェックツール)を実行
      - name: Run Biome check
        run: pnpm ci:biome

  # Prettier を使ってコードの整形を確認するジョブ
  prettier:
    name: 'Prettier Formatting Check'
    runs-on: ubuntu-latest
    needs: typecheck # 型チェックが成功した後に実行
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Setup Node.js and install dependencies
        uses: ./.github/actions/install
        with:
          pnpm-version: ${{ env.PNPM_VERSION }}
          node-version: ${{ env.NODE_VERSION }}

      # Prettier を使ってコードのフォーマットを確認
      - name: Run Prettier check
        run: pnpm ci:prettier

  # プロジェクトのビルドを行うジョブ
  build:
    name: 'Build the Project'
    runs-on: ubuntu-latest
    needs: [typecheck, test, biome, prettier] # 型チェック、テスト、コードスタイル、整形が成功した後に実行
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Setup Node.js and install dependencies
        uses: ./.github/actions/install
        with:
          pnpm-version: ${{ env.PNPM_VERSION }}
          node-version: ${{ env.NODE_VERSION }}

      # プロジェクトのビルドを実行
      - name: Run Build
        run: pnpm build

以上で品質管理用のワークフローを作成することができました。

ところどころで、needsというキーワードを使っています。
needsにジョブ名を指定することで、そのジョブが成功した後に実行されるようになります。

needs を使うことで、ジョブ間の依存関係を定義でき、指定したジョブが成功した場合にのみ次のジョブが実行されます。
もし依存するジョブが失敗した場合、その後のジョブはスキップされます。これにより無駄な実行を防ぎ、効率的にワークフローを進めることができます。

例えば、型チェックが失敗しているときにテストなどをしても失敗する可能性は高いですし、テストが失敗しているときにビルドをしても成功とする可能性は低いです。

まとめ

今回は GitHub Actions を使って CI/CD を行うためのワークフローを一緒に見てきました。
GitHub Actions を使えば、手動で行っていた面倒な作業を自動化し、開発者が本来集中すべき「コードを書く」という本業に専念できるようになります。

それだけではありません。
自動化されたテストやビルドにより、コードの品質を保ち、チームの誰かがうっかりミスをしても事前に発見できるのです。
つまり、GitHub Actions は、あなたのプロジェクトにとっての「見えない仲間」です。

カッコつけて「CI/CD」と言いたいだけでなく、実際にその力を活用してみましょう!

GitHubで編集を提案

Discussion