diff --git a/composer.json b/composer.json index 5f30345b55f..6cc71db0d47 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,7 @@ "nette/application": "^2.4", "phpstan/phpstan": "^0.10.3|^0.9", "phpunit/phpunit": "^7.3", + "slam/php-cs-fixer-extensions": "^1.17", "tracy/tracy": "^2.4" }, "replace": { diff --git a/packages/MonorepoBuilder/README.md b/packages/MonorepoBuilder/README.md index d2acbdbac06..7f7c13340ca 100644 --- a/packages/MonorepoBuilder/README.md +++ b/packages/MonorepoBuilder/README.md @@ -18,16 +18,32 @@ composer require symplify/monorepo-builder --dev ### 1. Merge local `composer.json` to the Root One -Merges following sections to the root `composer.json`, so you can only edit `composer.json` of particular packages and let script to synchronize it. - -- 'require' -- 'require-dev' -- 'autoload' -- 'autoload-dev' -- 'repositories' -- 'scripts' -- 'extra' -- ... +Merges configured sections to the root `composer.json`, so you can only edit `composer.json` of particular packages and let script to synchronize it. + +```yaml +# monorepo-builder.yml +parameters: + merge_sections: + # default values + - 'require' + - 'require-dev' + - 'autoload' + - 'autoload-dev' + - 'repositories' +``` + +You can configure it and add `minimum-stablity` for example or remove any of those default values: + +```yaml +# monorepo-builder.yml +parameters: + merge_sections: + - 'require' + - 'require-dev' + - 'minimum-stability' +``` + +To merge just run: ```bash vendor/bin/monorepo-builder merge diff --git a/packages/MonorepoBuilder/src/Console/Command/MergeCommand.php b/packages/MonorepoBuilder/src/Console/Command/MergeCommand.php index d66805d5693..5eec9d19cc6 100644 --- a/packages/MonorepoBuilder/src/Console/Command/MergeCommand.php +++ b/packages/MonorepoBuilder/src/Console/Command/MergeCommand.php @@ -6,9 +6,11 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symplify\MonorepoBuilder\Console\Reporter\ConflictingPackageVersionsReporter; use Symplify\MonorepoBuilder\DependenciesMerger; +use Symplify\MonorepoBuilder\FileSystem\ComposerJsonProvider; use Symplify\MonorepoBuilder\Package\PackageComposerJsonMerger; -use Symplify\MonorepoBuilder\PackageComposerFinder; +use Symplify\MonorepoBuilder\VersionValidator; use Symplify\PackageBuilder\Console\Command\CommandNaming; final class MergeCommand extends Command @@ -29,14 +31,24 @@ final class MergeCommand extends Command private $mergeSections = []; /** - * @var PackageComposerFinder + * @var DependenciesMerger */ - private $packageComposerFinder; + private $dependenciesMerger; /** - * @var DependenciesMerger + * @var VersionValidator */ - private $dependenciesMerger; + private $versionValidator; + + /** + * @var ComposerJsonProvider + */ + private $composerJsonProvider; + + /** + * @var ConflictingPackageVersionsReporter + */ + private $conflictingPackageVersionsReporter; /** * @param string[] $mergeSections @@ -45,16 +57,20 @@ public function __construct( array $mergeSections, SymfonyStyle $symfonyStyle, PackageComposerJsonMerger $packageComposerJsonMerger, - PackageComposerFinder $packageComposerFinder, - DependenciesMerger $dependenciesMerger + DependenciesMerger $dependenciesMerger, + VersionValidator $versionValidator, + ComposerJsonProvider $composerJsonProvider, + ConflictingPackageVersionsReporter $conflictingPackageVersionsReporter ) { + parent::__construct(); $this->symfonyStyle = $symfonyStyle; $this->packageComposerJsonMerger = $packageComposerJsonMerger; - $this->packageComposerFinder = $packageComposerFinder; $this->dependenciesMerger = $dependenciesMerger; $this->mergeSections = $mergeSections; + $this->versionValidator = $versionValidator; + $this->composerJsonProvider = $composerJsonProvider; - parent::__construct(); + $this->conflictingPackageVersionsReporter = $conflictingPackageVersionsReporter; } protected function configure(): void @@ -65,7 +81,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $composerPackageFiles = $this->packageComposerFinder->getPackageComposerFiles(); + $composerPackageFiles = $this->composerJsonProvider->getPackagesComposerJsonFileInfos(); if (! count($composerPackageFiles)) { $this->symfonyStyle->error('No "composer.json" were found in packages.'); return 1; @@ -73,23 +89,32 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($this->mergeSections === []) { $this->symfonyStyle->error( - 'The "merge_sections:" parameter is empty, add "require", "require-dev", "autoload", "autoload-dev" and or "repositories" to your config' + 'The "parameters > merge_sections:" is empty, add "require", "require-dev", "autoload", "autoload-dev" and or "repositories" to your config' ); return 1; } - $merged = $this->packageComposerJsonMerger->mergeFileInfos($composerPackageFiles, $this->mergeSections); + $allComposerJsonFiles = $composerPackageFiles + [$this->composerJsonProvider->getRootComposerJsonFileInfo()]; + + $conflictingPackageVersions = $this->versionValidator->findConflictingPackageVersionsInFileInfos( + $allComposerJsonFiles + ); + + if (count($conflictingPackageVersions) > 0) { + $this->conflictingPackageVersionsReporter->report($conflictingPackageVersions); + // fail + return 1; + } + + $merged = $this->packageComposerJsonMerger->mergeFileInfos($composerPackageFiles, $this->mergeSections); if ($merged === []) { $this->symfonyStyle->note('Nothing to merge.'); // success return 0; } - $this->dependenciesMerger->mergeJsonToRootFilePathAndSave( - $merged, - getcwd() . DIRECTORY_SEPARATOR . 'composer.json' - ); + $this->dependenciesMerger->mergeJsonToRootFilePathAndSave($merged, getcwd() . '/composer.json'); $this->symfonyStyle->success('Main "composer.json" was updated.'); diff --git a/packages/MonorepoBuilder/src/Console/Command/ValidateCommand.php b/packages/MonorepoBuilder/src/Console/Command/ValidateCommand.php index 747a3baff67..06605b6aa78 100644 --- a/packages/MonorepoBuilder/src/Console/Command/ValidateCommand.php +++ b/packages/MonorepoBuilder/src/Console/Command/ValidateCommand.php @@ -6,6 +6,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symplify\MonorepoBuilder\Console\Reporter\ConflictingPackageVersionsReporter; use Symplify\MonorepoBuilder\FileSystem\ComposerJsonProvider; use Symplify\MonorepoBuilder\VersionValidator; use Symplify\PackageBuilder\Console\Command\CommandNaming; @@ -27,16 +28,22 @@ final class ValidateCommand extends Command */ private $composerJsonProvider; + /** + * @var ConflictingPackageVersionsReporter + */ + private $conflictingPackageVersionsReporter; + public function __construct( SymfonyStyle $symfonyStyle, ComposerJsonProvider $composerJsonProvider, - VersionValidator $versionValidator + VersionValidator $versionValidator, + ConflictingPackageVersionsReporter $conflictingPackageVersionsReporter ) { + parent::__construct(); $this->symfonyStyle = $symfonyStyle; $this->versionValidator = $versionValidator; $this->composerJsonProvider = $composerJsonProvider; - - parent::__construct(); + $this->conflictingPackageVersionsReporter = $conflictingPackageVersionsReporter; } protected function configure(): void @@ -55,25 +62,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int $composerFileInfos[] = $this->composerJsonProvider->getRootComposerJsonFileInfo(); - $conflictingPackage = $this->versionValidator->findConflictingPackageInFileInfos($composerFileInfos); - if ($conflictingPackage === []) { + $conflictingPackageVersions = $this->versionValidator->findConflictingPackageVersionsInFileInfos( + $composerFileInfos + ); + if ($conflictingPackageVersions === []) { $this->symfonyStyle->success('All packages "composer.json" files use same package versions.'); // success return 0; } - foreach ($conflictingPackage as $packageName => $filesToVersions) { - $tableData = []; - foreach ($filesToVersions as $file => $version) { - $tableData[] = [$file, $version]; - } - - $this->symfonyStyle->title(sprintf('Package "%s" has various version', $packageName)); - $this->symfonyStyle->table(['File', 'Version'], $tableData); - } - - $this->symfonyStyle->error('Found conflicting package versions, fix them first.'); + $this->conflictingPackageVersionsReporter->report($conflictingPackageVersions); // fail return 1; diff --git a/packages/MonorepoBuilder/src/Console/Reporter/ConflictingPackageVersionsReporter.php b/packages/MonorepoBuilder/src/Console/Reporter/ConflictingPackageVersionsReporter.php new file mode 100644 index 00000000000..24c262b5837 --- /dev/null +++ b/packages/MonorepoBuilder/src/Console/Reporter/ConflictingPackageVersionsReporter.php @@ -0,0 +1,36 @@ +symfonyStyle = $symfonyStyle; + } + + /** + * @param mixed[] $conflictingPackages + */ + public function report(array $conflictingPackages): void + { + foreach ($conflictingPackages as $packageName => $filesToVersions) { + $tableData = []; + foreach ($filesToVersions as $file => $version) { + $tableData[] = [$file, $version]; + } + + $this->symfonyStyle->title(sprintf('Package "%s" has various version', $packageName)); + $this->symfonyStyle->table(['File', 'Version'], $tableData); + } + + $this->symfonyStyle->error('Found conflicting package versions, fix them first.'); + } +} diff --git a/packages/MonorepoBuilder/src/Package/PackageComposerJsonMerger.php b/packages/MonorepoBuilder/src/Package/PackageComposerJsonMerger.php index df393fc5547..b98236eb948 100644 --- a/packages/MonorepoBuilder/src/Package/PackageComposerJsonMerger.php +++ b/packages/MonorepoBuilder/src/Package/PackageComposerJsonMerger.php @@ -55,13 +55,33 @@ public function mergeFileInfos(array $composerPackageFileInfos, array $sections) continue; } - $merged[$section] = $this->parametersMerger->merge( - $merged[$section] ?? [], - $packageComposerJson[$section] - ); + $merged = $this->mergeSection($packageComposerJson, $section, $merged); } } return $merged; } + + /** + * @param mixed[] $packageComposerJson + * @param mixed[] $merged + * @return mixed[] + */ + private function mergeSection(array $packageComposerJson, string $section, array $merged): array + { + // array sections + if (is_array($packageComposerJson[$section])) { + $merged[$section] = $this->parametersMerger->merge( + $merged[$section] ?? [], + $packageComposerJson[$section] + ); + + return $merged; + } + + // key: value sections, like "minimum-stability: dev" + $merged[$section] = $packageComposerJson[$section]; + + return $merged; + } } diff --git a/packages/MonorepoBuilder/src/VersionValidator.php b/packages/MonorepoBuilder/src/VersionValidator.php index fe5c0cd93ff..7d2c86e3bfb 100644 --- a/packages/MonorepoBuilder/src/VersionValidator.php +++ b/packages/MonorepoBuilder/src/VersionValidator.php @@ -27,7 +27,7 @@ public function __construct(JsonFileManager $jsonFileManager) * @param SplFileInfo[] $fileInfos * @return string[][] */ - public function findConflictingPackageInFileInfos(array $fileInfos): array + public function findConflictingPackageVersionsInFileInfos(array $fileInfos): array { $packageVersionsPerFile = []; diff --git a/packages/MonorepoBuilder/tests/VersionValidator/VersionValidatorTest.php b/packages/MonorepoBuilder/tests/VersionValidator/VersionValidatorTest.php index cf42ea839aa..f8894d1a31c 100644 --- a/packages/MonorepoBuilder/tests/VersionValidator/VersionValidatorTest.php +++ b/packages/MonorepoBuilder/tests/VersionValidator/VersionValidatorTest.php @@ -23,7 +23,9 @@ public function test(): void { $fileInfos = iterator_to_array(Finder::create()->name('*.json')->in(__DIR__ . '/Source') ->getIterator()); - $conflictingPackageVersionsPerFile = $this->versionValidator->findConflictingPackageInFileInfos($fileInfos); + $conflictingPackageVersionsPerFile = $this->versionValidator->findConflictingPackageVersionsInFileInfos( + $fileInfos + ); $this->assertArrayHasKey('some/package', $conflictingPackageVersionsPerFile); diff --git a/phpstan.neon b/phpstan.neon index 2378ef10d8e..883329560ff 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -101,7 +101,6 @@ parameters: - '#Method Symplify\\EasyCodingStandard\\Finder\\SourceFinder::find\(\) should return array but returns array#' - '#Property Symplify\\EasyCodingStandard\\Yaml\\CheckerServiceParametersShifter::\$serviceKeywords \(array\) does not accept ReflectionProperty#' - '#Method Symplify\\EasyCodingStandard\\Tests\\Indentation\\IndentationTest::getIndentationTypeFixerFromContainer\(\) should return PhpCsFixer\\Fixer\\Whitespace\\IndentationTypeFixer but returns PhpCsFixer\\Fixer\\FixerInterface\|null#' - - '#Method Symplify\\MonorepoBuilder\\Package\\PackageComposerJsonMerger::mergeFileInfos\(\) should return array but returns array#' - '#Parameter \#1 \$path of function dirname expects string, string\|false given#' - '#Method Symplify\\PackageBuilder\\Console\\Command\\CommandNaming::getShortClassName\(\) should return string but returns string\|null#' - '#Method Symplify\\PackageBuilder\\Tests\\Console\\ThrowableRendererTest::getTestErrorOutput\(\) should return string but returns string\|false#'