PHPプロジェクト用のパブリック/プライベートマルチモノレポの作成
公開: 2022-03-10開発エクスペリエンスを高速化するために、プロジェクトに必要なすべてのPHPパッケージをmonorepoに移動しました。 各パッケージが独自のリポジトリでホストされている場合(マルチリポジトリアプローチ)、Composerを介して他のパッケージにインストールする前に、独自に開発してテストし、Packagistに公開する必要があります。 monorepoを使用すると、すべてのパッケージが一緒にホストされるため、開発、テスト、バージョン管理、およびリリースを同時に行うことができます。
私のPHPパッケージをホストするmonorepoは公開されており、GitHubの誰でもアクセスできます。 Gitリポジトリは、異なるアセットへの異なるアクセスを許可することはできません。 それはすべてパブリックまたはプライベートのいずれかです。 私はプロのWordPressプラグインをリリースする予定なので、そのパッケージを非公開にしておく必要があります。つまり、パブリックモノリポジトリに追加することはできません。
私が見つけた解決策は、2つのモノリポジトリで構成される「マルチモノリポジトリ」アプローチを使用することです。1つはパブリックともう1つはプライベートで、プライベートモノリポジトリはパブリックをGitサブモジュールとして埋め込み、ファイルにアクセスできるようにします。 パブリックモノレポはアップストリームモノレポ、プライベートモノレポはダウンストリームモノレポと見なすことができます。
コードを繰り返し処理するにつれて、プロジェクトの各段階で使用する必要のあるリポジトリの設定もアップグレードする必要がありました。 したがって、私は初日にマルチモノレポアプローチに到達しませんでした。 それは数年にわたるプロセスであり、単一のリポジトリから複数のリポジトリ、モノリポジトリ、そして最終的にはマルチモノリポジトリに至るまで、かなりの労力を要しました。
この記事では、PHPプロジェクトで機能し、Composerに基づくMonorepoBuilderを使用してマルチモノレポをセットアップする方法について説明します。
マルチモノレポでのコードの再利用
leoloso/PoP
のパブリックmonorepoは、すべてのPHPプロジェクトを保持する場所です。
このmonorepoには、ワークフローファイルgenerate_plugins.yml
が含まれています。このファイルは、GitHubでリリースを作成するときに、配布用に複数のWordPressプラグインを生成します。
ワークフロー構成はYAMLファイルにハードコーディングされていませんが、PHPコードを介して挿入されています。
- id: output_data run: | echo "::set-output name=plugin_config_entries::$(vendor/bin/monorepo-builder plugin-config-entries-json)"
また、構成はカスタムPHPクラスを介して提供されます。
class PluginDataSource { public function getPluginConfigEntries(): array { return [ // GraphQL API for WordPress [ 'path' => 'layers/GraphQLAPIForWP/plugins/graphql-api-for-wp', 'zip_file' => 'graphql-api.zip', 'main_file' => 'graphql-api.php', 'dist_repo_organization' => 'GraphQLAPI', 'dist_repo_name' => 'graphql-api-for-wp-dist', ], // GraphQL API - Extension Demo [ 'path' => 'layers/GraphQLAPIForWP/plugins/extension-demo', 'zip_file' => 'graphql-api-extension-demo.zip', 'main_file' => 'graphql-api-extension-demo.php', 'dist_repo_organization' => 'GraphQLAPI', 'dist_repo_name' => 'extension-demo-dist', ], ]; } }
複数のWordPressプラグインをまとめて生成し、PHPを介してワークフローを構成することで、プロジェクトの管理に必要な時間を短縮できました。 ワークフローは現在、2つのプラグイン(GraphQL APIとその拡張デモ)を処理しますが、私が追加の作業をしなくても200を処理できます。
leoloso/GraphQLAPI-PRO
でプライベートモノレポに再利用したいのはこの設定です。これにより、プロのプラグインも簡単に生成できます。
再利用されるコードは次のもので構成されます。
- WordPressプラグインを生成するためのGitHubActionsワークフロー(スコープ、PHP 8.0から7.1へのダウングレード、リリースページへのアップロードを含む)。
- ワークフローを構成するためのカスタムPHPサービス。
プライベートmonorepoは、パブリックmonorepoからワークフローをトリガーし、PHPでの構成をオーバーライドするだけで、プロのWordPressプラグインを生成できます。
Gitサブモジュールを介したMonoreposのリンク
パブリックリポジトリをプライベートリポジトリに埋め込むには、Gitサブモジュールを使用します。
git submodule add <public repo URL>
パブリックリポジトリをプライベートモノリポジトリのサブフォルダsubmodules
に埋め込み、必要に応じて将来的にアップストリームモノリポジトリを追加できるようにしました。 GitHubでは、フォルダーにサブモジュールの特定のコミットが表示され、それをクリックすると、 leoloso/PoP
でそのコミットに移動します。
プライベートリポジトリにはサブモジュールが含まれているため、クローンを作成するには、 --recursive
オプションを指定する必要があります。
git clone --recursive <private repo URL>
GitHubアクションワークフローの再利用
GitHub Actionsは、 .github/workflows
の下からのみワークフローをロードします。 ダウンストリームのモノリポジトリのパブリックワークフローはsubmodules/PoP/.github/workflows
の下にあるため、これらは予想される場所に複製する必要があります。
信頼できる唯一の情報源としてアップストリームワークフローを維持するために、 .github/workflows
の下でファイルをダウンストリームにコピーするように制限できますが、そこで編集することはできません。 変更を行う場合は、アップストリームのモノリポジトリで行ってからコピーする必要があります。
補足として、これがマルチモノレポのリークを意味することに注意してください。上流のモノレポは完全に自律的ではなく、下流のモノレポに適合するように適合させる必要があります。
ワークフローをコピーする最初の反復では、単純なComposerスクリプトを作成しました。
{ "scripts": { "copy-workflows": [ "php -r \"copy('submodules/PoP/.github/workflows/generate_plugins.yml', '.github/workflows/generate_plugins.yml');\"", "php -r \"copy('submodules/PoP/.github/workflows/split_monorepo.yaml', '.github/workflows/split_monorepo.yaml');\"" ] } }
次に、アップストリームのモノリポジトリでワークフローを編集した後、次の手順を実行してワークフローをダウンストリームにコピーします。
composer copy-workflows
しかし、ワークフローをコピーするだけでは不十分であることに気付きました。ワークフローもプロセスで変更する必要があります。 これは、ダウンストリームのモノリポジトリをチェックアウトするには、サブモジュールもチェックアウトするためにオプション--recurse-submodules
が必要なためです。
GitHub Actionsでは、ダウンストリームのチェックアウトは次のように行われます。
- uses: actions/checkout@v2 with: submodules: recursive
したがって、ダウンストリームリポジトリをチェックアウトするには、入力submodules: recursive
ですが、アップストリームリポジトリには必要ありません。どちらも、同じソースファイルを使用します。
私が見つけた解決策は、環境変数CHECKOUT_SUBMODULES
を介して入力submodules
の値を提供することです。これは、デフォルトではアップストリームリポジトリでは空です。
env: CHECKOUT_SUBMODULES: "" jobs: provide_data: steps: - uses: actions/checkout@v2 with: submodules: ${{ env.CHECKOUT_SUBMODULES }}
次に、ワークフローをアップストリームからダウンストリームにコピーするときに、 CHECKOUT_SUBMODULES
の値がrecursive
に置き換えられます。
env: CHECKOUT_SUBMODULES: "recursive"
ワークフローを変更するときは、正規表現(regex)を使用して、ソースファイルのさまざまな形式( CHECKOUT_SUBMODULES: ""
またはCHECKOUT_SUBMODULES:''
またはCHECKOUT_SUBMODULES:
で機能するようにすることをお勧めします。 これにより、これらの種類の表面上は無害な変更に対してバグが作成されるのを防ぐことができます。
したがって、上記のcopy-workflows
Composerスクリプトは、この複雑さを処理するには十分ではありません。
次の反復では、MonorepoBuilderを介して実行されるPHPコマンドCopyUpstreamMonorepoFilesCommand
を作成しました。
vendor/bin/monorepo-builder copy-upstream-monorepo-files
このコマンドは、カスタムサービスFileCopierSystem
を使用して、すべてのファイルをソースフォルダーから指定された宛先にコピーし、オプションでその内容を置き換えます。
namespace PoP\GraphQLAPIPRO\Extensions\Symplify\MonorepoBuilder\SmartFile; use Nette\Utils\Strings; use Symplify\SmartFileSystem\Finder\SmartFinder; use Symplify\SmartFileSystem\SmartFileSystem; final class FileCopierSystem { public function __construct( private SmartFileSystem $smartFileSystem, private SmartFinder $smartFinder, ) { } /** * @param array $patternReplacements a regex pattern to search, and its replacement */ public function copyFilesFromFolder( string $fromFolder, string $toFolder, array $patternReplacements = [] ): void { $smartFileInfos = $this->smartFinder->find([$fromFolder], '*'); foreach ($smartFileInfos as $smartFileInfo) { $fromFile = $smartFileInfo->getRealPath(); $fileContent = $this->smartFileSystem->readFile($fromFile); foreach ($patternReplacements as $pattern => $replacement) { $fileContent = Strings::replace($fileContent, $pattern, $replacement); } $toFile = $toFolder . substr($fromFile, strlen($fromFolder)); $this->smartFileSystem->dumpFile($toFile, $fileContent); } } }
namespace PoP\GraphQLAPIPRO\Extensions\Symplify\MonorepoBuilder\SmartFile; use Nette\Utils\Strings; use Symplify\SmartFileSystem\Finder\SmartFinder; use Symplify\SmartFileSystem\SmartFileSystem; final class FileCopierSystem { public function __construct( private SmartFileSystem $smartFileSystem, private SmartFinder $smartFinder, ) { } /** * @param array $patternReplacements a regex pattern to search, and its replacement */ public function copyFilesFromFolder( string $fromFolder, string $toFolder, array $patternReplacements = [] ): void { $smartFileInfos = $this->smartFinder->find([$fromFolder], '*'); foreach ($smartFileInfos as $smartFileInfo) { $fromFile = $smartFileInfo->getRealPath(); $fileContent = $this->smartFileSystem->readFile($fromFile); foreach ($patternReplacements as $pattern => $replacement) { $fileContent = Strings::replace($fileContent, $pattern, $replacement); } $toFile = $toFolder . substr($fromFile, strlen($fromFolder)); $this->smartFileSystem->dumpFile($toFile, $fileContent); } } }
このメソッドを呼び出してすべてのワークフローをダウンストリームにコピーするときは、 CHECKOUT_SUBMODULES
の値も置き換えます。
/** * Copy all workflows to `.github/`, and convert: * `CHECKOUT_SUBMODULES: ""` * into: * `CHECKOUT_SUBMODULES: "recursive"` */ $regexReplacements = [ '#CHECKOUT_SUBMODULES:(\s+".*")?#' => 'CHECKOUT_SUBMODULES: "recursive"', ]; (new FileCopierSystem())->copyFilesFromFolder( 'submodules/PoP/.github/workflows', '.github/workflows', $regexReplacements );
generate_plugins.yml
のワークフローには、追加の置換が必要です。 WordPressプラグインが生成されると、スクリプトci/downgrade/downgrade_code.sh
を呼び出すことにより、そのコードがPHP8.0から7.1にダウングレードされます。
- name: Downgrade code for production (to PHP 7.1) run: ci/downgrade/downgrade_code.sh "${{ matrix.pluginConfig.rector_downgrade_config }}" "" "${{ matrix.pluginConfig.path }}" "${{ matrix.pluginConfig.additional_rector_configs }}"
ダウンストリームのモノリポジトリでは、このファイルはsubmodules/PoP/ci/downgrade/downgrade_code.sh
の下にあります。 次に、この置換を使用して、ダウンストリームワークフローを正しいパスに向けます。
$regexReplacements = [ // ... '#(ci/downgrade/downgrade_code\.sh)#' => 'submodules/PoP/$1', ];
MonorepoBuilderでのパッケージの構成
monorepoのルートにあるファイルmonorepo-builder.php
は、MonorepoBuilderの構成を保持しています。 その中で、パッケージ(およびプラグイン、クライアント、その他)がどこにあるかを示す必要があります。
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symplify\MonorepoBuilder\ValueObject\Option; return static function (ContainerConfigurator $containerConfigurator): void { $parameters = $containerConfigurator->parameters(); $parameters->set(Option::PACKAGE_DIRECTORIES, [ __DIR__ . '/packages', __DIR__ . '/plugins', ]); };
プライベートmonorepoは、すべてのコードにアクセスできる必要があります。独自のパッケージに加えて、パブリックmonorepoからのパッケージです。 次に、構成ファイル内の両方のモノリポジトリからすべてのパッケージを定義する必要があります。 パブリックmonorepoからのものは/submodules/PoP
の下にあります:
return static function (ContainerConfigurator $containerConfigurator): void { $parameters = $containerConfigurator->parameters(); $parameters->set(Option::PACKAGE_DIRECTORIES, [ // public code __DIR__ . '/submodules/PoP/packages', __DIR__ . '/submodules/PoP/plugins', // private code __DIR__ . '/packages', __DIR__ . '/plugins', __DIR__ . '/clients', ]); };
現状では、アップストリームとダウンストリームの構成はほとんど同じですが、ダウンストリームの構成は次のようになります。
- パブリックパッケージへのパスを変更し、
- プライベートパッケージを追加します。
したがって、オブジェクト指向プログラミング(OOP)を使用して構成を書き直すことは理にかなっています。 パブリックリポジトリのPHPクラスをプライベートリポジトリで拡張することにより、DRYの原則(「繰り返さないでください」)に従いましょう。
OOPを介した構成の再作成
構成をリファクタリングしてみましょう。 パブリックリポジトリでは、ファイルmonorepo-builder.php
は、新しいクラスContainerConfigurationService
を参照するだけで、すべてのアクションが実行されます。
use PoP\PoP\Config\Symplify\MonorepoBuilder\Configurators\ContainerConfigurationService; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $containerConfigurationService = new ContainerConfigurationService( $containerConfigurator, __DIR__ ); $containerConfigurationService->configureContainer(); };
__DIR__
パラメーターは、モノリポジトリのルートを指します。 パッケージディレクトリへのフルパスを取得する必要があります。
これで、 ContainerConfigurationService
クラスが構成の生成を担当します。
namespace PoP\PoP\Config\Symplify\MonorepoBuilder\Configurators; use PoP\PoP\Config\Symplify\MonorepoBuilder\DataSources\PackageOrganizationDataSource; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symplify\MonorepoBuilder\ValueObject\Option; class ContainerConfigurationService { public function __construct( protected ContainerConfigurator $containerConfigurator, protected string $rootDirectory, ) { } public function configureContainer(): void { $parameters = $this->containerConfigurator->parameters(); if ($packageOrganizationConfig = $this->getPackageOrganizationDataSource($this->rootDirectory)) { $parameters->set( Option::PACKAGE_DIRECTORIES, $packageOrganizationConfig->getPackageDirectories() ); } } protected function getPackageOrganizationDataSource(): ?PackageOrganizationDataSource { return new PackageOrganizationDataSource($this->rootDirectory); } }
構成は複数のクラスに分割できます。 この場合、 ContainerConfigurationService
は、クラスPackageOrganizationDataSource
を介してパッケージ構成を取得します。このクラスの実装は、次のとおりです。
namespace PoP\PoP\Config\Symplify\MonorepoBuilder\DataSources; class PackageOrganizationDataSource { public function __construct(protected string $rootDir) { } public function getPackageDirectories(): array { return array_map( fn (string $packagePath) => $this->rootDir . '/' . $packagePath, $this->getRelativePackagePaths() ); } public function getRelativePackagePaths(): array { return [ 'packages', 'plugins', ]; } }
ダウンストリームMonorepoでの構成のオーバーライド
パブリックモノレポの構成がOOPを使用してセットアップされたので、プライベートモノレポのニーズに合わせて構成を拡張できます。
プライベートmonorepoがパブリックmonorepoからPHPコードを自動ロードできるようにするには、最初に、パスsubmodules/PoP/src
の下にあるアップストリームからのソースコードを参照するようにダウンストリームcomposer.json
ファイルを構成する必要があります。
{ "autoload": { "psr-4": { "PoP\\GraphQLAPIPRO\\": "src", "PoP\\PoP\\": "submodules/PoP/src" } } }
以下は、プライベートmonorepoのmonorepo-builder.php
ファイルです。 アップストリームリポジトリで参照されているクラスContainerConfigurationService
はPoP\PoP
名前空間に属していましたが、 PoP\GraphQLAPIPRO
名前空間に切り替えられていることに注意してください。 このクラスは、パブリックパッケージへのフルパスを再作成するために、( submodules/PoP
の値を持つ) $upstreamRelativeRootPath
の追加入力を受け取る必要があります。
use PoP\GraphQLAPIPRO\Config\Symplify\MonorepoBuilder\Configurators\ContainerConfigurationService; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $containerConfigurationService = new ContainerConfigurationService( $containerConfigurator, __DIR__, 'submodules/PoP' ); $containerConfigurationService->configureContainer(); };
ダウンストリームクラスContainerConfigurationService
は、構成で使用されるPackageOrganizationDataSource
クラスをオーバーライドします。
namespace PoP\GraphQLAPIPRO\Config\Symplify\MonorepoBuilder\Configurators; use PoP\PoP\Config\Symplify\MonorepoBuilder\Configurators\ContainerConfigurationService as UpstreamContainerConfigurationService; use PoP\GraphQLAPIPRO\Config\Symplify\MonorepoBuilder\DataSources\PackageOrganizationDataSource; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; class ContainerConfigurationService extends UpstreamContainerConfigurationService { public function __construct( ContainerConfigurator $containerConfigurator, string $rootDirectory, protected string $upstreamRelativeRootPath ) { parent::__construct( $containerConfigurator, $rootDirectory ); } protected function getPackageOrganizationDataSource(): ?PackageOrganizationDataSource { return new PackageOrganizationDataSource( $this->rootDirectory, $this->upstreamRelativeRootPath ); } }
最後に、ダウンストリームクラスPackageOrganizationDataSource
には、パブリックパッケージとプライベートパッケージの両方へのフルパスが含まれています。
namespace PoP\GraphQLAPIPRO\Config\Symplify\MonorepoBuilder\DataSources; use PoP\PoP\Config\Symplify\MonorepoBuilder\DataSources\PackageOrganizationDataSource as UpstreamPackageOrganizationDataSource; class PackageOrganizationDataSource extends UpstreamPackageOrganizationDataSource { public function __construct( string $rootDir, protected string $upstreamRelativeRootPath ) { parent::__construct($rootDir); } public function getRelativePackagePaths(): array { return array_merge( // Public packages - Prepend them with "submodules/PoP/" array_map( fn ($upstreamPackagePath) => $this->upstreamRelativeRootPath . '/' . $upstreamPackagePath, parent::getRelativePackagePaths() ), // Private packages [ 'packages', 'plugins', 'clients', ] ); } }
PHPからGitHubアクションへの構成の挿入
Monorepo Builderはコマンドpackages-json
を提供します。これを使用して、パッケージパスをGitHubアクションワークフローに挿入できます。
jobs: provide_data: steps: - id: output_data name: Calculate matrix for packages run: | echo "::set-output name=matrix::$(vendor/bin/monorepo-builder packages-json)" outputs: matrix: ${{ steps.output_data.outputs.matrix }}
このコマンドは、文字列化されたJSONを生成します。 ワークフローでは、 fromJson
を介してJSONオブジェクトに変換する必要があります。
jobs: split_monorepo: needs: provide_data strategy: matrix: package: ${{ fromJson(needs.provide_data.outputs.matrix) }}
残念ながら、コマンドpackages-json
はパッケージ名を出力しますが、パスは出力しません。 これは、すべてのパッケージが同じフォルダー( packages/
など)の下にある場合は機能しますが、パブリックパッケージとプライベートパッケージが異なるフォルダーにあるため、この場合は機能しません。
幸い、MonorepoBuilderはカスタムPHPサービスで拡張できます。 そこで、パッケージへのパスを出力するカスタムコマンドpackage-entries-json
(クラスPackageEntriesJsonCommand
を介して)を作成しました。
次に、ワークフローが新しいコマンドで更新されました。
run: | echo "::set-output name=matrix::$(vendor/bin/monorepo-builder package-entries-json)"
パブリックモノリポジトリで実行すると、次のパッケージが生成されます(他の多くのパッケージの中でも)。
[ { "name": "graphql-api-for-wp", "path": "layers/GraphQLAPIForWP/plugins/graphql-api-for-wp" }, { "name": "extension-demo", "path": "layers/GraphQLAPIForWP/plugins/extension-demo" }, { "name": "access-control", "path": "layers/Engine/packages/access-control" }, { "name": "api", "path": "layers/API/packages/api" }, { "name": "api-clients", "path": "layers/API/packages/api-clients" } ]
プライベートモノリポジトリで実行すると、次のエントリが生成されます(他の多くのエントリの中でも)。
[ { "name": "graphql-api-for-wp", "path": "submodules/PoP/layers/GraphQLAPIForWP/plugins/graphql-api-for-wp" }, { "name": "extension-demo", "path": "submodules/PoP/layers/GraphQLAPIForWP/plugins/extension-demo" }, { "name": "access-control", "path": "submodules/PoP/layers/Engine/packages/access-control" }, { "name": "api", "path": "submodules/PoP/layers/API/packages/api" }, { "name": "api-clients", "path": "submodules/PoP/layers/API/packages/api-clients" }, { "name": "graphql-api-pro", "path": "layers/GraphQLAPIForWP/plugins/graphql-api-pro" }, { "name": "convert-case-directives", "path": "layers/Schema/packages/convert-case-directives" }, { "name": "export-directive", "path": "layers/GraphQLByPoP/packages/export-directive" } ]
それは非常にうまく機能します。 ダウンストリームモノリポジトリの構成には、パブリックパッケージとプライベートパッケージの両方が含まれ、パブリックパッケージへのパスの前にsubmodules/PoP
が追加されます。
ダウンストリームMonorepoでパブリックパッケージをスキップする
これまでのところ、ダウンストリームのモノリポジトリには、構成にパブリックパッケージとプライベートパッケージの両方が含まれています。 ただし、すべてのコマンドをパブリックパッケージで実行する必要はありません。
たとえば、静的分析を考えてみましょう。 パブリックmonorepoは、この実行に示されているように、ワークフローファイルphpstan.yml
を介してすべてのパブリックパッケージでPHPStanをすでに実行しています。 ダウンストリームのモノリポジトリがパブリックパッケージでPHPStanを再度実行した場合、計算時間の無駄になります。 phpstan.yml
ワークフローは、プライベートパッケージでのみ実行する必要があります。
つまり、ダウンストリームリポジトリで実行するコマンドに応じて、パブリックパッケージとプライベートパッケージの両方を含めるか、プライベートパッケージのみを含めることができます。
ダウンストリーム構成にパブリックパッケージを追加するかどうかを決定するために、ダウンストリームクラスPackageOrganizationDataSource
を適合させて、入力$includeUpstreamPackages
を介してこの条件をチェックします。
namespace PoP\GraphQLAPIPRO\Config\Symplify\MonorepoBuilder\DataSources; use PoP\PoP\Config\Symplify\MonorepoBuilder\DataSources\PackageOrganizationDataSource as UpstreamPackageOrganizationDataSource; class PackageOrganizationDataSource extends UpstreamPackageOrganizationDataSource { public function __construct( string $rootDir, protected string $upstreamRelativeRootPath, protected bool $includeUpstreamPackages ) { parent::__construct($rootDir); } public function getRelativePackagePaths(): array { return array_merge( // Add the public packages? $this->includeUpstreamPackages ? // Public packages - Prepend them with "submodules/PoP/" array_map( fn ($upstreamPackagePath) => $this->upstreamRelativeRootPath . '/' . $upstreamPackagePath, parent::getRelativePackagePaths() ) : [], // Private packages [ 'packages', 'plugins', 'clients', ] ); } }
次に、実行するコマンドに応じて、値$includeUpstreamPackages
をtrue
またはfalse
として指定する必要があります。
これを行うには、構成ファイルmonorepo-builder.php
を他の2つの構成ファイル: monorepo-builder-with-upstream-packages.php
( $includeUpstreamPackages
=> true
を渡す)とmonorepo-builder-without-upstream-packages.php
。 monorepo-builder-without-upstream-packages.php
( $includeUpstreamPackages
=> false
を渡します):
// File monorepo-builder-without-upstream-packages.php use PoP\GraphQLAPIPRO\Config\Symplify\MonorepoBuilder\Configurators\ContainerConfigurationService; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $containerConfigurationService = new ContainerConfigurationService( $containerConfigurator, __DIR__, 'submodules/PoP', false, // This is $includeUpstreamPackages ); $containerConfigurationService->configureContainer(); };
次に、 ContainerConfigurationService
を更新して、パラメーター$includeUpstreamPackages
を受け取り、それをPackageOrganizationDataSource
に渡します。
namespace PoP\GraphQLAPIPRO\Config\Symplify\MonorepoBuilder\Configurators; use PoP\PoP\Config\Symplify\MonorepoBuilder\Configurators\ContainerConfigurationService as UpstreamContainerConfigurationService; use PoP\GraphQLAPIPRO\Config\Symplify\MonorepoBuilder\DataSources\PackageOrganizationDataSource; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; class ContainerConfigurationService extends UpstreamContainerConfigurationService { public function __construct( ContainerConfigurator $containerConfigurator, string $rootDirectory, protected string $upstreamRelativeRootPath, protected bool $includeUpstreamPackages, ) { parent::__construct( $containerConfigurator, $rootDirectory, ); } protected function getPackageOrganizationDataSource(): ?PackageOrganizationDataSource { return new PackageOrganizationDataSource( $this->rootDirectory, $this->upstreamRelativeRootPath, $this->includeUpstreamPackages, ); } }
次に、 --config
オプションを指定して、いずれかの構成ファイルでmonorepo-builder
を呼び出す必要があります。
jobs: provide_data: steps: - id: output_data name: Calculate matrix for packages run: | echo "::set-output name=matrix::$(vendor/bin/monorepo-builder package-entries-json --config=monorepo-builder-without-upstream-packages.php)"
ただし、前に見たように、GitHub Actionsワークフローを信頼できる唯一の情報源としてアップストリームのモノリポジトリに保持したいので、これらの変更は明らかに必要ありません。
この問題に対する私が見つけた解決策は、アップストリームリポジトリに常に--config
オプションを提供し、各コマンドが独自の構成ファイルを取得することです。たとえば、 validate
コマンドはvalidate.php
構成ファイルを受け取ります。
- name: Run validation run: vendor/bin/monorepo-builder validate --config=config/monorepo-builder/validate.php
現在、アップストリームのモノリポジトリには構成ファイルが必要ないため、構成ファイルはありません。 ただし、Monorepo Builderは構成ファイルが存在するかどうかをチェックし、存在しない場合は代わりにデフォルトの構成ファイルをロードするため、破損することはありません。 したがって、構成をオーバーライドするか、何も起こりません。
ダウンストリームリポジトリは、各コマンドの構成ファイルを提供し、アップストリームパッケージを追加するかどうかを指定します。
ちなみに、これはマルチモノレポのリークのもう1つの例です。
// File config/monorepo-builder/validate.php return require_once __DIR__ . '/monorepo-builder-with-upstream-packages.php';
構成のオーバーライド
ほぼ完了です。 これで、ダウンストリームのモノレポはアップストリームのモノレポからの構成をオーバーライドできます。 したがって、あとは新しい構成を提供するだけです。
PluginDataSource
クラスで、WordPressプラグインを生成する必要がある構成をオーバーライドし、代わりにプロのプラグインを提供します。
namespace PoP\GraphQLAPIPRO\Config\Symplify\MonorepoBuilder\DataSources; use PoP\PoP\Config\Symplify\MonorepoBuilder\DataSources\PluginDataSource as UpstreamPluginDataSource; class PluginDataSource extends UpstreamPluginDataSource { public function getPluginConfigEntries(): array { return [ // GraphQL API PRO [ 'path' => 'layers/GraphQLAPIForWP/plugins/graphql-api-pro', 'zip_file' => 'graphql-api-pro.zip', 'main_file' => 'graphql-api-pro.php', 'dist_repo_organization' => 'GraphQLAPI-PRO', 'dist_repo_name' => 'graphql-api-pro-dist', ], // GraphQL API Extensions // Google Translate [ 'path' => 'layers/GraphQLAPIForWP/plugins/google-translate', 'zip_file' => 'graphql-api-google-translate.zip', 'main_file' => 'graphql-api-google-translate.php', 'dist_repo_organization' => 'GraphQLAPI-PRO', 'dist_repo_name' => 'graphql-api-google-translate-dist', ], // Events Manager [ 'path' => 'layers/GraphQLAPIForWP/plugins/events-manager', 'zip_file' => 'graphql-api-events-manager.zip', 'main_file' => 'graphql-api-events-manager.php', 'dist_repo_organization' => 'GraphQLAPI-PRO', 'dist_repo_name' => 'graphql-api-events-manager-dist', ], ]; } }
GitHubで新しいリリースを作成すると、 generate_plugins.yml
ワークフローがトリガーされ、プライベートモノリポジトリにプロプラグインが生成されます。
タダ!
結論
いつものように、「最良の」解決策はなく、状況に応じてより適切に機能する可能性のある解決策のみがあります。 マルチモノレポアプローチは、あらゆる種類のプロジェクトやチームに適しているわけではありません。 最大の受益者は、プロバージョンにアップグレードされるパブリックプラグインをリリースするプラグイン作成者と、クライアント用にプラグインをカスタマイズするエージェンシーだと思います。
私の場合、このアプローチには非常に満足しています。 それを正しく行うには少し時間と労力がかかりますが、それは1回限りの投資です。 セットアップが完了したら、プロプラグインの作成に集中でき、プロジェクト管理で節約できる時間は膨大になる可能性があります。