【実務・中級編】Composerの「Hooks」によるCI/CDワークフロー自動化:Git連携でコミット前にコード品質を強制する方法 – ビルド・パッケージ管理ツール生産性向上バイブル

序文:なぜ「Vendor製ツール」の直接実行はチーム開発の癌なのか

テックリードとして多くのPHPプロジェクトを監査してきた中で、最も絶望的な光景の一つが、開発者のローカル環境ごとにコード品質の検査ツール(PHPStanやPHP_CodeSnifferなど)のバージョンがバラバラに稼働している状態です。

「俺のローカルではパスしたのに、なぜCI(GitHub Actions)で落ちるんだ?」
この無駄な問答に、チーム全体でどれだけの開発工数がドブに捨てられているでしょうか。

根本的な原因は、開発者がグローバル環境にインストールされた静的解析ツールに頼っていたり、プロジェクトローカルであっても実行パスの解決を属人化させている点にあります。

PHPのパッケージマネージャーである Composer は、単なるライブラリのダウンローダーではありません。プロジェクト固有の依存関係(`vendor/bin/`)をカプセル化し、チーム全員に「同一の実行コンテキスト」を強制するための最強のオーケストレーターです。

今回は、Composerの `scripts` 機能と Git Hooks を完全調和させ、「開発者がコミットボタンを押した瞬間、ローカル環境の厳格な品質フィルターを強制通過させる」 究極の自動化パイプラインの構築手法を伝授します。

—

1. 思想設計:なぜ Composer Scripts と Git Hooks なのか

CI/CDパイプラインで静検解析やテストを走らせるのは現代の常識ですが、これだけでは「遅すぎるフィードバックループ」という問題が残ります。リモートリポジトリにPushし、CIが回り、失敗通知を受け取って修正するまでには数分のタイムラグが発生します。

このタイムラグをゼロにするのが Git Hooks(Pre-commit) です。

しかし、Gitのフック機構(`.git/hooks/`)に直接シェルスクリプトを書く運用は、チーム開発において破綻します。.git ディレクトリ配下はリモートリポジトリでバージョン管理されないため、メンバーごとにスクリプトを手動ポンチ絵で配置させたり、環境依存の絶対パス(`/usr/bin/php` など)をハードコーディングする悪夢を生むからです。

ここで Composer Scripts がキラーソリューションとして機能します。

1. 実行環境の局所化: `vendor/bin/phpstan` のように、プロジェクトの `composer.json` でバージョン固定されたバイナリを確実に叩く。
2. 実行経路の共通化: OS(macOS, Linux, WSL2等)の差異をComposerが抽象化し、統一されたコマンドインターフェースを提供する。
3. リポジトリ管理: フックから呼び出される実体を `composer.json`(と専用のスクリプトファイル)に記述することで、コードと一緒にバージョン管理下に置く。

—

2. 実装アーキテクチャ:composer.json のベストプラクティス設計

まずは、プロジェクトの心臓部である `composer.json` に、品質担保のためのスクリプト群を定義します。単にコマンドを並べるのではなく、「処理の原子性(アトミック性)」 と 「開発体験(DX)の高速化」 を両立させた構成がこちらです。

{
“name”: “enterprise/php-app”,
“description”: “High-performance enterprise backend application”,
“type”: “project”,
“require”: {
“php”: “^8.2”
},
“require-dev”: {
“friendsofphp/php-cs-fixer”: “^3.40”,
“phpstan/phpstan”: “^1.10”,
“squizlabs/php_codesniffer”: “^3.7”
},
“scripts”: {
// — 個別の品質検査タスク定義 —
“lint:cs”: “vendor/bin/php-cs-fixer fix –dry-run –diff”,
“fix:cs”: “vendor/bin/php-cs-fixer fix”,
“lint:stan”: “vendor/bin/phpstan analyse -c phpstan.neon –no-progress”,
“lint:sniff”: “vendor/bin/phpcs –standard=PSR12 src/”,

// — 複合品質チェック(CIおよびローカル用) —
“test:quality”: [
“@lint:cs”,
“@lint:stan”,
“@lint:sniff”
],

// — Git Hooks自動セットアップ用スクリプト —
“post-install-cmd”: [
“Composer\\Config::disableProcessTimeout”,
“@setup-hooks”
],
“post-update-cmd”: [
“Composer\\Config::disableProcessTimeout”,
“@setup-hooks”
],
“setup-hooks”: [
“echo ‘Setting up Git hooks…'”,
“cp -R .githooks/ .git/hooks/”,
“chmod +x .git/hooks/”,
“echo ‘Git hooks installed successfully!'”
]
},
“config”: {
“optimize-autoloader”: true,
“sort-packages”: true,
“allow-plugins”: {
“friendsofphp/php-cs-fixer”: true
}
}
}

この設定のアーキテクティングポイント

  • `post-install-cmd` / `post-update-cmd` のフック: 開発者が `composer install` を叩いた瞬間、自動的に後述する `.githooks` ディレクトリ内のスクリプトが `.git/hooks` に同期・実行権限が付与されます。これにより「フックの入れ忘れ」というヒューマンエラーを物理的に遮断します。
  • Composer内部構文 `@` の活用: `test:quality` 内で `@lint:cs` のようにアットマークを使用することで、定義済みの別のComposerスクリプトを効率的にチェーン実行できます。

—

3. Git Hooks の実体設計:軽量かつ堅牢な Pre-commit の実装

次に、リポジトリのルートに `.githooks` というディレクトリを作成し、その中に `pre-commit` ファイルを配置します。

このスクリプトの最大の肝は、「リポジトリ全体ではなく、今回コミットしようとしているステージング済みのPHPファイルのみを対象に静的解析を走らせる」 という点です。全ファイルをスキャンしていると、コードベースが巨大化した際にコミットのたびに数分待たされる地獄を見るためです。

`.githooks/pre-commit`

!/bin/sh
==============================================================================
Git Pre-commit Hook managed by Composer
==============================================================================

set -e

echo “==> Running pre-commit quality gates…”

1. ステージングされている(追加・変更された)PHPファイルを取得
STAGED_PHP_FILES=$(git diff –cached –name-only –diff-filter=ACM | grep ‘\.php$’)

PHPファイルがステージングされていない場合は何もせずにパス
if [ -z “$STAGED_PHP_FILES” ]; then
echo “No PHP files staged for commit. Skipping quality checks.”
exit 0
fi

echo “==> Analyzing staged PHP files:”
echo “$STAGED_PHP_FILES”

2. PHP-CS-Fixerによるコーディング規約の自動修正(またはドライラン検証)
ここではコミット前に自動フォーマットを適用してステージに戻す高度な処理を実行
echo “==> Running PHP-CS-Fixer…”
vendor/bin/php-cs-fixer fix –using-cache=no $STAGED_PHP_FILES

自動修正されたファイルを再度Gitのステージングエリアに追加
for file in $STAGED_PHP_FILES; do
if [ -f “$file” ]; then
git add “$file”
fi
done

3. PHPStanによる静的解析の実行(プロジェクト全体を対象に安全性を担保)
echo “==> Running PHPStan…”
vendor/bin/phpstan analyse -c phpstan.neon –no-progress

echo “==> All quality gates passed successfully! Proceeding with commit.”
exit 0

スクリプトの深掘り解説

  • `git diff –cached –name-only –diff-filter=ACM`: コミット対象としてステージングされたファイル(Added, Copied, Modified)のうち、PHP拡張子のものだけを高速に抽出します。
  • 自動フォーマットの巻き込み: `php-cs-fixer` を通した後に再度 `git add` を行うことで、開発者がインデントやスペースのミスを直す手間すら省き、常に美しいコードだけがリポジトリに刻まれるようになります。

—

4. プロ現場のプロフェッショナル・テクニック:極限まで開発スピードを上げるハック

ここまでの構成でも十分に実用的ですが、真のDevOpsエンジニアを目指すのであれば、さらに開発体験(DX)を加速させる「隠し味」が必要です。

1. 巨大プロジェクトにおける静的解析の高速化(差分解析)

プロジェクトが巨大化すると、PHPStanのフルスキャンは数秒〜数十秒かかり、コミットのテンポが落ちます。これを解決するため、PHPStanのビルドキャッシュを有効活用しつつ、ステージングファイルのみを高速チェックするカスタムComposerスクリプトを追加します。

“scripts”: {
“lint:stan-staged”: “vendor/bin/phpstan analyse –no-progress”
}

さらに、PHPStanの構成ファイル(`phpstan.neon`)において、パラレル処理を有効化し、CPUのコアをフル活用する設定を忘れてはなりません。

phpstan.neon
parameters:
level: max
paths:

  • src
  • tests

parallel:
maximumNumberOfProcesses: 8

2. 緊急時の「脱出ハッチ」(–no-verify の罠と対策)

どうしても緊急のホットフィックスで、テストや静的解析を通さずに急いでコミットしたい瞬間があります。Gitの標準仕様として `git commit -m “fix” –no-verify` を使えばフックをバイパスできますが、これを乱用されると品質ガバナンスが崩壊します。

チームへの心理的安全性と規律を保つため、緊急バイパス時はCI側で必ずフルテストが走るCIパイプライン(GitHub Actions)をセットで運用することが大前提となります。

.github/workflows/quality.yml の抜粋
name: CI Quality Gate
on:
push:
branches: [ “main”, “develop” ]
pull_request:
branches: [ “main”, “develop” ]

jobs:
quality:
runs-v2: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • name: Setup PHP

uses: shivammathur/setup-php@v2
with:
php-version: ‘8.2’
tools: composer:v2

  • name: Get Composer Cache Directory

id: composer-cache
run: echo “dir=$(composer config cache-files-dir)” >> $GITHUB_OUTPUT

  • uses: actions/cache@v3

with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${pis: hashFiles(‘/composer.lock’) }}
restore-keys: ${{ runner.os }}-composer-

  • name: Install dependencies

run: composer install –prefer-dist –no-progress

  • name: Run Quality Checks via Composer

run: composer test:quality

ローカルでフックをバイパスしても、リモートのGitHub Actions(`composer test:quality`)で必ず検知され、マージブロックされる二重防壁構造が完成します。

—

5. 結び:ツールに縛られるな、ツールを調律せよ

優れた開発環境とは、開発者が「品質を保とう」と意識しなくても、自然と最高のアウトプットしか出せない仕組み(ガードレール)が背後で完璧に機能している状態を指します。

今回紹介した Composer Scripts と Git Hooks の連携は、単なる設定ファイルのテクニックではありません。チームのコードレビューにおける「インデントが揃っていない」「タイポがある」といった不毛な議論を永遠に消し去り、アーキテクチャの設計やビジネスロジックの本質的な議論にエンジニアの脳内リソースを集中させるための、最もレバレッジの高い投資です。

今日からあなたのプロジェクトの `composer.json` を書き換え、チームの生産性を次のステージへと引き上げてください。

タイトルとURLをコピーしました