Skip to content
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

feat: Ability to run Fixer with parallel runner 🎉 #7777

Merged
merged 77 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
f48325b
Register hidden `WorkerCommand`
Wirone Jan 12, 2024
186bec4
Configuration flow for parallel run
Wirone Jan 12, 2024
eebe4ef
Extract factory method for linting aware file iterator
Wirone Jan 22, 2024
b788803
Initial implementation of parallel analysis 🎉
Wirone Jan 25, 2024
c12f95f
Use sequential runner by default
Wirone Jan 27, 2024
9fb0766
Display information about parallel runner being experimental feature
Wirone Jan 27, 2024
22d6bf3
Auto detection for parallel config
Wirone Jan 27, 2024
2afff3b
Use parallel runner with config auto-detection for the project itself
Wirone Jan 28, 2024
845b4ab
Pass analysis errors from worker to the main runner and report them t…
Wirone Jan 28, 2024
32355df
Use exact same PHP binary for worker process
Wirone Jan 29, 2024
d5c4283
Remove quasi-parallel Composer script
Wirone Jan 29, 2024
ea93685
Document usage of parallel runner
Wirone Jan 29, 2024
6f11b20
Allow only static factory for creating `Process`
Wirone Jan 30, 2024
6bdfa0e
Determine files count early from simple iterator
Wirone Jan 30, 2024
039a1f7
Revert BC break in `Runner` signature
Wirone Feb 1, 2024
e265e51
Improve type in `Error` constructor
Wirone Feb 1, 2024
93935d9
Fix test for fallback ParallelConfig
Wirone Feb 1, 2024
dd53801
Explicitly require `react/stream`
Wirone Feb 7, 2024
1f892a6
Introduce `DirectoryInterface::getAbsolutePath()`
Wirone Feb 7, 2024
d0364b1
Support cache in parallel analysis
Wirone Feb 7, 2024
63529c2
Save cache info in parallel analysis only when it makes sense
Wirone Feb 7, 2024
39a8c0e
Apply Julien's suggestions from code review
Wirone Feb 7, 2024
41ed875
Sync `--using-cache` description
Wirone Feb 7, 2024
34f3de0
Use `InvalidArgumentException` for parallelisation misconfiguration +…
Wirone Feb 7, 2024
4bee6dd
Fix BC-break in `DirectoryInterface`
Wirone Feb 8, 2024
e3726fb
Report file analysis result per file instead of per chunk
Wirone Feb 8, 2024
c9b75bf
Clean up parallel actions handling
Wirone Feb 8, 2024
4d1e7aa
Dirty workaround for running sequential runner in tests
Wirone Feb 12, 2024
9d965ee
Add most of the tests
Wirone Feb 13, 2024
4d477ff
Extract process creation to separate factory
Wirone Feb 14, 2024
2ec2ed8
Fix "$nextResult must not be accessed before initialization"
Wirone Feb 14, 2024
19d5bf2
Fix tests on 7.4 with lowest deps
Wirone Feb 14, 2024
cffeb97
Test that `FixCommand` can be run with parallel runner
Wirone Feb 14, 2024
b92bb52
Allow wider range of React packages
Wirone Feb 14, 2024
df9184c
Introduce `--sequential` option to `fix` command
Wirone Feb 15, 2024
24ac18b
More tests, more coverage
Wirone Feb 15, 2024
41cb3e0
Fix smoke tests
Wirone Feb 15, 2024
3c9af57
Fix parallelisation for PHAR usage
Wirone Feb 15, 2024
b06ad0c
Add missing phpDoc for callable supported in `ProcessPool` constructor
Wirone Feb 15, 2024
a305038
Add missing tests
Wirone Feb 15, 2024
53bd005
Backward compatibility for `cs:fix:parallel` script
Wirone Feb 16, 2024
f0918dd
Test that callback is executed on server close
Wirone Feb 16, 2024
f8798a5
Test server-worker communication
Wirone Feb 16, 2024
def6275
Add error handling
Wirone Mar 1, 2024
cd8eb2f
Fix test file/class name
Wirone Mar 1, 2024
a18b4e0
Add simple test for `PhpCsFixer\Runner\Parallel\Process` class
Wirone Mar 1, 2024
2cec9a2
Remove superfluous assertion
Wirone Mar 1, 2024
21f4e28
Run `WorkerCommandTest::testWorkerCommunicatesWithTheServer` only on …
Wirone Mar 1, 2024
2053e7f
Add various tests related to error handling
Wirone Mar 1, 2024
e9d5e73
Test future mode in Config
Wirone Mar 7, 2024
d5f6f47
Skip linting on file iteration in main process of parallel runner
Wirone Mar 20, 2024
4d3bf56
Add todos for 4.0
Wirone Mar 20, 2024
34308f9
Move check for file iterator presence to `getFilteringFileIterator()`
Wirone Mar 25, 2024
a766504
Fallback to default parallel config only if not defined explicitly du…
Wirone Mar 25, 2024
eaa1354
Always print information about CPU(s) used for analysis
Wirone Mar 26, 2024
a4cc010
Collection of dispatched events is a list
Wirone Mar 26, 2024
cfbf0bb
No need for class property to store `ReadonlyCacheManager` in worker …
Wirone Mar 26, 2024
2d05341
`ParallelAction` is not instantiable
Wirone Mar 26, 2024
71b089e
Make `RunnerConfig` internal
Wirone Mar 26, 2024
051b9ee
Do not expect tests for classes without public methods
Wirone Mar 26, 2024
763cc8b
Fix smoke tests
Wirone Mar 26, 2024
df35f4f
Align with PHPStan level 7
Wirone May 1, 2024
ee9def7
Align `ReadonlyCacheManager` with changes in main branch
Wirone May 6, 2024
c6ea784
Refactoring and cosmetic changes
keradus May 7, 2024
97a2764
More strict approach in `WorkerCommand`
keradus May 7, 2024
ab01e66
Auto-detected parallel config by default for future mode or as an opt…
keradus May 7, 2024
69b64be
Reuse existing cache hash to avoid IO read operation
keradus May 7, 2024
f347f13
Fix "well-defined arrays" CI step
Wirone May 7, 2024
077fd6b
Add explanation why some tests are skipped on Windows
Wirone May 7, 2024
b7ac50d
Clean up loop that re-reports errors from worker to main process' err…
Wirone May 7, 2024
5a0cc3e
Workaround for `ParallelConfigFactory` testability around CPU detection
Wirone May 7, 2024
5069549
Flip naming convention for `ParallelAction` consts and improve them
Wirone May 7, 2024
d613ade
Explicit `->toString()` conversion for process identifiers
Wirone May 8, 2024
7aa838b
Support for `--stop-on-violation` in parallel mode
Wirone May 8, 2024
f63b4d5
Rework parallel-related warnings displayed during analysis
Wirone May 13, 2024
0ad99f6
Rework error handling in workers
Wirone May 14, 2024
1bae68b
Fix smoke tests
Wirone May 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .php-cs-fixer.dist.php
Expand Up @@ -14,8 +14,10 @@

use PhpCsFixer\Config;
Wirone marked this conversation as resolved.
Show resolved Hide resolved
use PhpCsFixer\Finder;
use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

return (new Config())
->setParallelConfig(ParallelConfigFactory::detect()) // @TODO 4.0 no need to call this manually
->setRiskyAllowed(true)
->setRules([
'@PHP74Migration' => true,
Expand Down
14 changes: 12 additions & 2 deletions composer.json
Expand Up @@ -24,8 +24,15 @@
"ext-filter": "*",
"ext-json": "*",
"ext-tokenizer": "*",
"clue/ndjson-react": "^1.0",
"composer/semver": "^3.4",
"composer/xdebug-handler": "^3.0.3",
"fidry/cpu-core-counter": "^1.0",
keradus marked this conversation as resolved.
Show resolved Hide resolved
"react/child-process": "^0.6.5",
keradus marked this conversation as resolved.
Show resolved Hide resolved
"react/event-loop": "^1.0",
"react/promise": "^2.0 || ^3.0",
"react/socket": "^1.0",
"react/stream": "^1.0",
"sebastian/diff": "^4.0 || ^5.0 || ^6.0",
"symfony/console": "^5.4 || ^6.0 || ^7.0",
"symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0",
Expand Down Expand Up @@ -87,7 +94,10 @@
],
"cs:check": "@php php-cs-fixer check --verbose --diff",
"cs:fix": "@php php-cs-fixer fix",
"cs:fix:parallel": "echo '🔍 Will run in batches of 50 files.'; if [[ -f .php-cs-fixer.php ]]; then FIXER_CONFIG=.php-cs-fixer.php; else FIXER_CONFIG=.php-cs-fixer.dist.php; fi; php php-cs-fixer list-files --config=$FIXER_CONFIG | xargs -n 50 -P 8 php php-cs-fixer fix --config=$FIXER_CONFIG --path-mode intersection 2> /dev/null",
Wirone marked this conversation as resolved.
Show resolved Hide resolved
"cs:fix:parallel": [
"echo '⚠️ This script is deprecated! Utilise built-in parallelisation instead.';",
"@cs:fix"
],
"docs": "@php dev-tools/doc.php",
"infection": "@test:mutation",
"install-tools": "@composer --working-dir=dev-tools install",
Expand Down Expand Up @@ -156,7 +166,7 @@
"auto-review": "Execute Auto-review",
"cs:check": "Check coding standards",
"cs:fix": "Fix coding standards",
"cs:fix:parallel": "Fix coding standards in naive parallel mode (using xargs)",
"cs:fix:parallel": "⚠️DEPRECATED! Use cs:fix with proper parallel config",
"docs": "Regenerate docs",
"infection": "Alias for 'test:mutation'",
"install-tools": "Install DEV tools",
Expand Down
22 changes: 19 additions & 3 deletions doc/usage.rst
Expand Up @@ -21,11 +21,25 @@ If you do not have config file, you can run following command to fix non-hidden,

php php-cs-fixer.phar fix .

With some magic of tools provided by your OS, you can also fix files in parallel:
You can also fix files in parallel, utilising more CPU cores. You can do this by using config class that implements ``PhpCsFixer\Runner\Parallel\ParallelConfig\ParallelAwareConfigInterface``, and use ``setParallelConfig()`` method. Recommended way is to utilise auto-detecting parallel configuration:

.. code-block:: console
.. code-block:: php

php php-cs-fixer.phar list-files --config=.php-cs-fixer.dist.php | xargs -n 50 -P 8 php php-cs-fixer.phar fix --config=.php-cs-fixer.dist.php --path-mode intersection -v
<?php

return (new PhpCsFixer\Config())
->setParallelConfig(ParallelConfigFactory::detect())
;

However, in some case you may want to fine-tune parallelisation with explicit values (e.g. in environments where auto-detection does not work properly and suggests more cores than it should):

.. code-block:: php

<?php

return (new PhpCsFixer\Config())
->setParallelConfig(new ParallelConfig(4, 20))
;

You can limit process to given file or files in a given directory and its subdirectories:

Expand Down Expand Up @@ -98,6 +112,8 @@ Complete configuration for rules can be supplied using a ``json`` formatted stri

The ``--dry-run`` flag will run the fixer without making changes to your files (implicitly set when you use ``check`` command).

The ``--sequential`` flag will enforce sequential analysis even if parallel config is provided.
Wirone marked this conversation as resolved.
Show resolved Hide resolved

The ``--diff`` flag can be used to let the fixer output all the changes it makes in ``udiff`` format.

The ``--allow-risky`` option (pass ``yes`` or ``no``) allows you to set whether risky rules may run. Default value is taken from config file.
Expand Down
25 changes: 24 additions & 1 deletion src/Config.php
Expand Up @@ -15,13 +15,15 @@
namespace PhpCsFixer;

use PhpCsFixer\Fixer\FixerInterface;
use PhpCsFixer\Runner\Parallel\ParallelConfig;
use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

/**
* @author Fabien Potencier <fabien@symfony.com>
* @author Katsuhiro Ogawa <ko.fivestar@gmail.com>
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
*/
class Config implements ConfigInterface
class Config implements ConfigInterface, ParallelAwareConfigInterface
{
private string $cacheFile = '.php-cs-fixer.cache';

Expand All @@ -47,6 +49,8 @@

private string $name;

private ParallelConfig $parallelConfig;

/**
* @var null|string
*/
Expand All @@ -71,6 +75,13 @@
$this->name = $name;
$this->rules = ['@PSR12' => true];
}

// @TODO 4.0 cleanup
if (Utils::isFutureModeEnabled() || filter_var(getenv('PHP_CS_FIXER_PARALLEL'), FILTER_VALIDATE_BOOL)) {

Check warning on line 80 in src/Config.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "LogicalOr": --- Original +++ New @@ @@ $this->rules = ['@psr12' => true]; } // @todo 4.0 cleanup - if (Utils::isFutureModeEnabled() || filter_var(getenv('PHP_CS_FIXER_PARALLEL'), FILTER_VALIDATE_BOOL)) { + if (Utils::isFutureModeEnabled() && filter_var(getenv('PHP_CS_FIXER_PARALLEL'), FILTER_VALIDATE_BOOL)) { $this->parallelConfig = ParallelConfigFactory::detect(); } else { $this->parallelConfig = ParallelConfigFactory::sequential();
$this->parallelConfig = ParallelConfigFactory::detect();
} else {
$this->parallelConfig = ParallelConfigFactory::sequential();
}
}

public function getCacheFile(): string
Expand Down Expand Up @@ -118,6 +129,11 @@
return $this->name;
}

public function getParallelConfig(): ParallelConfig
{
return $this->parallelConfig;
}

public function getPhpExecutable(): ?string
{
return $this->phpExecutable;
Expand Down Expand Up @@ -189,6 +205,13 @@
return $this;
}

public function setParallelConfig(ParallelConfig $config): ConfigInterface
{
$this->parallelConfig = $config;

return $this;
}

public function setPhpExecutable(?string $phpExecutable): ConfigInterface
{
$this->phpExecutable = $phpExecutable;
Expand Down
46 changes: 46 additions & 0 deletions src/Console/Application.php
Expand Up @@ -21,12 +21,15 @@
use PhpCsFixer\Console\Command\ListFilesCommand;
use PhpCsFixer\Console\Command\ListSetsCommand;
use PhpCsFixer\Console\Command\SelfUpdateCommand;
use PhpCsFixer\Console\Command\WorkerCommand;
use PhpCsFixer\Console\SelfUpdate\GithubClient;
use PhpCsFixer\Console\SelfUpdate\NewVersionChecker;
use PhpCsFixer\PharChecker;
use PhpCsFixer\Runner\Parallel\WorkerException;
use PhpCsFixer\ToolInfo;
use PhpCsFixer\Utils;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\ListCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
Expand All @@ -45,6 +48,7 @@
public const VERSION_CODENAME = '15 Keys Accelerate';

private ToolInfo $toolInfo;
private ?Command $executedCommand = null;

public function __construct()
{
Expand All @@ -63,6 +67,7 @@
$this->toolInfo,
new PharChecker()
));
$this->add(new WorkerCommand($this->toolInfo));

Check warning on line 70 in src/Console/Application.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $this->add(new ListFilesCommand($this->toolInfo)); $this->add(new ListSetsCommand()); $this->add(new SelfUpdateCommand(new NewVersionChecker(new GithubClient()), $this->toolInfo, new PharChecker())); - $this->add(new WorkerCommand($this->toolInfo)); + } public static function getMajorVersion() : int {
}

public static function getMajorVersion(): int
Expand Down Expand Up @@ -160,4 +165,45 @@
{
return [new HelpCommand(), new ListCommand()];
}

/**
* @throws \Throwable
*/
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output): int
{
$this->executedCommand = $command;

return parent::doRunCommand($command, $input, $output);
}

protected function doRenderThrowable(\Throwable $e, OutputInterface $output): void
{
// Since parallel analysis utilises child processes, and they have their own output,
// we need to capture the output of the child process to determine it there was an exception.
// Default render format is not machine-friendly, so we need to override it for `worker` command,
// in order to be able to easily parse exception data for further displaying on main process' side.
if ($this->executedCommand instanceof WorkerCommand) {

Check warning on line 185 in src/Console/Application.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "InstanceOf_": --- Original +++ New @@ @@ // we need to capture the output of the child process to determine it there was an exception. // Default render format is not machine-friendly, so we need to override it for `worker` command, // in order to be able to easily parse exception data for further displaying on main process' side. - if ($this->executedCommand instanceof WorkerCommand) { + if (true) { $output->writeln(WorkerCommand::ERROR_PREFIX . json_encode(['class' => \get_class($e), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'code' => $e->getCode(), 'trace' => $e->getTraceAsString()])); return; }
Wirone marked this conversation as resolved.
Show resolved Hide resolved
$output->writeln(WorkerCommand::ERROR_PREFIX.json_encode(
[
'class' => \get_class($e),
'message' => $e->getMessage(),
'file' => $e->getFile(),

Check warning on line 190 in src/Console/Application.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "ArrayItem": --- Original +++ New @@ @@ // Default render format is not machine-friendly, so we need to override it for `worker` command, // in order to be able to easily parse exception data for further displaying on main process' side. if ($this->executedCommand instanceof WorkerCommand) { - $output->writeln(WorkerCommand::ERROR_PREFIX . json_encode(['class' => \get_class($e), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'code' => $e->getCode(), 'trace' => $e->getTraceAsString()])); + $output->writeln(WorkerCommand::ERROR_PREFIX . json_encode(['class' => \get_class($e), 'message' => $e->getMessage(), 'file' > $e->getFile(), 'line' => $e->getLine(), 'code' => $e->getCode(), 'trace' => $e->getTraceAsString()])); return; } parent::doRenderThrowable($e, $output);
'line' => $e->getLine(),

Check warning on line 191 in src/Console/Application.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "ArrayItem": --- Original +++ New @@ @@ // Default render format is not machine-friendly, so we need to override it for `worker` command, // in order to be able to easily parse exception data for further displaying on main process' side. if ($this->executedCommand instanceof WorkerCommand) { - $output->writeln(WorkerCommand::ERROR_PREFIX . json_encode(['class' => \get_class($e), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'code' => $e->getCode(), 'trace' => $e->getTraceAsString()])); + $output->writeln(WorkerCommand::ERROR_PREFIX . json_encode(['class' => \get_class($e), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' > $e->getLine(), 'code' => $e->getCode(), 'trace' => $e->getTraceAsString()])); return; } parent::doRenderThrowable($e, $output);
'code' => $e->getCode(),

Check warning on line 192 in src/Console/Application.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "ArrayItem": --- Original +++ New @@ @@ // Default render format is not machine-friendly, so we need to override it for `worker` command, // in order to be able to easily parse exception data for further displaying on main process' side. if ($this->executedCommand instanceof WorkerCommand) { - $output->writeln(WorkerCommand::ERROR_PREFIX . json_encode(['class' => \get_class($e), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'code' => $e->getCode(), 'trace' => $e->getTraceAsString()])); + $output->writeln(WorkerCommand::ERROR_PREFIX . json_encode(['class' => \get_class($e), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'code' > $e->getCode(), 'trace' => $e->getTraceAsString()])); return; } parent::doRenderThrowable($e, $output);
'trace' => $e->getTraceAsString(),

Check warning on line 193 in src/Console/Application.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "ArrayItem": --- Original +++ New @@ @@ // Default render format is not machine-friendly, so we need to override it for `worker` command, // in order to be able to easily parse exception data for further displaying on main process' side. if ($this->executedCommand instanceof WorkerCommand) { - $output->writeln(WorkerCommand::ERROR_PREFIX . json_encode(['class' => \get_class($e), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'code' => $e->getCode(), 'trace' => $e->getTraceAsString()])); + $output->writeln(WorkerCommand::ERROR_PREFIX . json_encode(['class' => \get_class($e), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'code' => $e->getCode(), 'trace' > $e->getTraceAsString()])); return; } parent::doRenderThrowable($e, $output);
]
));

return;
}

parent::doRenderThrowable($e, $output);

if ($output->isVeryVerbose() && $e instanceof WorkerException) {
$output->writeln('<comment>Original trace from worker:</comment>');
$output->writeln('');
$output->writeln($e->getOriginalTraceAsString());
$output->writeln('');
}
}
}
37 changes: 35 additions & 2 deletions src/Console/Command/FixCommand.php
Expand Up @@ -30,6 +30,7 @@
use PhpCsFixer\ToolInfoInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
Expand Down Expand Up @@ -150,6 +151,8 @@

The <comment>--dry-run</comment> flag will run the fixer without making changes to your files.

The <comment>--sequential</comment> flag will enforce sequential analysis even if parallel config is provided.

The <comment>--diff</comment> flag can be used to let the fixer output all the changes it makes.

The <comment>--allow-risky</comment> option (pass `yes` or `no`) allows you to set whether risky rules may run. Default value is taken from config file.
Expand Down Expand Up @@ -206,12 +209,13 @@
new InputOption('config', '', InputOption::VALUE_REQUIRED, 'The path to a config file.'),
new InputOption('dry-run', '', InputOption::VALUE_NONE, 'Only shows which files would have been modified.'),
new InputOption('rules', '', InputOption::VALUE_REQUIRED, 'List of rules that should be run against configured paths.'),
new InputOption('using-cache', '', InputOption::VALUE_REQUIRED, 'Does cache should be used (can be `yes` or `no`).'),
new InputOption('using-cache', '', InputOption::VALUE_REQUIRED, 'Should cache be used (can be `yes` or `no`).'),
keradus marked this conversation as resolved.
Show resolved Hide resolved
new InputOption('cache-file', '', InputOption::VALUE_REQUIRED, 'The path to the cache file.'),
new InputOption('diff', '', InputOption::VALUE_NONE, 'Prints diff for each file.'),
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'To output results in other formats.'),
new InputOption('stop-on-violation', '', InputOption::VALUE_NONE, 'Stop execution on first violation.'),
new InputOption('show-progress', '', InputOption::VALUE_REQUIRED, 'Type of progress indicator (none, dots).'),
new InputOption('sequential', '', InputOption::VALUE_NONE, 'Enforce sequential analysis.'),
]
);
}
Expand All @@ -229,7 +233,7 @@

$resolver = new ConfigurationResolver(
$this->defaultConfig,
[

Check warning on line 236 in src/Console/Command/FixCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "ArrayItemRemoval": --- Original +++ New @@ @@ if (null !== $passedConfig && null !== $passedRules) { throw new InvalidConfigurationException('Passing both `--config` and `--rules` options is not allowed.'); } - $resolver = new ConfigurationResolver($this->defaultConfig, ['allow-risky' => $input->getOption('allow-risky'), 'config' => $passedConfig, 'dry-run' => $this->isDryRun($input), 'rules' => $passedRules, 'path' => $input->getArgument('path'), 'path-mode' => $input->getOption('path-mode'), 'using-cache' => $input->getOption('using-cache'), 'cache-file' => $input->getOption('cache-file'), 'format' => $input->getOption('format'), 'diff' => $input->getOption('diff'), 'stop-on-violation' => $input->getOption('stop-on-violation'), 'verbosity' => $verbosity, 'show-progress' => $input->getOption('show-progress'), 'sequential' => $input->getOption('sequential')], getcwd(), $this->toolInfo); + $resolver = new ConfigurationResolver($this->defaultConfig, ['config' => $passedConfig, 'dry-run' => $this->isDryRun($input), 'rules' => $passedRules, 'path' => $input->getArgument('path'), 'path-mode' => $input->getOption('path-mode'), 'using-cache' => $input->getOption('using-cache'), 'cache-file' => $input->getOption('cache-file'), 'format' => $input->getOption('format'), 'diff' => $input->getOption('diff'), 'stop-on-violation' => $input->getOption('stop-on-violation'), 'verbosity' => $verbosity, 'show-progress' => $input->getOption('show-progress'), 'sequential' => $input->getOption('sequential')], getcwd(), $this->toolInfo); $reporter = $resolver->getReporter(); $stdErr = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : ('txt' === $reporter->getFormat() ? $output : null); if (null !== $stdErr) {
'allow-risky' => $input->getOption('allow-risky'),
'config' => $passedConfig,
'dry-run' => $this->isDryRun($input),
Expand All @@ -243,6 +247,7 @@
'stop-on-violation' => $input->getOption('stop-on-violation'),
'verbosity' => $verbosity,
'show-progress' => $input->getOption('show-progress'),
'sequential' => $input->getOption('sequential'),
],
getcwd(),
$this->toolInfo
Expand All @@ -256,6 +261,31 @@

if (null !== $stdErr) {
$stdErr->writeln(Application::getAboutWithRuntime(true));
$isParallel = $resolver->getParallelConfig()->getMaxProcesses() > 1;

$stdErr->writeln(sprintf(
'Running analysis on %d core%s.',
$resolver->getParallelConfig()->getMaxProcesses(),
$isParallel ? sprintf(
's with %d file%s per process',
$resolver->getParallelConfig()->getFilesPerProcess(),
$resolver->getParallelConfig()->getFilesPerProcess() > 1 ? 's' : ''
) : ' sequentially'
));

/** @TODO v4 remove warnings related to parallel runner */
$usageDocs = 'https://cs.symfony.com/doc/usage.html';
$stdErr->writeln(sprintf(
$stdErr->isDecorated() ? '<bg=yellow;fg=black;>%s</>' : '%s',

Check warning on line 279 in src/Console/Command/FixCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "Ternary": --- Original +++ New @@ @@ $stdErr->writeln(sprintf('Running analysis on %d core%s.', $resolver->getParallelConfig()->getMaxProcesses(), $isParallel ? sprintf('s with %d file%s per process', $resolver->getParallelConfig()->getFilesPerProcess(), $resolver->getParallelConfig()->getFilesPerProcess() > 1 ? 's' : '') : ' sequentially')); /** @todo v4 remove warnings related to parallel runner */ $usageDocs = 'https://cs.symfony.com/doc/usage.html'; - $stdErr->writeln(sprintf($stdErr->isDecorated() ? '<bg=yellow;fg=black;>%s</>' : '%s', $isParallel ? 'Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!' : sprintf('You can enable parallel runner and speed up the analysis! Please see %s for more information.', $stdErr->isDecorated() ? sprintf('<href=%s;bg=yellow;fg=red;bold>usage docs</>', OutputFormatter::escape($usageDocs)) : $usageDocs))); + $stdErr->writeln(sprintf($stdErr->isDecorated() ? '%s' : '<bg=yellow;fg=black;>%s</>', $isParallel ? 'Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!' : sprintf('You can enable parallel runner and speed up the analysis! Please see %s for more information.', $stdErr->isDecorated() ? sprintf('<href=%s;bg=yellow;fg=red;bold>usage docs</>', OutputFormatter::escape($usageDocs)) : $usageDocs))); $configFile = $resolver->getConfigFile(); $stdErr->writeln(sprintf('Loaded config <comment>%s</comment>%s.', $resolver->getConfig()->getName(), null === $configFile ? '' : ' from "' . $configFile . '"')); if ($resolver->getUsingCache()) {
$isParallel
? 'Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!'
: sprintf(
'You can enable parallel runner and speed up the analysis! Please see %s for more information.',
$stdErr->isDecorated()

Check warning on line 284 in src/Console/Command/FixCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 mutation tests

Escaped Mutant for Mutator "Ternary": --- Original +++ New @@ @@ $stdErr->writeln(sprintf('Running analysis on %d core%s.', $resolver->getParallelConfig()->getMaxProcesses(), $isParallel ? sprintf('s with %d file%s per process', $resolver->getParallelConfig()->getFilesPerProcess(), $resolver->getParallelConfig()->getFilesPerProcess() > 1 ? 's' : '') : ' sequentially')); /** @todo v4 remove warnings related to parallel runner */ $usageDocs = 'https://cs.symfony.com/doc/usage.html'; - $stdErr->writeln(sprintf($stdErr->isDecorated() ? '<bg=yellow;fg=black;>%s</>' : '%s', $isParallel ? 'Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!' : sprintf('You can enable parallel runner and speed up the analysis! Please see %s for more information.', $stdErr->isDecorated() ? sprintf('<href=%s;bg=yellow;fg=red;bold>usage docs</>', OutputFormatter::escape($usageDocs)) : $usageDocs))); + $stdErr->writeln(sprintf($stdErr->isDecorated() ? '<bg=yellow;fg=black;>%s</>' : '%s', $isParallel ? 'Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!' : sprintf('You can enable parallel runner and speed up the analysis! Please see %s for more information.', $stdErr->isDecorated() ? $usageDocs : sprintf('<href=%s;bg=yellow;fg=red;bold>usage docs</>', OutputFormatter::escape($usageDocs))))); $configFile = $resolver->getConfigFile(); $stdErr->writeln(sprintf('Loaded config <comment>%s</comment>%s.', $resolver->getConfig()->getName(), null === $configFile ? '' : ' from "' . $configFile . '"')); if ($resolver->getUsingCache()) {
? sprintf('<href=%s;bg=yellow;fg=red;bold>usage docs</>', OutputFormatter::escape($usageDocs))
: $usageDocs
)
));

$configFile = $resolver->getConfigFile();
$stdErr->writeln(sprintf('Loaded config <comment>%s</comment>%s.', $resolver->getConfig()->getName(), null === $configFile ? '' : ' from "'.$configFile.'"'));
Expand Down Expand Up @@ -297,7 +327,10 @@
$resolver->isDryRun(),
$resolver->getCacheManager(),
$resolver->getDirectory(),
$resolver->shouldStopOnViolation()
$resolver->shouldStopOnViolation(),
$resolver->getParallelConfig(),
$input,
$resolver->getConfigFile()
);

$this->eventDispatcher->addListener(FixerFileProcessedEvent::NAME, [$progressOutput, 'onFixerFileProcessed']);
Expand Down
9 changes: 7 additions & 2 deletions src/Console/Command/FixCommandExitStatusCalculator.php
Expand Up @@ -28,8 +28,13 @@ final class FixCommandExitStatusCalculator
public const EXIT_STATUS_FLAG_HAS_INVALID_FIXER_CONFIG = 32;
public const EXIT_STATUS_FLAG_EXCEPTION_IN_APP = 64;

public function calculate(bool $isDryRun, bool $hasChangedFiles, bool $hasInvalidErrors, bool $hasExceptionErrors, bool $hasLintErrorsAfterFixing): int
{
public function calculate(
bool $isDryRun,
bool $hasChangedFiles,
bool $hasInvalidErrors,
bool $hasExceptionErrors,
bool $hasLintErrorsAfterFixing
): int {
$exitStatus = 0;

if ($isDryRun) {
Expand Down