Skip to content

Commit

Permalink
Merge pull request #52 from boesing/feature/autoinstalls
Browse files Browse the repository at this point in the history
Rename `component-whitelist` to `component-auto-installs`
  • Loading branch information
boesing authored Jul 3, 2022
2 parents 2f0ea6c + 529f33d commit 3c998d2
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 60 deletions.
20 changes: 11 additions & 9 deletions docs/book/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
]
}
}
}
```

Expand Down
20 changes: 11 additions & 9 deletions src/ComponentInstaller.php
Original file line number Diff line number Diff line change
Expand Up @@ -478,23 +478,23 @@ 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(
string $name,
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();
}
Expand Down Expand Up @@ -990,17 +990,19 @@ function (Collection $injectors, string $module) use ($options, $packageTypes, $
// @codingStandardsIgnoreEnd
// Get extra from root package
/** @var array<string,mixed> $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
);

Expand Down
255 changes: 213 additions & 42 deletions test/ComponentInstallerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ final class ComponentInstallerTest extends TestCase
/** @var InstallationManager&MockObject */
private $installationManager;

/** @var array{laminas?:array{component-whitelist?:list<non-empty-string>}} */
/** @var array{laminas?:array{component-auto-installs?:list<non-empty-string>,component-whitelist?:list<non-empty-string>}} */
private $rootPackageExtra = [];

protected function setUp(): void
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -1603,7 +1564,178 @@ public function testUninstallMessageWithDifferentInjectors($configContents, arra
$this->installer->onPostPackageUninstall($event);
}

public function testInstallWhitelistedDevModuleWithDifferentInjectors(): void
public function testInstallAutoInstallableDevModuleWithDifferentInjectors(): void
{
$moduleConfigContent = <<<'CONFIG'
<?php
return [
'modules' => [
'Laminas\Router',
'Laminas\Validator',
'Application'
]
];
CONFIG;

$this->createConfigFile('modules.config.php', $moduleConfigContent);

$configContents = <<<'CONFIG'
<?php
return [
'modules' => [
]
];
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<non-empty-string>} $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<non-empty-string>} $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'
<?php
return [
'modules' => [
'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<non-empty-string>} $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'
<?php
Expand Down Expand Up @@ -1677,7 +1809,46 @@ public function testInstallWhitelistedDevModuleWithDifferentInjectors(): void
self::assertEquals(['Some\Component'], $modules);
}

public function testInstallWhitelistedDevModuleWithUniqueInjector(): void
public function testOnPostPackageInstallDoesNotPromptForDeprecatedWhitelistedPackages(): 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 testInstallDeprecatedWhitelistedDevModuleWithUniqueInjector(): void
{
$moduleConfigContent = <<<'CONFIG'
<?php
Expand Down

0 comments on commit 3c998d2

Please sign in to comment.