symplify/monorepo-builder
Tools for PHP monorepos: discover packages, merge and propagate composer.json, validate dependency/version consistency, and automate releases. Configure via monorepo-builder.php and manage packages across /packages or custom directories.
A set of tools for managing PHP monorepos: merging composer.json files, validating package versions, releasing with automation, and more.
composer require monorepo-php/monorepo --dev
Requires PHP 8.2+. For PHP 8.1, use symplify/monorepo-builder:^11.2 (no longer maintained).
If you're new to monorepos, generate a basic structure:
vendor/bin/monorepo-builder init
All configuration goes in monorepo-builder.php at your project root.
By default, packages are discovered from ./packages. To customize:
use Symplify\MonorepoBuilder\Config\MBConfig;
return static function (MBConfig $mbConfig): void {
$mbConfig->packageDirectories([
__DIR__ . '/packages',
__DIR__ . '/projects',
]);
// exclude specific packages
$mbConfig->packageDirectoriesExcludes([__DIR__ . '/packages/secret-package']);
};
Merges all sections from package composer.json files into the root composer.json. For the reverse direction, see propagate.
vendor/bin/monorepo-builder merge
Behavior:
require, autoload, etc.) and custom ones (scripts-aliases, abandoned, etc.)require and require-dev, the require entry takes prioritycomposer.json is preserved; new sections are appended at the endAppend and remove data after merge:
use Symplify\MonorepoBuilder\ComposerJsonManipulator\ValueObject\ComposerJsonSection;
use Symplify\MonorepoBuilder\Config\MBConfig;
use Symplify\MonorepoBuilder\ValueObject\Option;
return static function (MBConfig $mbConfig): void {
// add data after merge (supports any composer.json key)
$mbConfig->dataToAppend([
ComposerJsonSection::AUTOLOAD_DEV => [
'psr-4' => [
'Symplify\Tests\\' => 'tests',
],
],
ComposerJsonSection::REQUIRE_DEV => [
'phpstan/phpstan' => '^2.1',
],
]);
// remove data after merge
$mbConfig->dataToRemove([
ComposerJsonSection::REQUIRE => [
// removed by key, version is irrelevant
'phpunit/phpunit' => '*',
],
ComposerJsonSection::REPOSITORIES => [
Option::REMOVE_COMPLETELY,
],
]);
};
Custom section order:
By default, the original key order is preserved. To enforce a specific order:
use Symplify\MonorepoBuilder\Config\MBConfig;
use Symplify\MonorepoBuilder\Merge\JsonSchema;
return static function (MBConfig $mbConfig): void {
$mbConfig->composerSectionOrder(JsonSchema::getProperties());
};
Checks that all packages use the same version for shared dependencies:
vendor/bin/monorepo-builder validate
Updates mutual dependencies between packages to a given version:
vendor/bin/monorepo-builder bump-interdependency "^4.0"
Propagates versions from root composer.json to all packages (the reverse of merge):
vendor/bin/monorepo-builder propagate
Updates the branch-alias in every package composer.json to match the current version:
vendor/bin/monorepo-builder package-alias
To customize the alias format:
use Symplify\MonorepoBuilder\Config\MBConfig;
return static function (MBConfig $mbConfig): void {
// default: "<major>.<minor>-dev"
$mbConfig->packageAliasFormat('<major>.<minor>.x-dev');
};
Sets mutual package paths to local packages for pre-split testing:
vendor/bin/monorepo-builder localize-composer-paths
Automates the release process: bumping dependencies, tagging, pushing, and updating changelogs.
vendor/bin/monorepo-builder release v7.0
Preview what will happen without making changes:
vendor/bin/monorepo-builder release v7.0 --dry-run
Release by semver level (patch, minor, or major):
# current v0.7.1 -> v0.7.2
vendor/bin/monorepo-builder release patch
Configuring release workers:
TagVersionReleaseWorker and PushTagReleaseWorker are enabled by default. Add more workers or customize the order:
use Symplify\MonorepoBuilder\Config\MBConfig;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\AddTagToChangelogReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\PushNextDevReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\PushTagReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\SetCurrentMutualDependenciesReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\SetNextMutualDependenciesReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\TagVersionReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\UpdateBranchAliasReleaseWorker;
use Symplify\MonorepoBuilder\Release\ReleaseWorker\UpdateReplaceReleaseWorker;
return static function (MBConfig $mbConfig): void {
$mbConfig->workers([
UpdateReplaceReleaseWorker::class,
SetCurrentMutualDependenciesReleaseWorker::class,
AddTagToChangelogReleaseWorker::class,
TagVersionReleaseWorker::class,
PushTagReleaseWorker::class,
SetNextMutualDependenciesReleaseWorker::class,
UpdateBranchAliasReleaseWorker::class,
PushNextDevReleaseWorker::class,
]);
};
To disable the default workers:
return static function (MBConfig $mbConfig): void {
$mbConfig->disableDefaultWorkers();
};
You can also add custom workers by implementing ReleaseWorkerInterface.
Branch-aware tag validation (LTS):
If you maintain multiple version lines, the release command may reject older versions because it compares against the most recent tag globally. Enable branch-aware validation to compare only within the same major version:
use Symplify\MonorepoBuilder\Config\MBConfig;
use Symplify\MonorepoBuilder\Git\BranchAwareTagResolver;
use Symplify\MonorepoBuilder\Contract\Git\TagResolverInterface;
return static function (MBConfig $mbConfig): void {
$services = $mbConfig->services();
$services->set(BranchAwareTagResolver::class);
$services->alias(TagResolverInterface::class, BranchAwareTagResolver::class);
};
To split packages into separate repositories, use symplify/github-action-monorepo-split with GitHub Actions.
How can I help you explore Laravel packages today?