Composerの「Dry-Run」とログ徹底活用:依存関係更新時の事故を未然に防ぐ防御的メンテナンス
開発現場において、`composer update` は時としてパンドラの箱を開ける行為に等しい。
「最新のセキュリティパッチを適用したい」「新機能のために特定のパッケージを上げたい」――その純粋な動機が、巨大な依存関係ツリーの深部で予期せぬ破壊的変更(Breaking Changes)を引き起こし、ステージング環境や最悪の場合は本番環境を沈黙させる。
ネットの海を漂う「Composerの基本の使い方」といった入門記事では、`composer update` を叩けとしか書かれていない。しかし、何十ものマイクロサービスやレガシーが混在する複雑なプロダクトを背負う我々DevOpsエンジニアにとって必要なのは、「祈るようなデプロイ」ではなく、数学的・論理的に安全性が証明された「防御的メンテナンス」である。
本稿では、Composerの内部アーキテクチャに踏み込み、`–dry-run` と `-vvv` ログの徹底活用、そしてそれらをCI/CDパイプラインやコンテナ環境に組み込んで「事故の芽を完全に摘み取る」ための極限の自動化手法を解説する。
—
1. 内部アーキテクチャから紐解く:なぜ `composer update` は事故るのか
Composerの本質は、高度なSAT(満満足問題)ソルバーである。
`composer.json` に記述された制約(Constraints)を読み込み、Packagist等のリポジトリからメタデータを取得し、すべてのパッケージが矛盾なく共存できるバージョンを計算(Resolve)している。
ここで発生する最大のリスクは、バージョン制約の「甘さ」にある。
例えば、`^1.2` と指定した場合、Composerは `1.2.0` から `2.0.0` 未満までの最新を許容する。依存しているライブラリAがライブラリBのバージョンを曖昧に許容していると、`composer update` を実行した瞬間に、開発者が意図しないメジャーアップデートやマイナーアップデートが連鎖的に発生する。
依存関係解決のブラックボックス
Composerは、解決プロセスにおいて以下のステップを踏む:
1. Local Repository & Cache 参照: すでにインストールされているものやキャッシュを確認。
2. Remote Metadata (packages.json) の取得: 差分や利用可能なバージョンを特定。
3. Dependency Graph の構築とSATソルバーによる解決: 競合(Conflicts)や置換(Replaces)を考慮した最適解の探索。
このステップ3の「探索」において、人間の脳で結果を完璧に予測することは不可能に近い。だからこそ、「実際にファイルを書き換える前」にComposerの頭脳内部を覗き見し、検証する仕組みが不可欠となる。
—
2. `–dry-run` と `-vvv` による完全な変更予測
コードベースやディスクを一切汚さずに、依存関係の解決結果だけをシミュレートするのが `–dry-run` オプションだ。これに詳細度を最大化する `-vvv` を組み合わせることで、Composerの思考プロセスと最終的な変更差分のすべてが露わになる。
実務で使う最強のコマンドライン
composer update –dry-run -vvv –profile
各フラグのエンジニアリング的意味
- `–dry-run`: 依存関係の解決とロックファイルの生成・パッケージのダウンロードをシミュレートするが、ファイルシステムへの書き込み(vendor/ 及 composer.lock の更新)を一切行わない。
- `-vvv`: 出力詳細度を最高レベル(Debug)に引き上げる。リクエストのタイムスタンプ、メモリ消費量、SATソルバーがどのようにバージョンを削っていったかのログが出力される。
- `–profile`: 依存関係の解決にかかった実行時間とピークメモリ使用量を計測する。メモリリークやComposerのパフォーマンス劣化の兆候を検知できる。
ログの読み解き方:意図しないメジャーアップデートの検知
`-vvv` を付与して実行すると、以下のようなデバッグログが流れる。ここから「何が変わりそうか」を読み取る。
[2023-10-25T12:00:00.000000+00:00] Reading /app/composer.json
[2023-10-25T12:00:00.123456+00:00] Loading composer repositories with local cache
[2023-10-25T12:00:00.567890+00:00] Dependency resolution:
- Selecting vendor/package-a (2.1.0)
- Upgrading vendor/package-b (1.4.2 -> 2.0.0) <-- 【危険信号】意図しないメジャーアップデート!
この `Upgrading … (1.4.2 -> 2.0.0)` のような行を見逃さないことだ。もしこれが意図しないものであれば、`composer.json` の制約が緩すぎることの証明であり、直ちに制約を絞る(例: `^1.4` に固定するなど)必要がある。
—
3. CI/CDパイプラインとの高度な連携:事故を自動ブロックする仕組み
人間の目によるレビューだけに頼る運用は、ヒューマンエラーの温床となる。
プルリクエスト作成時や夜間バッチで、GitHub Actions等のCI/CD環境を使い、「予期せぬ依存関係の変更やセキュリティ脆弱性がある場合に自動でPRを落とす・警告する」パイプラインを構築する。
以下に、実務で即座に使える GitHub Actions のワークフロー定義を示す。
name: “Defensive Composer Maintenance”
on:
pull_request:
paths:
- ‘composer.json’
- ‘composer.lock’
schedule:
- cron: ‘0 2 1’ # 毎週月曜日の午前2時に自動実行し、脆弱性やアップデートを監視
jobs:
dry-run-update:
name: “Analyze Composer Updates (Dry-Run)”
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup PHP Environment
uses: shivammathur/setup-php@v2
with:
php-version: ‘8.2’
tools: composer:v2
coverage: none
- name: Get Composer Cache Directory
id: composer-cache
run: echo “dir=$(composer config cache-files-dir)” >> $GITHUB_OUTPUT
- name: Cache Composer Dependencies
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${