Repository navigation
[Configuration] Auto-detect installed packages in withComposerBased() called with no argument #8748
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MadBox-99
wants to merge
3
commits into
rectorphp:main
Choose a base branch
from
MadBox-99:composer-based-auto-detect
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
31e1ec8
[Configuration] Auto-detect installed frameworks in withComposerBased…
MadBox-99 995bbfe
Trigger composer-based sets by any of several packages, use package w…
MadBox-99 aa524fe
Generate composer-based trigger packages from the packages the sets u…
MadBox-99 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| use Rector\Config\RectorConfig; | ||
| use Rector\Configuration\Option; | ||
| use Rector\Configuration\Parameter\SimpleParameterProvider; | ||
| use Rector\Doctrine\Set\DoctrineSetList; | ||
| use Rector\PHPUnit\Set\PHPUnitSetList; | ||
| use Rector\Symfony\Set\SymfonySetList; | ||
| use Rector\Symfony\Set\TwigSetList; | ||
| use Rector\VersionBonding\Contract\ComposerPackageConstraintInterface; | ||
| use Rector\VersionBonding\ValueObject\ComposerBoundRuleConfiguration; | ||
| use Symfony\Component\Console\Input\ArrayInput; | ||
| use Symfony\Component\Console\Output\ConsoleOutput; | ||
| use Symfony\Component\Console\Style\SymfonyStyle; | ||
|
|
||
| require __DIR__ . '/../vendor/autoload.php'; | ||
|
|
||
| // regenerates src/Configuration/ComposerBasedTriggerPackages.php from the packages the composer-based sets are bound to | ||
| $composerBasedSetFiles = [ | ||
| 'twig' => TwigSetList::COMPOSER_BASED, | ||
| 'doctrine' => DoctrineSetList::COMPOSER_BASED, | ||
| 'phpunit' => PHPUnitSetList::COMPOSER_BASED, | ||
| 'symfony' => SymfonySetList::COMPOSER_BASED, | ||
| ]; | ||
|
|
||
| // rector-laravel and drupal-rector are not required here, so their sets cannot be read | ||
| $extensionTriggerPackages = [ | ||
| 'laravel' => ['laravel/framework', 'illuminate/support'], | ||
| 'drupal' => ['drupal/core'], | ||
| ]; | ||
|
|
||
| $triggerPackages = []; | ||
|
|
||
| foreach ($composerBasedSetFiles as $argumentName => $composerBasedSetFile) { | ||
| // the parameter provider is static, start each set from scratch | ||
| SimpleParameterProvider::setParameter(Option::COMPOSER_BOUND_RULE_CONFIGURATIONS, []); | ||
|
|
||
| $rectorConfig = new RectorConfig(); | ||
|
|
||
| /** @var callable $configCallable */ | ||
| $configCallable = require $composerBasedSetFile; | ||
| $configCallable($rectorConfig); | ||
|
|
||
| $rectorClasses = array_merge( | ||
| $rectorConfig->getMainRectorClasses(), | ||
| array_keys($rectorConfig->getRuleConfigurations()) | ||
| ); | ||
|
|
||
| $packageNames = []; | ||
|
|
||
| foreach (array_unique($rectorClasses) as $rectorClass) { | ||
| // the package constraint does not depend on constructor dependencies, skip them | ||
| $rector = new ReflectionClass($rectorClass)->newInstanceWithoutConstructor(); | ||
| if (! $rector instanceof ComposerPackageConstraintInterface) { | ||
| continue; | ||
| } | ||
|
|
||
| $composerPackageConstraint = $rector->provideComposerPackageConstraint(); | ||
| $composerPackageConstraints = is_array($composerPackageConstraint) | ||
| ? $composerPackageConstraint | ||
| : [$composerPackageConstraint]; | ||
|
|
||
| foreach ($composerPackageConstraints as $composerPackageConstraint) { | ||
| $packageNames[] = $composerPackageConstraint->getPackageName(); | ||
| } | ||
| } | ||
|
|
||
| // rule configurations registered via ruleWithConfigurationComposerVersionBound(), the inactive ones included | ||
| $composerBoundRuleConfigurations = SimpleParameterProvider::provideArrayParameter( | ||
| Option::COMPOSER_BOUND_RULE_CONFIGURATIONS | ||
| ); | ||
|
|
||
| foreach ($composerBoundRuleConfigurations as $composerBoundRuleConfiguration) { | ||
| if ($composerBoundRuleConfiguration instanceof ComposerBoundRuleConfiguration) { | ||
| $packageNames[] = $composerBoundRuleConfiguration->getPackageName(); | ||
| } | ||
| } | ||
|
|
||
| $packageNames = array_values(array_unique($packageNames)); | ||
| sort($packageNames); | ||
|
|
||
| $triggerPackages[$argumentName] = $packageNames; | ||
| } | ||
|
|
||
| $triggerPackages = array_merge($triggerPackages, $extensionTriggerPackages); | ||
|
|
||
| $printedTriggerPackages = ''; | ||
| foreach ($triggerPackages as $argumentName => $packageNames) { | ||
| $printedTriggerPackages .= sprintf(" '%s' => [\n", $argumentName); | ||
|
|
||
| foreach ($packageNames as $packageName) { | ||
| $printedTriggerPackages .= sprintf(" '%s',\n", $packageName); | ||
| } | ||
|
|
||
| $printedTriggerPackages .= " ],\n"; | ||
| } | ||
|
|
||
| $fileContents = <<<PHP | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Rector\Configuration; | ||
|
|
||
| /** | ||
| * Generated by "php scripts/generate-composer-based-trigger-packages.php", do not edit manually | ||
| */ | ||
| final class ComposerBasedTriggerPackages | ||
| { | ||
| /** | ||
| * Any of these installed packages loads the composer-based set of withComposerBased() argument | ||
| * | ||
| * @var array<string, string[]> | ||
| */ | ||
| public const array PACKAGES_BY_ARGUMENT = [ | ||
| {$printedTriggerPackages} ]; | ||
| } | ||
|
|
||
| PHP; | ||
|
|
||
| $targetFilePath = __DIR__ . '/../src/Configuration/ComposerBasedTriggerPackages.php'; | ||
| file_put_contents($targetFilePath, $fileContents); | ||
|
|
||
| $symfonyStyle = new SymfonyStyle(new ArrayInput([]), new ConsoleOutput()); | ||
|
|
||
| foreach ($triggerPackages as $argumentName => $packageNames) { | ||
| $symfonyStyle->writeln(sprintf('<fg=green>%s</>: %d packages', $argumentName, count($packageNames))); | ||
| } | ||
|
|
||
| $symfonyStyle->success(sprintf('File "%s" was generated', realpath($targetFilePath))); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Rector\Configuration; | ||
|
|
||
| /** | ||
| * Generated by "php scripts/generate-composer-based-trigger-packages.php", do not edit manually | ||
| */ | ||
| final class ComposerBasedTriggerPackages | ||
| { | ||
| /** | ||
| * Any of these installed packages loads the composer-based set of withComposerBased() argument | ||
| * | ||
| * @var array<string, string[]> | ||
| */ | ||
| public const array PACKAGES_BY_ARGUMENT = [ | ||
| 'twig' => [ | ||
| 'twig/twig', | ||
| ], | ||
| 'doctrine' => [ | ||
| 'doctrine/collections', | ||
| 'doctrine/common', | ||
| 'doctrine/data-fixtures', | ||
| 'doctrine/dbal', | ||
| 'doctrine/doctrine-bundle', | ||
| 'doctrine/mongodb-odm', | ||
| 'doctrine/mongodb-odm-bundle', | ||
| 'doctrine/orm', | ||
| 'gedmo/doctrine-extensions', | ||
| ], | ||
| 'phpunit' => [ | ||
| 'phpunit/phpunit', | ||
| ], | ||
| 'symfony' => [ | ||
| 'jms/serializer', | ||
| 'symfony/bridge-swift-mailer', | ||
| 'symfony/browser-kit', | ||
| 'symfony/cache', | ||
| 'symfony/class-loader', | ||
| 'symfony/config', | ||
| 'symfony/console', | ||
| 'symfony/contracts', | ||
| 'symfony/debug', | ||
| 'symfony/dependency-injection', | ||
| 'symfony/doctrine-bridge', | ||
| 'symfony/dom-crawler', | ||
| 'symfony/error-handler', | ||
| 'symfony/event-dispatcher', | ||
| 'symfony/expression-language', | ||
| 'symfony/filesystem', | ||
| 'symfony/finder', | ||
| 'symfony/form', | ||
| 'symfony/framework-bundle', | ||
| 'symfony/http-client', | ||
| 'symfony/http-foundation', | ||
| 'symfony/http-kernel', | ||
| 'symfony/inflector', | ||
| 'symfony/intl', | ||
| 'symfony/json-streamer', | ||
| 'symfony/mail-pace-mailer', | ||
| 'symfony/mailer', | ||
| 'symfony/messenger', | ||
| 'symfony/mime', | ||
| 'symfony/monolog-bridge', | ||
| 'symfony/notifier', | ||
| 'symfony/options-resolver', | ||
| 'symfony/process', | ||
| 'symfony/property-access', | ||
| 'symfony/property-info', | ||
| 'symfony/routing', | ||
| 'symfony/scheduler', | ||
| 'symfony/security', | ||
| 'symfony/security-bundle', | ||
| 'symfony/security-core', | ||
| 'symfony/security-http', | ||
| 'symfony/security-mailer', | ||
| 'symfony/serializer', | ||
| 'symfony/symfony', | ||
| 'symfony/templating', | ||
| 'symfony/translation', | ||
| 'symfony/translation-contracts', | ||
| 'symfony/twig-bridge', | ||
| 'symfony/twig-bundle', | ||
| 'symfony/type-info', | ||
| 'symfony/uid', | ||
| 'symfony/validator', | ||
| 'symfony/var-dumper', | ||
| 'symfony/web-link', | ||
| 'symfony/workflow', | ||
| 'symfony/yaml', | ||
| 'twig/twig', | ||
| ], | ||
| 'laravel' => [ | ||
| 'laravel/framework', | ||
| 'illuminate/support', | ||
| ], | ||
| 'drupal' => [ | ||
| 'drupal/core', | ||
| ], | ||
| ]; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Rector\Tests\Configuration; | ||
|
|
||
| use PHPUnit\Framework\TestCase; | ||
| use Rector\Configuration\ComposerBasedTriggerPackages; | ||
| use Rector\Configuration\RectorConfigBuilder; | ||
| use Rector\Doctrine\Set\DoctrineSetList; | ||
| use Rector\PHPUnit\Set\PHPUnitSetList; | ||
| use Rector\Symfony\Set\SymfonySetList; | ||
| use Rector\Symfony\Set\TwigSetList; | ||
| use ReflectionClass; | ||
| use ReflectionMethod; | ||
| use ReflectionParameter; | ||
|
|
||
| final class ComposerBasedAutoDetectTest extends TestCase | ||
| { | ||
| private string $originalWorkingDirectory; | ||
|
|
||
| protected function setUp(): void | ||
| { | ||
| $this->originalWorkingDirectory = (string) getcwd(); | ||
| $this->changeProjectDirectory('installed_packages'); | ||
| } | ||
|
|
||
| protected function tearDown(): void | ||
| { | ||
| chdir($this->originalWorkingDirectory); | ||
| } | ||
|
|
||
| /** | ||
| * doctrine/dbal and symfony/console are enough to load their sets, no doctrine/orm or symfony/framework-bundle needed | ||
| */ | ||
| public function testNoArgumentLoadsSetsOfInstalledPackages(): void | ||
| { | ||
| $rectorConfigBuilder = new RectorConfigBuilder() | ||
| ->withComposerBased(); | ||
|
|
||
| $this->assertSame( | ||
| [PHPUnitSetList::COMPOSER_BASED, DoctrineSetList::COMPOSER_BASED, SymfonySetList::COMPOSER_BASED], | ||
| $this->readSets($rectorConfigBuilder) | ||
| ); | ||
| } | ||
|
|
||
| public function testNoArgumentWithoutTriggerPackagesLoadsNothing(): void | ||
| { | ||
| $this->changeProjectDirectory('no_trigger_packages'); | ||
|
|
||
| $rectorConfigBuilder = new RectorConfigBuilder() | ||
| ->withComposerBased(); | ||
|
|
||
| $this->assertSame([], $this->readSets($rectorConfigBuilder)); | ||
| } | ||
|
|
||
| public function testExplicitArgumentSkipsDetection(): void | ||
| { | ||
| $rectorConfigBuilder = new RectorConfigBuilder() | ||
| ->withComposerBased(twig: true); | ||
|
|
||
| $this->assertSame([TwigSetList::COMPOSER_BASED], $this->readSets($rectorConfigBuilder)); | ||
| } | ||
|
|
||
| public function testExplicitFalseArgumentSkipsDetection(): void | ||
| { | ||
| $rectorConfigBuilder = new RectorConfigBuilder() | ||
| ->withComposerBased(phpunit: false); | ||
|
|
||
| $this->assertSame([], $this->readSets($rectorConfigBuilder)); | ||
| } | ||
|
|
||
| public function testEveryArgumentHasTriggerPackages(): void | ||
| { | ||
| $argumentNames = array_map( | ||
| static fn (ReflectionParameter $reflectionParameter): string => $reflectionParameter->getName(), | ||
| new ReflectionMethod(RectorConfigBuilder::class, 'withComposerBased')->getParameters() | ||
| ); | ||
|
|
||
| // deprecated, no longer applied | ||
| $argumentNames = array_values(array_diff($argumentNames, ['netteUtils'])); | ||
|
|
||
| $this->assertSame($argumentNames, array_keys(ComposerBasedTriggerPackages::PACKAGES_BY_ARGUMENT)); | ||
| } | ||
|
|
||
| private function changeProjectDirectory(string $projectName): void | ||
| { | ||
| chdir(__DIR__ . '/Fixture/ComposerBasedAutoDetect/' . $projectName); | ||
| } | ||
|
|
||
| /** | ||
| * @return string[] | ||
| */ | ||
| private function readSets(RectorConfigBuilder $rectorConfigBuilder): array | ||
| { | ||
| $sets = new ReflectionClass($rectorConfigBuilder) | ||
| ->getProperty('sets') | ||
| ->getValue($rectorConfigBuilder); | ||
|
|
||
| $this->assertIsArray($sets); | ||
|
|
||
| return $sets; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the script should be on github action with weekly run?
see https://github.com/rectorphp/rector-src/blob/main/.github/workflows/weekly_pull_requests.yaml
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can run it per commit, to fail if new package is there.
How fast it is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
run locally, seems fast enough: 0.253 seconds
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍