1
0
Fork 0

Feature: Add support for autocompleting setting-keys in config-command (#11130)

* Add support for autocompleting setting-keys in config-command

This PR implements the support for autocompletion of setting-keys in the
config-command and its related funtional tests. The implementation takes
existing command-line options like `--unset` or `--global` into account, as
well as existing exposable package-properties. The result is a context-
sensitive auto-completion of individual lists of setting-keys.

Additionally showing the value `suggest.` sub-keys has been allowed in
the config-command, as setting and unsetting them has alwas been possible,
and I did not want to distinguish between showing, setting and unsetting
those keys.

In the setting-key flattening-method I used the `array_is_list()` function,
which was added in PHP 8.1 recently. For backwards PHP-compatiblity the
package `symfony/polyfill-php81` has been added.

Signed-off-by: Stephan Jorek <stephan.jorek@brandung.de>
pull/11150/head
Stephan Jorek 2022-10-25 16:24:02 +02:00 committed by GitHub
parent 8ffd8bef08
commit d54f2f21c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 290 additions and 23 deletions

View File

@ -41,6 +41,7 @@
"composer/pcre": "^2 || ^3", "composer/pcre": "^2 || ^3",
"symfony/polyfill-php73": "^1.24", "symfony/polyfill-php73": "^1.24",
"symfony/polyfill-php80": "^1.24", "symfony/polyfill-php80": "^1.24",
"symfony/polyfill-php81": "^1.24",
"seld/signal-handler": "^2.0" "seld/signal-handler": "^2.0"
}, },
"require-dev": { "require-dev": {

81
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "a676a5855e58daf2c80e70dbb6fce5da", "content-hash": "2dc8cd355eee4bb6c9fb0d2e10ffb7c6",
"packages": [ "packages": [
{ {
"name": "composer/ca-bundle", "name": "composer/ca-bundle",
@ -1724,6 +1724,85 @@
], ],
"time": "2022-05-10T07:21:04+00:00" "time": "2022-05-10T07:21:04+00:00"
}, },
{
"name": "symfony/polyfill-php81",
"version": "v1.26.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1",
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.26-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php81\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-05-24T11:49:31+00:00"
},
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v5.4.11", "version": "v5.4.11",

View File

@ -17,7 +17,8 @@ use Composer\Util\Filesystem;
use Composer\Util\Platform; use Composer\Util\Platform;
use Composer\Util\Silencer; use Composer\Util\Silencer;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument; use Composer\Console\Input\InputArgument;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Composer\Config; use Composer\Config;
@ -34,6 +35,26 @@ use Composer\Package\BasePackage;
*/ */
class ConfigCommand extends BaseCommand class ConfigCommand extends BaseCommand
{ {
/**
* List of additional configurable package-properties
*
* @var string[]
*/
protected const CONFIGURABLE_PACKAGE_PROPERTIES = [
'name',
'type',
'description',
'homepage',
'version',
'minimum-stability',
'prefer-stable',
'keywords',
'license',
'repositories',
'suggest',
'extra',
];
/** /**
* @var Config * @var Config
*/ */
@ -76,7 +97,7 @@ class ConfigCommand extends BaseCommand
new InputOption('merge', 'm', InputOption::VALUE_NONE, 'Merge the setting value with the current value, to be used with extra.* keys in combination with --json'), new InputOption('merge', 'm', InputOption::VALUE_NONE, 'Merge the setting value with the current value, to be used with extra.* keys in combination with --json'),
new InputOption('append', null, InputOption::VALUE_NONE, 'When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority)'), new InputOption('append', null, InputOption::VALUE_NONE, 'When adding a repository, append it (lowest priority) to the existing ones instead of prepending it (highest priority)'),
new InputOption('source', null, InputOption::VALUE_NONE, 'Display where the config value is loaded from'), new InputOption('source', null, InputOption::VALUE_NONE, 'Display where the config value is loaded from'),
new InputArgument('setting-key', null, 'Setting key'), new InputArgument('setting-key', null, 'Setting key', null, $this->suggestSettingKeys()),
new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'), new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'),
]) ])
->setHelp( ->setHelp(
@ -161,11 +182,7 @@ EOT
$io = $this->getIO(); $io = $this->getIO();
$this->config = Factory::createConfig($io); $this->config = Factory::createConfig($io);
// Get the local composer.json, global config.json, or if the user $configFile = $this->getComposerConfigFile($input, $this->config);
// passed in a file to use
$configFile = $input->getOption('global')
? ($this->config->get('home') . '/config.json')
: ($input->getOption('file') ?: Factory::getComposerFile());
// Create global composer.json if this was invoked using `composer global config` // Create global composer.json if this was invoked using `composer global config`
if ( if (
@ -179,9 +196,7 @@ EOT
$this->configFile = new JsonFile($configFile, null, $io); $this->configFile = new JsonFile($configFile, null, $io);
$this->configSource = new JsonConfigSource($this->configFile); $this->configSource = new JsonConfigSource($this->configFile);
$authConfigFile = $input->getOption('global') $authConfigFile = $this->getAuthConfigFile($input, $this->config);
? ($this->config->get('home') . '/auth.json')
: dirname($configFile) . '/auth.json';
$this->authConfigFile = new JsonFile($authConfigFile, null, $io); $this->authConfigFile = new JsonFile($authConfigFile, null, $io);
$this->authConfigSource = new JsonConfigSource($this->authConfigFile, true); $this->authConfigSource = new JsonConfigSource($this->authConfigFile, true);
@ -258,7 +273,7 @@ EOT
// show the value if no value is provided // show the value if no value is provided
if ([] === $input->getArgument('setting-value') && !$input->getOption('unset')) { if ([] === $input->getArgument('setting-value') && !$input->getOption('unset')) {
$properties = ['name', 'type', 'description', 'homepage', 'version', 'minimum-stability', 'prefer-stable', 'keywords', 'license', 'extra']; $properties = self::CONFIGURABLE_PACKAGE_PROPERTIES;
$rawData = $this->configFile->read(); $rawData = $this->configFile->read();
$data = $this->config->all(); $data = $this->config->all();
if (Preg::isMatch('/^repos?(?:itories)?(?:\.(.+))?/', $settingKey, $matches)) { if (Preg::isMatch('/^repos?(?:itories)?(?:\.(.+))?/', $settingKey, $matches)) {
@ -273,7 +288,7 @@ EOT
} }
} elseif (strpos($settingKey, '.')) { } elseif (strpos($settingKey, '.')) {
$bits = explode('.', $settingKey); $bits = explode('.', $settingKey);
if ($bits[0] === 'extra') { if ($bits[0] === 'extra' || $bits[0] === 'suggest') {
$data = $rawData; $data = $rawData;
} else { } else {
$data = $data['config']; $data = $data['config'];
@ -914,4 +929,136 @@ EOT
} }
} }
} }
/**
* Get the local composer.json, global config.json, or the file passed by the user
*/
private function getComposerConfigFile(InputInterface $input, Config $config): string
{
return $input->getOption('global')
? ($config->get('home') . '/config.json')
: ($input->getOption('file') ?: Factory::getComposerFile())
;
}
/**
* Get the local auth.json or global auth.json, or if the user passed in a file to use,
* the corresponding auth.json
*/
private function getAuthConfigFile(InputInterface $input, Config $config): string
{
return $input->getOption('global')
? ($config->get('home') . '/auth.json')
: dirname($this->getComposerConfigFile($input, $config)) . '/auth.json'
;
}
/**
* Suggest setting-keys, while taking given options in acount.
*/
private function suggestSettingKeys(): \Closure
{
return function (CompletionInput $input): array {
if ($input->getOption('list') || $input->getOption('editor') || $input->getOption('auth')) {
return [];
}
// initialize configuration
$config = Factory::createConfig();
// load configuration
$configFile = new JsonFile($this->getComposerConfigFile($input, $config));
if ($configFile->exists()) {
$config->merge($configFile->read(), $configFile->getPath());
}
// load auth-configuration
$authConfigFile = new JsonFile($this->getAuthConfigFile($input, $config));
if ($authConfigFile->exists()) {
$config->merge(['config' => $authConfigFile->read()], $authConfigFile->getPath());
}
// collect all configuration setting-keys
$rawConfig = $config->raw();
$keys = array_merge(
$this->flattenSettingKeys($rawConfig['config']),
$this->flattenSettingKeys($rawConfig['repositories'], 'repositories.')
);
// if unsetting …
if ($input->getOption('unset')) {
// … keep only the currently customized setting-keys …
$sources = [$configFile->getPath(), $authConfigFile->getPath()];
$keys = array_filter(
$keys,
static function (string $key) use ($config, $sources): bool {
return in_array($config->getSourceOfValue($key), $sources, true);
}
);
// … else if showing or setting a value …
} else {
// … add all configurable package-properties, no matter if it exist
$keys = array_merge($keys, self::CONFIGURABLE_PACKAGE_PROPERTIES);
// it would be nice to distinguish between showing and setting
// a value, but that makes the implementation much more complex
// and partially impossible because symfony's implementation
// does not complete arguments followed by other arguments
}
// add all existing configurable package-properties
if ($configFile->exists()) {
$properties = array_filter(
$configFile->read(),
static function (string $key): bool {
return in_array($key, self::CONFIGURABLE_PACKAGE_PROPERTIES, true);
},
ARRAY_FILTER_USE_KEY
);
$keys = array_merge(
$keys,
$this->flattenSettingKeys($properties)
);
}
// filter settings-keys by completion value
$completionValue = $input->getCompletionValue();
if ($completionValue !== '') {
$keys = array_filter(
$keys,
static function (string $key) use ($completionValue): bool {
return str_starts_with($key, $completionValue);
}
);
}
sort($keys);
return array_unique($keys);
};
}
/**
* build a flat list of dot-separated setting-keys from given config
*
* @param array<mixed[]|string> $config
* @return string[]
*/
private function flattenSettingKeys(array $config, string $prefix = ''): array
{
$keys = [];
foreach ($config as $key => $value) {
$keys[] = [$prefix . $key];
// array-lists must not be added to completion
// sub-keys of repository-keys must not be added to completion
if (is_array($value) && !array_is_list($value) && $prefix !== 'repositories.') {
$keys[] = $this->flattenSettingKeys($value, $prefix . $key . '.');
}
}
return array_merge(...$keys);
}
} }

View File

@ -78,6 +78,46 @@ class CompletionFunctionalTest extends TestCase
yield ['update --prefer-install ', $preferInstall]; yield ['update --prefer-install ', $preferInstall];
yield ['update ', $installedPackages]; yield ['update ', $installedPackages];
yield ['config --list ', null];
yield ['config --editor ', null];
yield ['config --auth ', null];
yield ['config ', ['bin-compat', 'extra', 'extra.branch-alias', 'home', 'name', 'repositories', 'repositories.packagist.org', 'suggest', 'suggest.ext-zip', 'type', 'version']];
yield ['config bin', ['bin-dir']]; // global setting
yield ['config nam', ['name']]; // existing package-property
yield ['config ver', ['version']]; // non-existing package-property
yield ['config repo', ['repositories', 'repositories.packagist.org']];
yield ['config repositories.', ['repositories.packagist.org']];
yield ['config sug', ['suggest', 'suggest.ext-zip']];
yield ['config suggest.ext-', ['suggest.ext-zip']];
yield ['config ext', ['extra', 'extra.branch-alias', 'extra.branch-alias.dev-main']];
// as this test does not use a fixture (yet?), the completion
// of setting authentication settings can have varying results
// yield ['config http-basic.', […]];
yield ['config --unset ', ['extra', 'extra.branch-alias', 'extra.branch-alias.dev-main', 'name', 'suggest', 'suggest.ext-zip', 'type']];
yield ['config --unset bin-dir', null]; // global setting
yield ['config --unset nam', ['name']]; // existing package-property
yield ['config --unset version', null]; // non-existing package-property
yield ['config --unset extra.', ['extra.branch-alias', 'extra.branch-alias.dev-main']];
// as this test does not use a fixture (yet?), the completion
// of unsetting authentication settings can have varying results
// yield ['config --unset http-basic.', […]];
yield ['config --global ', ['bin-compat', 'home', 'repositories', 'repositories.packagist.org']];
yield ['config --global repo', ['repositories', 'repositories.packagist.org']];
yield ['config --global repositories.', ['repositories.packagist.org']];
// as this test does not use a fixture (yet?), the completion
// of unsetting global settings can have varying results
// yield ['config --global --unset ', null];
// as this test does not use a fixture (yet?), the completion of
// unsetting global authentication settings can have varying results
// yield ['config --global --unset http-basic.', […]];
} }
/** /**

View File

@ -8,7 +8,7 @@ Checks that package versions in InstalledVersions are correct on initial install
update update
--EXPECT-- --EXPECT--
> Hooks::preUpdate > Hooks::preUpdate
!!PreUpdate:["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string"] !!PreUpdate:["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/polyfill-php81","symfony/process","symfony/service-contracts","symfony/string"]
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
Loading composer repositories with package information Loading composer repositories with package information
%((Info|Warning) from .*\n)?%Updating dependencies %((Info|Warning) from .*\n)?%Updating dependencies
@ -26,12 +26,12 @@ Package operations: 6 installs, 0 updates, 0 removals%(\nAs there is no 'unzip'
- Downloading symfony/filesystem (%v?[2-8]\.\d+\.\d+%) - Downloading symfony/filesystem (%v?[2-8]\.\d+\.\d+%)
- Installing symfony/console (99999.1.2): Symlinking from symfony-console - Installing symfony/console (99999.1.2): Symlinking from symfony-console
- Installing plugin/a (1.1.1): Symlinking from plugin-a - Installing plugin/a (1.1.1): Symlinking from plugin-a
!!PluginAInit["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string","root/pkg"] !!PluginAInit["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/polyfill-php81","symfony/process","symfony/service-contracts","symfony/string","root/pkg"]
!!PluginA:null !!PluginA:null
!!PluginB:null !!PluginB:null
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
- Installing plugin/b (2.2.2): Symlinking from plugin-b - Installing plugin/b (2.2.2): Symlinking from plugin-b
!!PluginBInit["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string","plugin/a","root/pkg"] !!PluginBInit["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/polyfill-php81","symfony/process","symfony/service-contracts","symfony/string","plugin/a","root/pkg"]
!!PluginA:1.1.1.0 !!PluginA:1.1.1.0
!!PluginB:null !!PluginB:null
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
@ -42,7 +42,7 @@ Generating autoload files
2 packages you are using are looking for funding. 2 packages you are using are looking for funding.
Use the `composer fund` command to find out more! Use the `composer fund` command to find out more!
> Hooks::postUpdate > Hooks::postUpdate
!!PostUpdate:["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"] !!PostUpdate:["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/polyfill-php81","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"]
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
--EXPECT-EXIT-CODE-- --EXPECT-EXIT-CODE--

View File

@ -7,14 +7,14 @@ Checks that package versions in InstalledVersions are correct during an upgrade.
--RUN-- --RUN--
update plugin/* symfony/console symfony/filesystem symfony/process update plugin/* symfony/console symfony/filesystem symfony/process
--EXPECT-- --EXPECT--
!!PluginA:1.1.1.0["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"] !!PluginA:1.1.1.0["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/polyfill-php81","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"]
!!PluginB:2.2.2.0 !!PluginB:2.2.2.0
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
!!PluginB:2.2.2.0["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"] !!PluginB:2.2.2.0["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/polyfill-php81","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"]
!!PluginA:1.1.1.0 !!PluginA:1.1.1.0
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
> Hooks::preUpdate > Hooks::preUpdate
!!PreUpdate:["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"] !!PreUpdate:["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/polyfill-php81","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"]
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
Loading composer repositories with package information Loading composer repositories with package information
%((Info|Warning) from .*\n)?%Updating dependencies %((Info|Warning) from .*\n)?%Updating dependencies
@ -30,12 +30,12 @@ Package operations: 0 installs, 5 updates, 0 removals%(\nAs there is no 'unzip'
- Downloading symfony/filesystem (%v?[2-8]\.\d+\.\d+%) - Downloading symfony/filesystem (%v?[2-8]\.\d+\.\d+%)
- Upgrading symfony/console (99999.1.2 => 99999.1.3): Mirroring from symfony-console - Upgrading symfony/console (99999.1.2 => 99999.1.3): Mirroring from symfony-console
- Upgrading plugin/a (1.1.1 => 1.1.2): Mirroring from plugin-a - Upgrading plugin/a (1.1.1 => 1.1.2): Mirroring from plugin-a
!!PluginAInit["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"] !!PluginAInit["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/polyfill-php81","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"]
!!PluginA:1.1.1.0 !!PluginA:1.1.1.0
!!PluginB:2.2.2.0 !!PluginB:2.2.2.0
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
- Upgrading plugin/b (2.2.2 => 2.2.3): Mirroring from plugin-b - Upgrading plugin/b (2.2.2 => 2.2.3): Mirroring from plugin-b
!!PluginBInit["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"] !!PluginBInit["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/polyfill-php81","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"]
!!PluginA:1.1.2.0 !!PluginA:1.1.2.0
!!PluginB:2.2.2.0 !!PluginB:2.2.2.0
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
@ -45,7 +45,7 @@ Generating autoload files
2 packages you are using are looking for funding. 2 packages you are using are looking for funding.
Use the `composer fund` command to find out more! Use the `composer fund` command to find out more!
> Hooks::postUpdate > Hooks::postUpdate
!!PostUpdate:["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"] !!PostUpdate:["composer/ca-bundle","composer/class-map-generator","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","seld/signal-handler","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/polyfill-php81","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"]
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
!!PluginA:1.1.2.0 !!PluginA:1.1.2.0
!!PluginB:2.2.3.0 !!PluginB:2.2.3.0