AI

ローカルCI/CDパイプライン構築【act + lefthook + mise 完全ガイド】

ローカルCI/CDパイプライン構築【act + lefthook + mise 完全ガイド】

コードの変更をプッシュするまで、CI(継続的インテグレーション)の実行結果が分からない。この状況にストレスを感じた経験はないでしょうか。CIの実行には時間がかかり、わずかな変更の確認でも待機が必要です。

本番環境のCI/CDパイプラインは、開発者の手元から遠い場所にあります。そのため、ローカル環境とCI環境との差異に悩まされることも少なくありません。「私の環境では動くのに、CIではエラーになる」といった問題は、開発効率を大きく低下させます。

しかし、もしGitHubへのプッシュなしで、本番環境とほぼ同等のCIをローカルで回せたらどうでしょう。開発者はコードをコミットする前に、品質チェックを完了できます。フィードバックサイクルが短縮され、手戻りが大幅に減少するはずです。

この記事では、actlefthookmise という3つのツールを組み合わせます。これらを使って、ローカルで本番同等のCI/CDパイプラインを構築する方法を解説します。開発体験を向上させ、より質の高いソフトウェア開発を目指しましょう。

ローカルCI/CDパイプラインが最強な理由

actlefthookmise の組み合わせは、ローカル開発環境を劇的に強化します。GitHubへのプッシュなしでCI/CDパイプラインを回せるためです。この連携は、開発の初期段階で問題を早期発見します。結果として、手戻りを最小限に抑え、開発効率を飛躍的に向上させます。

具体的には、以下の相乗効果が期待できます。

  1. 高速フィードバック: lefthook がコミット前にコード品質をチェックします。さらに、act がGitHub Actionsワークフローをローカルで実行します。これにより、変更のたびにCIを待つ必要がなくなります。
  2. 環境の統一: mise は開発ツールや環境変数をプロジェクトごとに管理します。ローカル環境とCI環境の差異をなくし、「私の環境では動くのに」問題を解決します。
  3. 本番同等の検証: act はGitHub ActionsのワークフローをDockerコンテナ内で実行します。本番CI環境に近い条件でテストできるため、信頼性の高い検証が可能です。

これらのツールを組み合わせることで、開発者は安心してコードを書けます。品質の高いコードを迅速に提供できるようになるでしょう。

各ツールの役割と担当領域

ローカルCI/CDパイプラインを構築する上で、各ツールには明確な役割があります。

act: GitHub Actionsのローカル実行エンジン

act は、GitHub Actionsのワークフローをローカル環境で実行するツールです。GitHub Actionsは、コードのテストやビルド、デプロイなどの自動化を行うための機能です。通常、GitHubにコードをプッシュした際に実行されます。

act を使うと、GitHubにプッシュする手間を省き、ローカルでワークフローを試せます。Dockerを利用してGitHub Actionsのランナー環境を再現します。これにより、本番環境に近い条件で動作確認が可能です。高速なフィードバックループを実現し、GitHub Actionsのワークフロー開発を効率化します。

lefthook: Gitフックの強力なマネージャー

lefthook は、Gitフックを管理するためのツールです。Gitフックとは、git commitgit push といった特定のGit操作の前後で自動的に実行されるスクリプトを指します。例えば、コミット前にコードのフォーマットをチェックしたり、テストを実行したりできます。

lefthook は設定ファイルをYAML形式で記述します。複数のフックを並列で実行する機能も備えています。これにより、大規模なプロジェクトでも高速なチェックが可能です。コミット前に品質問題を早期に発見し、開発ワークフローに自動的な品質ゲートを設ける役割を担います。

mise: 開発環境の統一と管理

mise は、開発ツール、環境変数、タスクを一元的に管理するCLIツールです。プロジェクトごとに異なるバージョンのNode.jsやPython、Terraformなどを簡単に切り替えられます。これにより、複数のプロジェクトを並行して開発する際に発生するバージョン競合の問題を解決します。

misemise.toml という設定ファイルを使用します。このファイルにプロジェクトに必要なツールとバージョン、環境変数、カスタムタスクを定義します。開発環境のセットアップを自動化し、チームメンバー間で一貫した開発環境を維持するのに役立ちます。CI環境においても、mise を使うことでローカルと全く同じツールバージョンを保証できます。

実際のワークフロー:ローカルCI/CDのステップバイステップ

ここでは、miselefthookact を連携させたローカルCI/CDパイプラインの構築手順を解説します。

1. 前提条件の確認

まず、以下のツールがシステムにインストールされていることを確認してください。

  • Git
  • Docker (actが動作するために必要です)

2. miseのインストールと初期設定

mise をインストールし、プロジェクトに必要なツールを定義します。

# miseをインストール
curl https://mise.run | sh

# miseのPATHを通す(シェルの設定ファイルに追記)
# 例: ~/.zshrc または ~/.bashrc に以下を追加
# export PATH="$HOME/.local/bin:$PATH"
# source ~/.zshrc # または source ~/.bashrc

# プロジェクトディレクトリを作成し移動
mkdir my-local-ci-project
cd my-local-ci-project

# miseを初期化し、Node.jsとpnpmを導入する例
mise use -g node@20
mise use -g pnpm@latest

# mise.tomlを作成し、プロジェクト固有のツールとタスクを定義
# このファイルは後ほど詳しく解説します

3. lefthookのインストールと設定

lefthook をインストールし、Gitフックを設定します。

# lefthookをインストール
# Goがインストールされている場合
go install github.com/evilmartians/lefthook/v2@v2.1.9

# Node.jsプロジェクトの場合
# npm install lefthook --save-dev

# プロジェクトにlefthookをインストール
# これにより、.git/hooks ディレクトリにlefthookが設定されます
lefthook install

4. actのインストール

act をインストールします。

# Homebrewを使用する場合(macOS/Linux)
brew install act

# その他のインストール方法については公式ドキュメントを参照
# https://github.com/nektos/act#installation

5. GitHub Actionsワークフローの作成

act でローカル実行するためのGitHub Actionsワークフローを作成します。 例として、buildlint のジョブを持つ ci.yml を作成します。

mkdir -p .github/workflows
touch .github/workflows/ci.yml

# .github/workflows/ci.yml の内容は後ほど詳しく解説します

6. 設定ファイルの作成と連携

次に、mise.tomllefthook.yml を作成し、各ツールを連携させます。

mise.toml の例

プロジェクトに必要なツールバージョンと、カスタムタスクを定義します。 ここで定義したタスクは、mise run <task名> で実行できます。

[tools]
node = "20"
pnpm = "latest"

[tasks]
# pnpm installを実行するタスク
install = "pnpm install"

# lintを実行するタスク
lint = "pnpm lint" # 例えば、package.jsonに "lint": "eslint ." がある場合

# ビルドを実行するタスク
build = "pnpm build" # 例えば、package.jsonに "build": "vite build" がある場合

# テストを実行するタスク
test = "pnpm test" # 例えば、package.jsonに "test": "vitest" がある場合

.github/workflows/ci.yml の例

GitHub Actionsのワークフローを定義します。act はこのファイルを読み込んでローカル実行します。mise-action を使うことで、GitHub Actions上でも mise で定義した環境を再現できます。

name: CI

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup mise
        uses: jdxcode/mise-action@v2
      - name: Install dependencies
        run: mise run install
      - name: Run build
        run: mise run build

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup mise
        uses: jdxcode/mise-action@v2
      - name: Install dependencies
        run: mise run install
      - name: Run lint
        run: mise run lint

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup mise
        uses: jdxcode/mise-action@v2
      - name: Install dependencies
        run: mise run install
      - name: Run tests
        run: mise run test

lefthook.yml の例

lefthook の設定ファイルで、pre-commit フックを定義します。 ここで act コマンドを呼び出し、特定のGitHub Actionsジョブをローカルで実行させます。

pre-commit:
  # 並列実行を有効にする
  parallel: true
  commands:
    # miseで定義したlintタスクを実行
    mise-lint:
      run: mise run lint
      # コミット対象ファイルのみをlintする場合
      # files: git diff --cached --name-only --diff-filter=ACM | grep '\.ts$'

    # actを使ってGitHub Actionsのlintジョブをローカルで実行
    github-actions-lint:
      # act -j lint は、ci.ymlで定義された 'lint' ジョブのみを実行します
      # --rm は実行後にコンテナを削除します
      # --use-docker はDockerコンテナを使用することを示します
      run: act -j lint --rm --use-docker

    # actを使ってGitHub Actionsのtestジョブをローカルで実行
    github-actions-test:
      run: act -j test --rm --use-docker

7. ローカルCI/CDの実行

これで準備が整いました。コードを修正し、コミットしてみましょう。

# 例: package.jsonを作成
pnpm init -y

# 例: eslintとvitestをインストール
pnpm add -D eslint vitest

# package.jsonにlintとtestスクリプトを追加
# "scripts": {
#   "lint": "eslint .",
#   "test": "vitest"
# }

# 適当なコードファイルを作成
echo "console.log('Hello, local CI!');" > index.js

# Gitにステージング
git add .

# コミットを実行
# この時点でlefthookが起動し、設定されたコマンドを実行します
git commit -m "feat: setup local CI pipeline"

git commit コマンドを実行すると、lefthookmise-lintgithub-actions-lintgithub-actions-test の各コマンドを並列で実行します。これにより、コードがGitHubにプッシュされる前に、ローカルでコード品質チェックとCIテストが完了します。

設定ファイル・dotfilesの例

前述のワークフローで使用した設定ファイルを改めて確認します。これらをプロジェクトのルートディレクトリに配置することで、ローカルCI/CDパイプラインが機能します。

mise.toml

このファイルは、プロジェクトで使う開発ツールとタスクを定義します。

# プロジェクトで使うツールとそのバージョン
# 例えばNode.js 20系とpnpmの最新版を指定
[tools]
node = "20"
pnpm = "latest"

# プロジェクト固有のタスク定義
# これらのタスクは 'mise run <task名>' で実行可能
[tasks]
# 依存関係のインストール
install = "pnpm install"

# コードのlint実行
lint = "pnpm lint"

# プロジェクトのビルド
build = "pnpm build"

# テスト実行
test = "pnpm test"

.github/workflows/ci.yml

GitHub Actionsのワークフロー定義です。act がこのファイルを読み込みます。

name: CI # ワークフロー名

# ワークフローが実行されるトリガー
on:
  push: # pushイベントで実行
    branches:
      - main # mainブランチへのpush
  pull_request: # pull_requestイベントで実行
    branches:
      - main # mainブランチへのpull request

# 実行するジョブの定義
jobs:
  build: # ビルドジョブ
    runs-on: ubuntu-latest # 実行環境
    steps:
      - uses: actions/checkout@v4 # リポジトリをチェックアウト
      - name: Setup mise # miseのセットアップ
        uses: jdxcode/mise-action@v2 # mise-actionを使ってmiseを導入
      - name: Install dependencies # 依存関係のインストール
        run: mise run install # miseで定義したinstallタスクを実行
      - name: Run build # ビルド実行
        run: mise run build # miseで定義したbuildタスクを実行

  lint: # lintジョブ
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup mise
        uses: jdxcode/mise-action@v2
      - name: Install dependencies
        run: mise run install
      - name: Run lint # lint実行
        run: mise run lint # miseで定義したlintタスクを実行

  test: # テストジョブ
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup mise
        uses: jdxcode/mise-action@v2
      - name: Install dependencies
        run: mise run install
      - name: Run tests # テスト実行
        run: mise run test # miseで定義したtestタスクを実行

lefthook.yml

Gitフックの動作を定義します。pre-commit フックで miseact を連携させます。

# pre-commitフックの設定
pre-commit:
  # コマンドを並列で実行
  parallel: true
  commands:
    # miseで定義したlintタスクを実行
    # コミット対象のファイルに対してのみ実行したい場合、filesオプションを追加
    mise-lint:
      run: mise run lint
      # files: git diff --cached --name-only --diff-filter=ACM | grep '\.ts$'

    # actを使ってGitHub Actionsのlintジョブをローカルで実行
    # -j lint は ci.yml の lint ジョブのみを実行
    # --rm は実行後にDockerコンテナを削除
    # --use-docker はDockerコンテナで実行
    github-actions-lint:
      run: act -j lint --rm --use-docker

    # actを使ってGitHub Actionsのtestジョブをローカルで実行
    github-actions-test:
      run: act -j test --rm --use-docker

これらの設定ファイルを適切に配置し、それぞれのツールをインストールすることで、ローカルでのCI/CDパイプラインが構築されます。

よくあるトラブルと解決法

ローカルCI/CDパイプラインの構築中に遭遇しやすいトラブルとその解決策を説明します。

1. act がDockerを見つけられない、または権限エラー

act はDockerコンテナ内でワークフローを実行します。Dockerデーモンが起動していない、またはユーザーにDockerソケットへのアクセス権がない場合に発生します。

解決法:

  • Docker DesktopなどのDockerデーモンが起動しているか確認します。
  • Linux環境では、現在のユーザーを docker グループに追加します。
    sudo groupadd docker # dockerグループが存在しない場合
    sudo usermod -aG docker $USER
    newgrp docker # グループ変更を即時適用(または再ログイン)
    

2. lefthook が実行されない

git commit 時に lefthook が動作しない場合、インストールが不完全な可能性があります。

解決法:

  • プロジェクトのルートディレクトリで lefthook install を実行したか確認します。これにより、.git/hooks ディレクトリにフックスクリプトが配置されます。
  • lefthook.yml の構文エラーがないか確認します。YAMLのインデントやキーが正しいかチェックしましょう。
  • Gitフックが何らかの理由でスキップされていないか確認します。例えば git commit --no-verify を使っていないか。

3. mise がツールをインストールできない、または認識しない

mise が必要なツールバージョンを見つけられない、またはコマンドが実行できない場合に発生します。

解決法:

  • mise use コマンドでツールが正しく指定されているか確認します。例えば mise use node@20
  • ネットワーク接続に問題がないか確認します。ツールは通常、外部からダウンロードされます。
  • mise reshim を実行して、実行パス(PATH)を更新します。
  • mise install を実行して、mise.toml に定義されたツールが全てインストールされているか確認します。

4. GitHub Actionsとローカル(act)で結果が異なる

act はGitHub Actionsの環境を忠実に再現しますが、完全に同一ではありません。特に環境変数やシークレットの扱いで差異が生じることがあります。

解決法:

  • GitHub Actionsのシークレットは act では自動的に利用できません。actsecrets オプション (-s KEY=VALUE) や .secrets ファイルを使ってローカルで設定します。
  • 環境変数がローカルとCIで異なる場合、actenv オプション (-e KEY=VALUE) や .env ファイルで調整します。
  • act--verbose オプションを使って詳細なログを確認し、差異の原因を探ります。
  • Dockerイメージの違いも考慮します。act はデフォルトで nektos/act-environments-ubuntu:18.04 などのイメージを使います。ワークフローで指定された uses アクションがローカル環境で利用可能か確認します。

5. lefthook でファイルが見つからないエラー

lefthook のコマンド内で、期待するファイルが渡されない場合に発生します。特に pre-commit フックでは、ステージングされたファイルのみが対象となることが多いです。

解決法:

  • lefthook.yml のコマンド定義に files: オプションを使用している場合、そのパターンが正しいか確認します。
  • 例えば、git diff --cached --name-only --diff-filter=ACM のようなコマンドで、ステージングされたファイルのリストを明示的に取得し、パイプで後続のコマンドに渡すようにします。
  • lefthookrun コマンドが、引数として受け取るファイルリストを適切に処理できるか確認します。

これらのトラブルシューティングを通じて、スムーズなローカルCI/CDパイプラインの運用を目指しましょう。

今日からできる実行プラン

ローカルCI/CDパイプラインの導入は、開発効率を大きく改善します。今日から実践できる3つのステップを紹介します。

ステップ1: 各ツールのインストール

まずは、miselefthookact の3つのツールを開発環境に導入します。

# miseのインストール
curl https://mise.run | sh
# シェルの設定ファイルにPATHを追加し、再読み込み
# export PATH="$HOME/.local/bin:$PATH"

# lefthookのインストール (Goがインストールされている場合)
go install github.com/evilmartians/lefthook/v2@v2.1.9
# または Node.jsプロジェクトの場合
# npm install lefthook --save-dev

# actのインストール (macOS/LinuxでHomebrewを使用)
brew install act

これらのコマンドを実行し、各ツールのバージョンを確認して、正しくインストールされたことを確かめます。

mise --version
lefthook version
act --version

ステップ2: プロジェクトへの導入と基本設定

次に、既存または新規のプロジェクトにこれらのツールを導入します。

  1. プロジェクトの初期化: Gitリポジトリがまだない場合は作成します。
    mkdir my-project-name
    cd my-project-name
    git init
    
  2. mise.toml の作成: プロジェクトで使うツール(例: Node.js, Python)と、簡単なタスクを定義します。
    # mise.toml を作成
    touch mise.toml
    # 例: Node.js 20系とpnpmを定義
    mise use -g node@20
    mise use -g pnpm@latest
    # mise.toml にタスクを追加(手動またはエディタで)
    # [tasks]
    # lint = "pnpm lint"
    # test = "pnpm test"
    
  3. lefthook.yml の作成:pre-commit フックで mise run lint を実行する設定を追加します。
    # lefthook.yml を作成
    touch lefthook.yml
    # lefthook.yml に以下の内容を記述(手動またはエディタで)
    # pre-commit:
    #   commands:
    #     mise-lint:
    #       run: mise run lint
    
  4. lefthook のインストール: プロジェクトのGitフックに lefthook を設定します。
    lefthook install
    
  5. GitHub Actionsワークフローの作成: 簡単なCIワークフロー (.github/workflows/ci.yml) を作成します。最初は lint ジョブだけでも構いません。
    mkdir -p .github/workflows
    touch .github/workflows/ci.yml
    # ci.yml に lint ジョブを定義(例を参考に)
    

ステップ3: 既存ワークフローの移行と拡張

最後に、既存のCIジョブを act でローカル実行できるように調整し、lefthook で自動化します。

  1. lefthook.yml の更新:lefthook.ymlact を使ったGitHub Actionsジョブの実行を追加します。
    # lefthook.yml に以下を追加
    # pre-commit:
    #   commands:
    #     github-actions-lint:
    #       run: act -j lint --rm --use-docker
    
  2. git commit で動作確認: コードを修正し、git add . && git commit -m "feat: setup local CI" を実行します。lefthookact が連携して動作することを確認しましょう。
  3. CIジョブの拡充:mise.toml.github/workflows/ci.ymlbuildtest ジョブを追加します。lefthook.yml も更新し、これらのジョブも pre-commit で実行されるように設定します。

この3ステップで、あなたの開発環境は強力なローカルCI/CDパイプラインを手に入れます。高速なフィードバックと本番同等の検証により、開発効率とコード品質が飛躍的に向上するでしょう。

関連ページ

参考文献

広告

-AI