Add a way to control which scripts get args and where (#12086)
Add support for `@no_additional_args` and `@additional_args` tags inside script handlers.pull/11942/head
parent
8bc8c4383a
commit
17930441a1
|
@ -410,6 +410,38 @@ JSON array of commands.
|
|||
You can also call a shell/bash script, which will have the path to
|
||||
the PHP executable available in it as a `PHP_BINARY` env var.
|
||||
|
||||
## Controlling additional arguments
|
||||
|
||||
When running scripts like `composer script-name arg arg2` or `composer script-name -- --option`,
|
||||
Composer will by default append `arg`, `arg2` and `--option` to the script's command.
|
||||
|
||||
If you do not want these args in a given command, you can put `@no_additional_args`
|
||||
anywhere in it, that will remove the default behavior and that flag will be removed
|
||||
as well before running the command.
|
||||
|
||||
If you want the args to be added somewhere else than at the very end, then you can put
|
||||
`@additional_args` to be able to choose exactly where they go.
|
||||
|
||||
For example running `composer run-commands ARG` with the below config:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"run-commands": [
|
||||
"echo hello @no_additional_args",
|
||||
"command-with-args @additional_args && do-something-without-args --here"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Would end up executing these commands:
|
||||
|
||||
```
|
||||
echo hello
|
||||
command-with-args ARG && do-something-without-args --here
|
||||
```
|
||||
|
||||
## Setting environment variables
|
||||
|
||||
To set an environment variable in a cross-platform way, you can use `@putenv`:
|
||||
|
|
|
@ -202,7 +202,12 @@ class EventDispatcher
|
|||
$return = 0;
|
||||
$this->ensureBinDirIsInPath();
|
||||
|
||||
$formattedEventNameWithArgs = $event->getName() . ($event->getArguments() !== [] ? ' (' . implode(', ', $event->getArguments()) . ')' : '');
|
||||
$additionalArgs = $event->getArguments();
|
||||
if (is_string($callable) && str_contains($callable, '@no_additional_args')) {
|
||||
$callable = Preg::replace('{ ?@no_additional_args}', '', $callable);
|
||||
$additionalArgs = [];
|
||||
}
|
||||
$formattedEventNameWithArgs = $event->getName() . ($additionalArgs !== [] ? ' (' . implode(', ', $additionalArgs) . ')' : '');
|
||||
if (!is_string($callable)) {
|
||||
if (!is_callable($callable)) {
|
||||
$className = is_object($callable[0]) ? get_class($callable[0]) : $callable[0];
|
||||
|
@ -220,7 +225,12 @@ class EventDispatcher
|
|||
$scriptName = $script[0];
|
||||
unset($script[0]);
|
||||
|
||||
$args = array_merge($script, $event->getArguments());
|
||||
$index = array_search('@additional_args', $script, true);
|
||||
if ($index !== false) {
|
||||
$args = array_splice($script, $index, 0, $additionalArgs);
|
||||
} else {
|
||||
$args = array_merge($script, $additionalArgs);
|
||||
}
|
||||
$flags = $event->getFlags();
|
||||
if (isset($flags['script-alias-input'])) {
|
||||
$argsString = implode(' ', array_map(static function ($arg) { return ProcessExecutor::escape($arg); }, $script));
|
||||
|
@ -294,7 +304,7 @@ class EventDispatcher
|
|||
$app->add($cmd);
|
||||
$app->setDefaultCommand((string) $cmd->getName(), true);
|
||||
try {
|
||||
$args = implode(' ', array_map(static function ($arg) { return ProcessExecutor::escape($arg); }, $event->getArguments()));
|
||||
$args = implode(' ', array_map(static function ($arg) { return ProcessExecutor::escape($arg); }, $additionalArgs));
|
||||
// reusing the output from $this->io is mostly needed for tests, but generally speaking
|
||||
// it does not hurt to keep the same stream as the current Application
|
||||
if ($this->io instanceof ConsoleIO) {
|
||||
|
@ -313,13 +323,17 @@ class EventDispatcher
|
|||
throw $e;
|
||||
}
|
||||
} else {
|
||||
$args = implode(' ', array_map(['Composer\Util\ProcessExecutor', 'escape'], $event->getArguments()));
|
||||
$args = implode(' ', array_map(['Composer\Util\ProcessExecutor', 'escape'], $additionalArgs));
|
||||
|
||||
// @putenv does not receive arguments
|
||||
if (strpos($callable, '@putenv ') === 0) {
|
||||
$exec = $callable;
|
||||
} else {
|
||||
$exec = $callable . ($args === '' ? '' : ' '.$args);
|
||||
if (str_contains($callable, '@additional_args')) {
|
||||
$exec = str_replace('@additional_args', $args, $callable);
|
||||
} else {
|
||||
$exec = $callable . ($args === '' ? '' : ' '.$args);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->io->isVerbose()) {
|
||||
|
|
|
@ -311,6 +311,51 @@ class EventDispatcherTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
public function testDispatcherSupportForAdditionalArgs(): void
|
||||
{
|
||||
$process = $this->getProcessExecutorMock();
|
||||
$dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
|
||||
->setConstructorArgs([
|
||||
$this->createComposerInstance(),
|
||||
$io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE),
|
||||
$process,
|
||||
])
|
||||
->onlyMethods([
|
||||
'getListeners',
|
||||
])
|
||||
->getMock();
|
||||
|
||||
$reflMethod = new \ReflectionMethod($dispatcher, 'getPhpExecCommand');
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$reflMethod->setAccessible(true);
|
||||
}
|
||||
$phpCmd = $reflMethod->invoke($dispatcher);
|
||||
|
||||
$args = ProcessExecutor::escape('ARG').' '.ProcessExecutor::escape('ARG2').' '.ProcessExecutor::escape('--arg');
|
||||
$process->expects([
|
||||
'echo -n foo',
|
||||
$phpCmd.' foo.php '.$args.' then the rest',
|
||||
'echo -n bar '.$args,
|
||||
], true);
|
||||
|
||||
$listeners = [
|
||||
'echo -n foo @no_additional_args',
|
||||
'@php foo.php @additional_args then the rest',
|
||||
'echo -n bar',
|
||||
];
|
||||
|
||||
$dispatcher->expects($this->atLeastOnce())
|
||||
->method('getListeners')
|
||||
->will($this->returnValue($listeners));
|
||||
|
||||
$dispatcher->dispatchScript(ScriptEvents::POST_INSTALL_CMD, false, ['ARG', 'ARG2', '--arg']);
|
||||
|
||||
$expected = '> post-install-cmd: echo -n foo'.PHP_EOL.
|
||||
'> post-install-cmd: @php foo.php '.$args.' then the rest'.PHP_EOL.
|
||||
'> post-install-cmd: echo -n bar '.$args.PHP_EOL;
|
||||
self::assertEquals($expected, $io->getOutput());
|
||||
}
|
||||
|
||||
public static function createsVendorBinFolderChecksEnvDoesNotContainsBin(): void
|
||||
{
|
||||
mkdir(__DIR__ . '/vendor/bin', 0700, true);
|
||||
|
|
Loading…
Reference in New Issue