baserCMS 通常プラグイン → コアプラグイン 昇格手順
サードパーティ/単体配布の baserCMS プラグインを、baserCMS 本体(monorepo)のコアプラグインとして取り込む手順。
参照: plugins/bc-blog 等の既存コアプラグインが「正」の構成。
前提と役割分担
- バージョン移行(5.2→5.3 / PHP8.5)でのコード非推奨対応は
basercms-plugin-5x-updateスキル。- ユニットテストの実行・集計・切り分けは
basercms-unittestスキル。- 本書は「通常プラグイン構成 → コアプラグイン構成」への構造変換に絞る。
0. 大原則 / 完成形
- コアプラグインは
plugins/<bc-name>/に置かれ、自前の.git/vendor//bin//composer.lock/phpunit.xml.dist/tests/bootstrap.php/tests/TestApp/を持たない。テストはアプリ全体のテスト基盤で実行する。 - 依存・オートロード・テスト登録・CI 登録はルート側に集約する。
- 完成形の例(
plugins/bc-blog):README.md / composer.json / config/ / config.php / src/ / tests/ / webroot/(+必要ならwebpack.config.js)。
1. プラグイン名をコアプラグイン規約へ変更
既に規約準拠の名前なら本節はスキップ。命名規約の詳細リネーム作業自体は別途 namespace 一括置換が必要(本スキルは「コア化に伴う命名規約」のみ規定)。
- フォルダ名: ケバブケースにし、
bc-プレフィックスを付与する。- CamelCase はハイフン区切りへ。例:
CuMcp→bc-mcp、MyAwesomePlugin→bc-my-awesome-plugin。
- CamelCase はハイフン区切りへ。例:
- namespace / プラグインクラス: パスカルケースで
Bcプレフィックス。例:BcMcp、メインクラスBcMcpPlugin。 - composer パッケージ名:
baserproject/<bc-name>。例:baserproject/bc-mcp。 - フォルダ名(ケバブ)と namespace(パスカル)の対応:
bc-mcp⇔BcMcp。 - 命名に伴う
namespace/use/ 文字列リテラル('plugin' => 'Xxx'・URL スラッグ/xxx・コマンド名・設定キー)の一括置換、composer.jsonのname/autoload/extra.cakephp.plugin-name変更も行う(誤爆防止のため先頭トークンのみ置換し、Mcp/BcBlogのようなサブ階層名は温存)。
2. プラグイン側の standalone 成果物を削除
monorepo では不要・有害(vendor 二重化や nested git)なものを削除する。
plugins/<bc-name>/
.git ← nested リポジトリを削除(親 monorepo に取り込む)
vendor/ ← 自前 vendor を削除(ルート vendor を使う)
bin/ ← 自前 cake バイナリを削除(ROOT/bin/cake を使う)
composer.lock ← 削除
phpunit.xml.dist ← 削除(ルート phpunit.xml.dist に testsuite を登録する)
tests/bootstrap.php ← 削除(ルート tests/bootstrap.php を使う)
tests/TestApp/ ← 削除(ルート tests/TestApp を使う)
.github/ ← 単体 CI は不要(split 先リポジトリでも別管理)。既存コアに無ければ削除
.gitignore ← 既存コアに無ければ削除
VERSION.txt ← 削除(バージョンは本体 monorepo 管理になる)
CHANGELOG.md ← 削除(変更履歴も本体管理)
.phpunit.cache / .phpunit.result.cache / .DS_Store / .idea ← 削除
- 完成形は既存コア(
plugins/bc-blog)に合わせる:README.md / composer.json / config/ / config.php / src/ / tests/ / webroot/(+必要ならtemplates/・webpack.config.js)。 - ⚠️
.git削除は不可逆。nested git の独自履歴が必要なら事前に退避。
3. ルート .gitignore の調整(2 種類・向きが逆なので注意)
3-1. プラグイン本体を「追跡対象」にする(無視の除外 !)
ルート .gitignore は /plugins/* で全プラグインを無視し、コアプラグインだけ ! でホワイトリストしている。コア化したプラグインを !/plugins/<bc-name> で追跡対象に加える(これを忘れると git add されず取り込まれない)。
# plugins
/plugins/*
!/plugins/baser-core
...
!/plugins/bc-seo
!/plugins/bc-mcp ← 追加(無視の除外=追跡対象化)
/plugins/*/vendor
/plugins/*/composer.lock
- 確認:
git check-ignore -v plugins/<bc-name>/composer.json(何も返らなければ追跡対象)、git status --short plugins/<bc-name>。
3-2. webroot シンボリックリンクは「無視」する
baserCMS は webroot/<plugin_underscored> → plugins/<bc-name>/webroot のシンボリックリンクを生成する(例 webroot/bc_mcp)。実体は plugins/<bc-name>/webroot にあるため、リンク側はルート .gitignore で無視する(他コアと同じ並び)。
/webroot/bc_blog
/webroot/bc_custom_content
/webroot/bc_mcp ← 追加(アンダースコア表記)
向きに注意:
plugins/<bc-name>は追跡(!で除外)/webroot/<underscored>は無視。
4. テストを「全体実行」に載せ替える
4-1. ルート phpunit.xml.dist に testsuite を追加(⚠️ 配置順が重要)
(プラグイン側 phpunit.xml.dist は 2. で削除済み。追加するのはルート側。)
<testsuite name="BcMcp">
<directory>plugins/bc-mcp/tests/TestCase</directory>
</testsuite>
- ⚠️ DB テーブルを持つが
defaultInstallCorePluginsに入れないプラグインは、BcInstallerの testsuite より「前」に置く。BcInstallerのInstallationsControllerTest::testStep3(mode=createDb)は実際のインストール処理で全テーブルを drop し defaultInstall のプラグインのみ再作成する。後ろに置くと、自プラグインのテーブルが作り直されないまま自テストが走り全アクション 500 になる(既存の非 default コア=BcContentLink/BcCustomContent 等もすべて BcInstaller より前に並んでいる)。- 例:
... BcSeoの直後・BcInstallerの直前にBcMcpを置く。 - defaultInstall に入れる方針なら BcInstaller の後でも可(createDb が作り直すため)。
- 例:
4-2. マイグレーション・プラグインリストへ追加(⚠️ 複数箇所ある)
プラグインが DB マイグレーション(config/Migrations)を持つ場合、テスト用 DB にテーブルを作る/再構築するプラグインリストすべてに追加する。1 箇所でも漏れると、全体実行時にそのプラグインのテーブルだけ欠落し Could not describe columns on <table>(→ コントローラ初期化失敗で当該プラグインの全アクション 500)になる。単独 testsuite では通り、全体実行でのみ落ちるのが特徴(切り分けは basercms-plugin-5x-update のテスト基盤節も参照)。
- ルート
tests/bootstrap.phpのMigrator::runMany(初期スキーマ構築)(new Migrator())->runMany([ ['plugin' => 'BaserCore'], ... ['plugin' => 'BcMcp'], // 追加 ]); - 「全テーブル drop → 再マイグレーション」をするテストの $plugins 配列(複数あり)。これらは
deleteTables()/deleteAllTables()で全テーブルを drop した後、ハードコードされたリストで再マイグレーションする。ここに無いとテーブルが復活せず、テスト順で先頭側に位置するため以降の全テストで欠落したままになる(実際に bc-mcp 追加時にoauth2_clientsがこれで消え、OAuth2 全テストが 500 になった)。判明している箇所(両方必須):plugins/baser-core/tests/TestCase/Service/BcDatabaseServiceTest.php→test_deleteTablesForMigrations()の$pluginsplugins/bc-installer/tests/TestCase/Service/Admin/InstallationsAdminServiceTest.php→test_deleteAllTables()の$plugins
$plugins = [ 'BaserCore', ..., 'BcMail', 'BcMcp', /* 追加 */ 'BcSearchIndex', ... ];
- 抜け漏れ確認(重要): この種の再構築リストは複数ファイルに散在する。必ず grep で全部洗う:
ヒットした各リストに新プラグインが入っているか確認する(1 つでも漏れると全体実行のみで落ちる)。grep -rn "'BcWidgetArea'," plugins --include="*.php" # 末尾要素で「コア列挙リスト」を検出 - 確実な検出法:
BcTestCase::setUp冒頭に「対象テーブルの存在を各テストで記録」する一時計測を仕込み、全 suite を実行してOK→MISSING転移点を洗い出す(転移直前の OK テストが drop 元)。全 dropper を一度に特定できる。確認後に計測は除去する。 - 切り分け診断(どのテストでテーブルが消えるか):
BcTestCase::setUp冒頭に一時計測(ConnectionManager::get('test')->getSchemaCollection()->listTables()に対象テーブルが在るか各テストで記録)を仕込み、最初に MISSING になる直前の OK テストが drop 元。確認後に必ず除去する。
4-3. プラグイン composer.json をコアプラグイン形式へ簡素化
require-dev(baser-core 等)・scripts・config.allow-plugins・minimum-stability は削除。vendor-dir を足す。外部ランタイム依存がある場合は require に残す(後で monorepo-builder merge がルートへ集約する)。
{
"name": "baserproject/bc-mcp",
"description": "BcMcp plugin for baserCMS",
"homepage": "https://basercms.net",
"type": "cakephp-plugin",
"license": "MIT",
"vendor-dir": "../../vendor",
"require": { // 外部依存が無ければ require ごと省略可(bc-blog 等)
"php": "^8.1",
"league/oauth2-server": "^8.5"
// ...プラグイン固有の外部依存
},
"autoload": { "psr-4": { "BcMcp\\": "src/" } },
"autoload-dev": { "psr-4": { "BcMcp\\Test\\": "tests/" } }
}
- テストが他コアの Test 名前空間(
BcBlog\Test等)やApp\(TestApp)を使う場合、**ルートcomposer.jsonの autoload-dev** が供給するため、プラグイン側 autoload-dev は自分のTestのみでよい。
5. システム(baserCMS / monorepo)への認識
5-1. plugins/baser-core/config/setting.php の corePlugins に追加
'corePlugins' => [ ..., 'BcMail', 'BcMcp', 'BcSeo', ... ],
5-2. defaultInstallCorePlugins への追加可否を確認
baserCMS 新規インストール時に自動有効化するなら追加(BcBlog/BcMail 等と同様)。任意機能・オプトインなら追加しない(corePlugins のみ=管理画面から手動有効化)。 → プロジェクト方針をユーザーに確認する。
5-3. ルート composer.json への集約(autoload / autoload-dev / replace / require)
原則 vendor/bin/monorepo-builder merge で集約する(monorepo-builder.php の packageDirectories([... '/plugins']) が plugins/ 配下を自動走査し、各 composer.json をルートへマージ+replace を管理)。
docker compose exec <container> sh -c "cd /var/www/html && vendor/bin/monorepo-builder merge"
- ⚠️ merge は全パッケージのバージョン整合をガードする。monorepo 外のローカル専用プラグイン(例 EgaRyu / BcUpdateSupporter 等)に版差があると
Found conflicting package versionsでブロックされる。その場合は当該プラグインの版を揃えるか、ブロック時は merge と同等の編集を手動で行う(下記)。 - merge 相当の手動編集(ルート
composer.json):autoload.psr-4に"BcMcp\\": "plugins/bc-mcp/src/"autoload-dev.psr-4に"BcMcp\\Test\\": "plugins/bc-mcp/tests/"requireにプラグインの外部依存(league/oauth2-server等、ext-*含む)replaceに"baserproject/bc-mcp": "<monorepo版>"(他コアに合わせる)
- 反映後、依存取得とオートロード再生成:
docker compose exec <container> sh -c "cd /var/www/html && composer update <added-packages> --no-interaction"
5-4. phpdoc.dist.xml に src パスを追加
<path>plugins/bc-mail/src</path>
<path>plugins/bc-mcp/src</path> <!-- 追加 -->
<path>plugins/bc-search-index/src</path>
5-5. .github/workflows/split_monorepo.yml に split 対象を追加
- local_path: 'bc-mcp'
split_repository: 'bc-mcp'
6. 全体テストでの実行確認
docker compose exec <container> sh -c "cd /var/www/html && vendor/bin/phpunit --testsuite BcMcp"
- standalone 専用 bootstrap が用意していた前提(外部プロセス・環境変数・鍵等)が全体 bootstrap には無いため、移行直後は失敗が出やすい。代表例:
- 外部プロセス依存: プロキシ統合テスト等が実サーバープロセスを要する場合、standalone では bootstrap が起動していた。全体側では該当テストの先頭で起動する(起動済みなら再利用)。起動コマンドは
ROOT/bin/cake <command>(アプリの cake と、コア登録済みのコマンド)を使う。setUp全体ではなくサーバーが要る個別テストにだけガードを入れる(他テストに起動待ちを波及させない)。 ⚠️ 起動判定は「プロセス存在(pidファイル)」だけでは不十分。プロセスは起きてもポートの bind が間に合わず、プロキシ接続先(例127.0.0.1:{port})が接続拒否=500 になる(ローカルは通り CI でのみ落ちる典型)。実際に接続できるまでポーリングで待つこと。 到達できない場合はmarkTestSkippedで隠さずassertTrue等で明示的に失敗させる(スキップはサーバー起動不具合を握りつぶす)。本方針は.github/instructions/basercms.instructions.md「開発・テスト・ビルド」にも記載。private function requireMcpServer(): void { $m = new McpServerManger(); $cfg = $m->getServerConfig(); if (!$m->isServerRunning()) { $m->startMcpServer($cfg); } $host = $cfg['host'] ?? '127.0.0.1'; $port = (int)($cfg['port'] ?? 3000); $deadline = microtime(true) + 15.0; $reachable = false; while (microtime(true) < $deadline) { $c = @fsockopen($host, $port, $e, $s, 1); if ($c) { fclose($c); $reachable = true; break; } usleep(300000); } $this->assertTrue($reachable, "MCP サーバー({$host}:{$port})へ接続できませんでした"); } // 実サーバーが要るテストの先頭で $this->requireMcpServer(); - マイグレーション未実行: 4-2 の追加漏れ → テーブル不在で失敗。
- プラグイン未 bootstrap / サブプラグイン未ロード 等は
basercms-plugin-5x-updateスキル(T-3〜T-5)参照。
- 外部プロセス依存: プロキシ統合テスト等が実サーバープロセスを要する場合、standalone では bootstrap が起動していた。全体側では該当テストの先頭で起動する(起動済みなら再利用)。起動コマンドは
- 仕上げに全体テストの回帰が無いことも確認する。
7. monorepo split 用 read-only リポジトリの確認
split_monorepo は各プラグインを baserproject/<split_repository> のread-only リポジトリへ push する。対象リポジトリが存在しないと split が失敗するため、事前に作成済みか確認する。
gh repo view baserproject/bc-mcp --json name,url,visibility
- 未作成の場合は作成する(外部・不可逆な操作。実行前にユーザーへ確認する)。
ghでの作成可否を先に確認する: 認証アカウントが対象 org(baserproject)の admin で、トークンにrepoスコープがあれば作成できる。gh auth status # ログイン/スコープ確認(repo が必要) gh api user/memberships/orgs/baserproject # role が "admin" か確認- 作成は既存 split リポジトリ(例
baserproject/bc-blog)の設定に合わせる: public / 説明[READ-ONLY] <PascalName>/ homepagehttps://basercms.net。gh repo create baserproject/bc-mcp --public -d "[READ-ONLY] BcMcp" -h https://basercms.net - 権限が無い場合は baserCMS 管理者に作成を依頼する(無断で別 owner に作らない)。
チェックリスト
- プラグイン名がコア規約(
bc-プレフィックス/ケバブ/Bcnamespace/baserproject/bc-*) -
.git/vendor/bin/composer.lock/phpunit.xml.dist/tests/bootstrap.php/TestApp/VERSION.txt/CHANGELOG.md削除 - ルート
.gitignore:!/plugins/<bc-name>で追跡対象化 +/webroot/<underscored>を無視 - ルート
phpunit.xml.distに testsuite 追加 - マイグレーション・リスト全箇所に追加(マイグレーションがある場合): ルート
tests/bootstrap.phpのrunMany+BcDatabaseServiceTest::test_deleteTablesForMigrationsの$plugins - プラグイン
composer.jsonをコア形式に簡素化(外部 require は残す) -
corePluginsに追加 /defaultInstallCorePluginsは方針確認 - ルート
composer.jsonに集約(merge もしくは手動)+composer update -
phpdoc.dist.xml/split_monorepo.ymlに追加 - 全体テストで testsuite OK(外部プロセス依存は setUp で起動)
- split 用 read-only リポジトリの存在確認
メモ:
.claude/skills/に追加・更新したスキルは、現行セッションには即時反映されない(起動時に discover)。Claude Code を再起動すると/basercms-core-plugin-convertとして呼べる。