イントロダクション:なぜ「Private PackagistでもVCSでもない選択」が必要なのか
テックリードとしてチーム全体の生産性を見渡したとき、バックエンドのモジュール化とコード共有は避けて通れない命題です。PHPエコシステムにおいて、共通処理を切り出してパッケージ化する際、真っ先に挙がる選択肢は VCS(GitHub/GitLab)リポジトリの直接指定 か、あるいは Private Packagist等の有償ホスティングサービス、もしくは Satisによる静的リポジトリジェネレータ でしょう。
しかし、実務の現場では以下の壁に突き当たります。
1. VCS直接指定の限界: `composer.json` に `repositories` として個別のGitリポジトリをズラリと並べ始めると、依存関係の解決(Dependency Resolution)が劇的に遅くなり、何よりバージョン管理のタグ付け漏れやブランチ名変更でCI/CDが突然爆発する。
2. Private Packagistのコスト: 優れたサービスだが、社内セキュア環境やコストの制約上、導入できないケースが少なくない。
3. Satisのオーバースペック: Satisは強力だが、ビルドの手間、JSON再生成のパイプライン構築、そして何より「単なる小規模な社内共通ライブラリ群を配るだけ」の目的に対して、構築・運用の認知負荷が高すぎる。
ここで提案したいのが、「超軽量・完全静的なCustom Repository(Satisレス)」 というアーキテクチャです。ビルドツールすらいらず、NginxやS3バケット、あるいは社内の小さなファイルサーバーに「1つの `packages.json`」を置くだけで、公式Packagistと同等の速度と安定性で社内ライブラリを配信する――この仕組みを構築・運用するための極意を、プロの実践テクニックを交えて徹底解説します。
—
1. 内部挙動の理解:Composerがリポジトリを引くとき何が起きているのか
ツールを真に使いこなすためには、ラッパーの背後で何が動いているかを知る必要があります。ComposerのCustom Repository(特に `package` や `composer` タイプ)は、マジックではありません。
Composerは、指定されたリポジトリURLに対して以下の順序でリクエストを飛ばします。
1. `packages.json`(または `packages.json` が指すメタデータ)をHTTP経由で取得する。
2. そのJSON内に定義されたパッケージ名、バージョン、そして実際のソース(ZipアーカイブやGitアーカイブ)のURLをメモリ上にマッピングする。
3. 依存関係解決エンジン(ComposerのSATソルバー)が、全パッケージの制約を満たす最適なバージョンツリーを計算する。
4. 決定されたバージョンのアーカイブをダウンロードし、プロジェクトの `vendor/` に展開する。
つまり、私たちがやるべきことは、この「Composerが読める正しいスキーマを持った `packages.json`」と「パッケージのzipファイル」を静的にホスティングし、適切な認証をかけることだけです。
—
2. 実践:Satisを使わない静的 `packages.json` の構築
余計なビルドスクリプトやSatisの導入を廃し、手動あるいは簡易なCIスクリプトで管理できる最小限かつ堅牢な `packages.json` のベストプラクティス構成例を提示します。
2.1. `packages.json` のベストプラクティス構成例
社内インフラ(例: `https://packages.internal.net/packages.json`)でホスティングするJSONの完全な構造です。
{
“packages”: {
“acme/auth-sdk”: {
“1.0.0”: {
“name”: “acme/auth-sdk”,
“version”: “1.0.0”,
“description”: “社内システム共通のOAuth2/JWT認証ミドルウェアパッケージ”,
“keywords”: [“auth”, “jwt”, “oauth2”, “internal”],
“homepage”: “https://git.internal.net/acme/auth-sdk”,
“license”: [“proprietary”],
“authors”: [
{
“name”: “Platform Engineering Team”,
“email”: “dev-platform@acme.internal”
}
],
“require”: {
“php”: “^8.2”,
“firebase/php-jwt”: “^6.8”,
“guzzlehttp/guzzle”: “^7.7”
},
“type”: “library”,
“dist”: {
“url”: “https://packages.internal.net/archives/acme-auth-sdk-1.0.0.zip”,
“type”: “zip”,
“reference”: “v1.0.0”
}
},
“1.1.0”: {
“name”: “acme/auth-sdk”,
“version”: “1.1.0”,
“description”: “社内システム共通のOAuth2/JWT認証ミドルウェアパッケージ”,
“keywords”: [“auth”, “jwt”, “oauth2”, “internal”],
“homepage”: “https://git.internal.net/acme/auth-sdk”,
“license”: [“proprietary”],
“require”: {
“php”: “^8.2”,
“firebase/php-jwt”: “^6.9”,
“guzzlehttp/guzzle”: “^7.7”
},
“type”: “library”,
“dist”: {
“url”: “https://packages.internal.net/archives/acme-auth-sdk-1.1.0.zip”,
“type”: “zip”,
“reference”: “v1.1.0”
}
}
}
}
}
この設計のポイント
- `dist` の活用: Gitリポジトリを直接クローンさせるのではなく、ビルド済みの `.zip` アーカイブを指定することで、依存関係解決とダウンロードの速度が劇的に向上します(Gitの認証情報を各開発者のマシンに保持させる必要もなくなります)。
- 正確な `require` の定義: 社内ライブラリが依存しているサードパーティパッケージ(ここでは `firebase/php-jwt` や `guzzlehttp/guzzle`)を明記することで、Composerはプロジェクト全体の依存関係競合を事前に検知できます。
—
3. チーム開発で役立つ設定の共有化ルール
静的リポジトリを導入する際、チームメンバー全員の `composer.json` に手動でリポジトリURLを書かせるのはナンセンスです。ヒューマンエラーの温床になります。
プロジェクトのルートにある `composer.json` に以下のように設定し、社内リポジトリへの参照を標準化します。
3.1. プロジェクト側 `composer.json` の設定
{
“name”: “acme/webapp”,
“description”: “社内向けメインWebアプリケーション”,
“type”: “project”,
“license”: “proprietary”,
“repositories”: [
{
“type”: “composer”,
“url”: “https://packages.internal.net”
},
{
“packagist.org”: false
}
],
“require”: {
“php”: “^8.2”,
“acme/auth-sdk”: “^1.0”
},
“config”: {
“optimize-autoloader”: true,
“preferred-install”: “dist”,
“sort-packages”: true,
“allow-plugins”: {
“composer/installers”: true
}
}
}
プロの技:`”packagist.org”: false` のセキュリティ効果
上記の `repositories` ブロックで `”packagist.org”: false` を宣言している点に注目してください。
これにより、悪意あるサードパーティパッケージがPackagist上で同名(Dependency Confusion攻撃)で登録されたとしても、社内アプリケーションが誤って外部の偽物パッケージを読み込むリスクを物理的に遮断できます。セキュリティ監査においても極めて高く評価される設定です。
—
4. 開発スピードを極限まで高める実践テクニックとシークレット
ここからは、日々の開発でテックリードが周囲に差をつけるための実践知見を共有します。
4.1. 認証のセキュアなハンドリング(HTTP Basic認証)
社内リポジトリサーバー(`https://packages.internal.net`)がBasic認証やトークン認証で保護されている場合、開発者ごとに設定させるのは困難です。CI/CDやローカル開発環境での認証情報は、環境変数またはComposerのグローバル設定でスマートに管理します。
開発者のローカルマシン、またはCIのコンテナ内で以下のコマンドを実行し、認証情報を安全にストアします。
社内パッケージサーバーに対するHTTP Basic認証の資格情報をグローバルに保存
composer config –global http-basic.packages.internal.net ユーザー名 パスワード
このコマンドを実行すると、ホームディレクトリの `~/.composer/auth.json`(Composer v2以降は `~/.config/composer/auth.json`)に暗号化または安全な形式でクレデンシャルが保存され、日々の `composer update` や `composer install` がシームレスに動作します。
4.2. 爆速でデバッグするためのローカルパス・リポジトリ(Path Repository)の併用
社内ライブラリ(例: `acme/auth-sdk`)のバグ修正や機能追加を行う際、わざわざコードを修正してZipを固め、サーバーにアップロードして `composer update` を走らせる……なんて非効率なデバッグをしていませんか?
Composerには、開発時にのみローカルのディレクトリを直接参照させる Path Repository という神機能があります。
アプリケーション側の `composer.json` に以下を追加します。
{
“repositories”: [
{
“type”: “path”,
“url”: “../auth-sdk”,
“options”: {
“symlink”: true
}
},
{
“type”: “composer”,
“url”: “https://packages.internal.net”
}
]
}
この設定の圧倒的なメリット
- `../auth-sdk`(ローカルでクローンして開発中のディレクトリ)へのシンボリックリンクとしてパッケージが結合されます。
- アプリケーション側からライブラリ側のコードを直接エディタで編集・ステップ実行でき、変更が即座に反映されます。
- バグ修正が終わったら、Path Repositoryの設定をコメントアウト(または削除)し、タグを切って `packages.json` を更新するだけで、クリーンな本番環境用ビルドに戻せます。
—
5. 運用自動化:パッケージ公開をCI/CDで完結させるスクリプト
最後に、開発者がライブラリのバージョンを上げた際に、面倒な `packages.json` の書き換えとZip作成を自動化する、GitHub Actions / GitLab CI向けのシェルスクリプトの断片を紹介します。
手動運用のミスをなくすため、リポジトリにタグ(例: `v1.2.0`)がプッシュされた瞬間に、CIが自動でZipを生成し、ストレージ(S3等)へアップロードした上で、`packages.json` をアトミックに更新するパイプラインを組むのがプロのモダンなアプローチです。
!/usr/bin/env bash
set -eu0
パッケージ名とバージョンの定義(タグから自動取得)
PACKAGE_NAME=”acme/auth-sdk”
VERSION=”${CI_COMMIT_TAG:-1.0.0}”
ARCHIVE_NAME=”acme-auth-sdk-${VERSION}.zip”
echo “===> Building zip archive for ${PACKAGE_NAME} version ${VERSION}…”
gitアーカイブを作成し、vendorなど不要なものを除外してzip化
git archive –format=zip –prefix=”${PACKAGE_NAME#/}-${VERSION}/” HEAD -o “${ARCHIVE_NAME}”
echo “===> Uploading archive to storage server…”
例: AWS S3や社内WebDAVへのアップロード
curl -T “${ARCHIVE_NAME}” https://packages.internal.net/archives/${ARCHIVE_NAME}
echo “===> Done. Remember to update packages.json or trigger the json-rebuilder Lambda.”
これを自動化するサーバーレスなLambdaや軽量スクリプトを1つ用意しておくだけで、社内パッケージ管理の運用コストは限りなくゼロに近づきます。
—
最後に:ツールに振り回されず、開発の「動脈」をデザインせよ
今回紹介した「Satisレスな静的 `packages.json` によるCustom Repository運用」は、一見すると地味なアプローチに見えるかもしれません。しかし、大規模な基盤や複雑なツールチェインに依存しないからこそ、「壊れない」「軽い」「誰でも中身が理解できる」 という、インフラストラクチャにおける最重要の美徳を備えています。
無駄なレイヤーを削ぎ落とし、Composerの本質的な仕様をハックすることで、チームのバックエンド開発スピードは劇的に加速します。ぜひ、あなたのチームの次期アーキテクチャに組み込み、その快適さを実感してください。