diff --git a/docs/book/index.md b/docs/book/index.md index 5acc1e7..d54a592 100644 --- a/docs/book/index.md +++ b/docs/book/index.md @@ -159,23 +159,25 @@ will need to have a line that instantiates either a Configuration providers are added at the **top** of the `ConfigManager`/`ConfigAggregator` provider array. -## Whitelisting packages to autoinstall +## Marking packages to auto-install At the root package level, you can indicate that certain packages that supply config providers and/or modules should automatically inject configuration, -instead of prompting for installation, via the `component-whitelist` setting. +instead of prompting for installation, via the `component-auto-installs` setting. This value should be an array of package names. ```json -"extra": { +{ + "extra": { "laminas": { - "component-whitelist": [ - "mezzio/mezzio", - "mezzio/mezzio-helper", - "mezzio/mezzio-fastrouterouter", - "mezzio/mezzio-platesrenderer" - ] + "component-auto-installs": [ + "mezzio/mezzio", + "mezzio/mezzio-helper", + "mezzio/mezzio-fastrouterouter", + "mezzio/mezzio-platesrenderer" + ] } + } } ``` diff --git a/src/ComponentInstaller.php b/src/ComponentInstaller.php index e2313ff..07e1e6c 100644 --- a/src/ComponentInstaller.php +++ b/src/ComponentInstaller.php @@ -478,7 +478,7 @@ private function marshalInstallableModules(array $extra, Collection $options) /** * Prompt for the user to select a configuration location to update. * - * @param array $whitelist + * @param array $autoInstallations * @return InjectorInterface */ private function promptForConfigOption( @@ -486,15 +486,15 @@ private function promptForConfigOption( Collection $options, int $packageType, string $packageName, - array $whitelist, + array $autoInstallations, bool $requireDev = false ) { if ($cachedInjector = $this->getCachedInjector($packageType)) { return $cachedInjector; } - // If package is whitelisted, don't ask... - if (in_array($packageName, $whitelist, true)) { + // If package is allowed to be auto-installed, don't ask... + if (in_array($packageName, $autoInstallations, true)) { if ($requireDev) { return isset($options[2]) ? $options[2]->getInjector() : $options[1]->getInjector(); } @@ -990,17 +990,19 @@ function (Collection $injectors, string $module) use ($options, $packageTypes, $ // @codingStandardsIgnoreEnd // Get extra from root package /** @var array $rootPackageExtra */ - $rootPackageExtra = $this->composer->getPackage()->getExtra(); - $rootExtra = $this->getExtraMetadata($rootPackageExtra); - $whitelist = $rootExtra['component-whitelist'] ?? []; - assert(is_array($whitelist)); + $rootPackageExtra = $this->composer->getPackage()->getExtra(); + $rootExtra = $this->getExtraMetadata($rootPackageExtra); + $autoInstallations = $rootExtra['component-auto-installs'] + ?? $rootExtra['component-whitelist'] + ?? []; + assert(is_array($autoInstallations)); $packageType = $packageTypes[$module]; $injectors[$module] = $this->promptForConfigOption( $module, $options, $packageType, $name, - $whitelist, + $autoInstallations, $requireDev ); diff --git a/test/ComponentInstallerTest.php b/test/ComponentInstallerTest.php index 05ec57c..3d27261 100644 --- a/test/ComponentInstallerTest.php +++ b/test/ComponentInstallerTest.php @@ -63,7 +63,7 @@ final class ComponentInstallerTest extends TestCase /** @var InstallationManager&MockObject */ private $installationManager; - /** @var array{laminas?:array{component-whitelist?:list}} */ + /** @var array{laminas?:array{component-auto-installs?:list,component-whitelist?:list}} */ private $rootPackageExtra = []; protected function setUp(): void @@ -949,45 +949,6 @@ public function testOnPostPackageInstallDoesNotPromptIfPackageIsAlreadyInConfigu self::assertStringContainsString("'Some\Component'", $config); } - public function testOnPostPackageInstallDoesNotPromptForWhitelistedPackages(): void - { - $this->createApplicationConfig(); - - $package = $this->createMock(PackageInterface::class); - $package->method('getName')->willReturn('some/component'); - $package->method('getExtra')->willReturn([ - 'laminas' => [ - 'component' => 'Some\\Component', - ], - ]); - - $operation = $this->createMock(InstallOperation::class); - $operation->method('getPackage')->willReturn($package); - - $event = $this->createMock(PackageEvent::class); - $event->method('isDevMode')->willReturn(true); - $event->method('getOperation')->willReturn($operation); - $this->prepareEventForPackageProviderDetection($event, 'some/component'); - - $this->rootPackage->method('getName')->willReturn('some/component'); - $this->rootPackageExtra = [ - 'laminas' => [ - 'component-whitelist' => ['some/component'], - ], - ]; - - $this->createOutputAssertions([ - 'Installing Some\Component from package some/component', - ]); - $this->io - ->expects(self::never()) - ->method('ask'); - - $this->installer->onPostPackageInstall($event); - $config = file_get_contents(vfsStream::url('project/config/application.config.php')); - self::assertStringContainsString("'Some\Component'", $config); - } - public function testOnPostPackageInstallPromptsForConfigOptions(): void { $this->createApplicationConfig(); @@ -1603,7 +1564,178 @@ public function testUninstallMessageWithDifferentInjectors($configContents, arra $this->installer->onPostPackageUninstall($event); } - public function testInstallWhitelistedDevModuleWithDifferentInjectors(): void + public function testInstallAutoInstallableDevModuleWithDifferentInjectors(): void + { + $moduleConfigContent = <<<'CONFIG' + [ + 'Laminas\Router', + 'Laminas\Validator', + 'Application' + ] +]; +CONFIG; + + $this->createConfigFile('modules.config.php', $moduleConfigContent); + + $configContents = <<<'CONFIG' + [ + ] +]; +CONFIG; + foreach (['development.config.php.dist', 'development.config.php'] as $configName) { + $this->createConfigFile($configName, $configContents); + } + + $this->rootPackage->method('getDevRequires')->willReturn(['some/component' => '*']); + $this->rootPackageExtra = [ + 'laminas' => [ + "component-auto-installs" => [ + "some/component", + ], + ], + ]; + + $package = $this->createMock(PackageInterface::class); + $package->method('getName')->willReturn('some/component'); + $package->method('getExtra')->willReturn([ + 'laminas' => [ + 'component' => [ + 'Some\\Component', + ], + ], + ]); + + $operation = $this->createMock(InstallOperation::class); + $operation->method('getPackage')->willReturn($package); + + $event = $this->createMock(PackageEvent::class); + $event->method('isDevMode')->willReturn(true); + $event->method('getOperation')->willReturn($operation); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); + + $this->installer->onPostPackageInstall($event); + /** + * @psalm-suppress UnresolvableInclude + * @psalm-var array{modules:list} $config + */ + $config = require vfsStream::url('project/config/modules.config.php'); + $modules = $config['modules']; + self::assertEquals([ + 'Laminas\Router', + 'Laminas\Validator', + 'Application', + ], $modules); + /** + * @psalm-suppress UnresolvableInclude + * @psalm-var array{modules:list} $config + */ + $config = require vfsStream::url('project/config/development.config.php'); + $modules = $config['modules']; + self::assertEquals(['Some\Component'], $modules); + } + + public function testOnPostPackageInstallDoesNotPromptForAutoInstallablePackages(): void + { + $this->createApplicationConfig(); + + $package = $this->createMock(PackageInterface::class); + $package->method('getName')->willReturn('some/component'); + $package->method('getExtra')->willReturn([ + 'laminas' => [ + 'component' => 'Some\\Component', + ], + ]); + + $operation = $this->createMock(InstallOperation::class); + $operation->method('getPackage')->willReturn($package); + + $event = $this->createMock(PackageEvent::class); + $event->method('isDevMode')->willReturn(true); + $event->method('getOperation')->willReturn($operation); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); + + $this->rootPackage->method('getName')->willReturn('some/component'); + $this->rootPackageExtra = [ + 'laminas' => [ + 'component-auto-installs' => ['some/component'], + ], + ]; + + $this->createOutputAssertions([ + 'Installing Some\Component from package some/component', + ]); + $this->io + ->expects(self::never()) + ->method('ask'); + + $this->installer->onPostPackageInstall($event); + $config = file_get_contents(vfsStream::url('project/config/application.config.php')); + self::assertStringContainsString("'Some\Component'", $config); + } + + public function testInstallAutoInstallableDevModuleWithUniqueInjector(): void + { + $moduleConfigContent = <<<'CONFIG' + [ + 'Laminas\Router', + 'Laminas\Validator', + 'Application', + ] +]; +CONFIG; + + $this->createConfigFile('modules.config.php', $moduleConfigContent); + + $this->rootPackageExtra = [ + 'laminas' => [ + "component-auto-installs" => [ + "some/module", + ], + ], + ]; + + $package = $this->createMock(PackageInterface::class); + $package->method('getName')->willReturn('some/module'); + $package->method('getExtra')->willReturn([ + 'laminas' => [ + 'module' => 'Some\\Module', + ], + ]); + + $operation = $this->createMock(InstallOperation::class); + $operation->method('getPackage')->willReturn($package); + + $event = $this->createMock(PackageEvent::class); + $event->method('isDevMode')->willReturn(true); + $event->method('getOperation')->willReturn($operation); + $this->prepareEventForPackageProviderDetection($event, 'some/module'); + + $this->rootPackage + ->method('getName') + ->willReturn('some/module'); + + $this->installer->onPostPackageInstall($event); + /** + * @psalm-suppress UnresolvableInclude + * @psalm-var array{modules:list} $config + */ + $config = require vfsStream::url('project/config/modules.config.php'); + $modules = $config['modules']; + self::assertEquals([ + 'Laminas\Router', + 'Laminas\Validator', + 'Application', + 'Some\Module', + ], $modules); + } + + public function testInstallDeprecatedWhitelistedDevModuleWithDifferentInjectors(): void { $moduleConfigContent = <<<'CONFIG' createApplicationConfig(); + + $package = $this->createMock(PackageInterface::class); + $package->method('getName')->willReturn('some/component'); + $package->method('getExtra')->willReturn([ + 'laminas' => [ + 'component' => 'Some\\Component', + ], + ]); + + $operation = $this->createMock(InstallOperation::class); + $operation->method('getPackage')->willReturn($package); + + $event = $this->createMock(PackageEvent::class); + $event->method('isDevMode')->willReturn(true); + $event->method('getOperation')->willReturn($operation); + $this->prepareEventForPackageProviderDetection($event, 'some/component'); + + $this->rootPackage->method('getName')->willReturn('some/component'); + $this->rootPackageExtra = [ + 'laminas' => [ + 'component-whitelist' => ['some/component'], + ], + ]; + + $this->createOutputAssertions([ + 'Installing Some\Component from package some/component', + ]); + $this->io + ->expects(self::never()) + ->method('ask'); + + $this->installer->onPostPackageInstall($event); + $config = file_get_contents(vfsStream::url('project/config/application.config.php')); + self::assertStringContainsString("'Some\Component'", $config); + } + + public function testInstallDeprecatedWhitelistedDevModuleWithUniqueInjector(): void { $moduleConfigContent = <<<'CONFIG'