composer(deps-dev): bump ergebnis/php-cs-fixer-config from 2.3.0 to 2.4.0
1 |
<?php
|
|
2 |
|
|
3 |
declare(strict_types=1); |
|
4 |
|
|
5 |
/**
|
|
6 |
* Copyright (c) 2018-2020 Andreas Möller
|
|
7 |
*
|
|
8 |
* For the full copyright and license information, please view
|
|
9 |
* the LICENSE.md file that was distributed with this source code.
|
|
10 |
*
|
|
11 |
* @see https://github.com/ergebnis/composer-normalize
|
|
12 |
*/
|
|
13 |
|
|
14 |
namespace Ergebnis\Composer\Normalize\Command; |
|
15 |
|
|
16 |
use Composer\Command; |
|
17 |
use Composer\Console\Application; |
|
18 |
use Composer\Factory; |
|
19 |
use Composer\IO; |
|
20 |
use Ergebnis\Composer\Normalize\Exception; |
|
21 |
use Ergebnis\Json\Normalizer; |
|
22 |
use Localheinz\Diff; |
|
23 |
use Symfony\Component\Console; |
|
24 |
|
|
25 |
/**
|
|
26 |
* @internal
|
|
27 |
*/
|
|
28 |
final class NormalizeCommand extends Command\BaseCommand |
|
29 |
{
|
|
30 |
/**
|
|
31 |
* @var array<string, string>
|
|
32 |
*/
|
|
33 |
private static $indentStyles = [ |
|
34 |
'space' => ' ', |
|
35 |
'tab' => "\t", |
|
36 |
];
|
|
37 |
|
|
38 |
private $factory; |
|
39 |
|
|
40 |
private $normalizer; |
|
41 |
|
|
42 |
private $formatter; |
|
43 |
|
|
44 |
private $differ; |
|
45 |
|
|
46 | 1 |
public function __construct( |
47 |
Factory $factory, |
|
48 |
Normalizer\NormalizerInterface $normalizer, |
|
49 |
Normalizer\Format\FormatterInterface $formatter, |
|
50 |
Diff\Differ $differ |
|
51 |
) { |
|
52 | 1 |
parent::__construct('normalize'); |
53 |
|
|
54 | 1 |
$this->factory = $factory; |
55 | 1 |
$this->normalizer = $normalizer; |
56 | 1 |
$this->formatter = $formatter; |
57 | 1 |
$this->differ = $differ; |
58 |
}
|
|
59 |
|
|
60 | 1 |
protected function configure(): void |
61 |
{
|
|
62 | 1 |
$this->setDescription('Normalizes composer.json according to its JSON schema (https://getcomposer.org/schema.json).'); |
63 | 1 |
$this->setDefinition([ |
64 | 1 |
new Console\Input\InputArgument( |
65 | 1 |
'file', |
66 | 1 |
Console\Input\InputArgument::OPTIONAL, |
67 | 1 |
'Path to composer.json file'
|
68 |
),
|
|
69 | 1 |
new Console\Input\InputOption( |
70 | 1 |
'diff', |
71 | 1 |
null, |
72 | 1 |
Console\Input\InputOption::VALUE_NONE, |
73 | 1 |
'Show the results of normalizing'
|
74 |
),
|
|
75 | 1 |
new Console\Input\InputOption( |
76 | 1 |
'dry-run', |
77 | 1 |
null, |
78 | 1 |
Console\Input\InputOption::VALUE_NONE, |
79 | 1 |
'Show the results of normalizing, but do not modify any files'
|
80 |
),
|
|
81 | 1 |
new Console\Input\InputOption( |
82 | 1 |
'indent-size', |
83 | 1 |
null, |
84 | 1 |
Console\Input\InputOption::VALUE_REQUIRED, |
85 | 1 |
'Indent size (an integer greater than 0); should be used with the --indent-style option'
|
86 |
),
|
|
87 | 1 |
new Console\Input\InputOption( |
88 | 1 |
'indent-style', |
89 | 1 |
null, |
90 | 1 |
Console\Input\InputOption::VALUE_REQUIRED, |
91 | 1 |
\sprintf( |
92 |
'Indent style (one of "%s"); should be used with the --indent-size option', |
|
93 | 1 |
\implode('", "', \array_keys(self::$indentStyles)) |
94 |
)
|
|
95 |
),
|
|
96 | 1 |
new Console\Input\InputOption( |
97 | 1 |
'no-check-lock', |
98 | 1 |
null, |
99 | 1 |
Console\Input\InputOption::VALUE_NONE, |
100 | 1 |
'Do not check if lock file is up to date'
|
101 |
),
|
|
102 | 1 |
new Console\Input\InputOption( |
103 | 1 |
'no-update-lock', |
104 | 1 |
null, |
105 | 1 |
Console\Input\InputOption::VALUE_NONE, |
106 | 1 |
'Do not update lock file if it exists'
|
107 |
),
|
|
108 |
]);
|
|
109 |
}
|
|
110 |
|
|
111 | 1 |
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output): int |
112 |
{
|
|
113 | 1 |
$io = $this->getIO(); |
114 |
|
|
115 |
try { |
|
116 | 1 |
$indent = self::indentFrom($input); |
117 | 1 |
} catch (\RuntimeException $exception) { |
118 | 1 |
$io->writeError(\sprintf( |
119 |
'<error>%s</error>', |
|
120 | 1 |
$exception->getMessage() |
121 |
));
|
|
122 |
|
|
123 | 1 |
return 1; |
124 |
}
|
|
125 |
|
|
126 | 1 |
$composerFile = $input->getArgument('file'); |
127 |
|
|
128 | 1 |
if (null === $composerFile) { |
129 | 1 |
$composerFile = Factory::getComposerFile(); |
130 |
}
|
|
131 |
|
|
132 | 1 |
$composer = $this->factory->createComposer( |
133 | 1 |
$io, |
134 |
$composerFile
|
|
135 |
);
|
|
136 |
|
|
137 | 1 |
if (false === $input->getOption('dry-run') && !\is_writable($composerFile)) { |
138 |
$io->writeError(\sprintf( |
|
139 |
'<error>%s is not writable.</error>', |
|
140 |
$composerFile
|
|
141 |
));
|
|
142 |
|
|
143 |
return 1; |
|
144 |
}
|
|
145 |
|
|
146 | 1 |
$locker = $composer->getLocker(); |
147 |
|
|
148 | 1 |
if (false === $input->getOption('no-check-lock') && $locker->isLocked() && !$locker->isFresh()) { |
149 | 1 |
$io->writeError('<error>The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update --lock`.</error>'); |
150 |
|
|
151 | 1 |
return 1; |
152 |
}
|
|
153 |
|
|
154 |
/** @var string $encoded */
|
|
155 | 1 |
$encoded = \file_get_contents($composerFile); |
156 |
|
|
157 | 1 |
$json = Normalizer\Json::fromEncoded($encoded); |
158 |
|
|
159 |
try { |
|
160 | 1 |
$normalized = $this->normalizer->normalize($json); |
161 | 1 |
} catch (Normalizer\Exception\OriginalInvalidAccordingToSchemaException $exception) { |
162 |
$io->writeError('<error>Original composer.json does not match the expected JSON schema:</error>'); |
|
163 |
|
|
164 |
self::showValidationErrors( |
|
165 |
$io, |
|
166 |
...$exception->errors() |
|
167 |
);
|
|
168 |
|
|
169 |
return 1; |
|
170 | 1 |
} catch (Normalizer\Exception\NormalizedInvalidAccordingToSchemaException $exception) { |
171 |
$io->writeError('<error>Normalized composer.json does not match the expected JSON schema:</error>'); |
|
172 |
|
|
173 |
self::showValidationErrors( |
|
174 |
$io, |
|
175 |
...$exception->errors() |
|
176 |
);
|
|
177 |
|
|
178 |
return 1; |
|
179 | 1 |
} catch (\RuntimeException $exception) { |
180 | 1 |
$io->writeError(\sprintf( |
181 |
'<error>%s</error>', |
|
182 | 1 |
$exception->getMessage() |
183 |
));
|
|
184 |
|
|
185 | 1 |
return 1; |
186 |
}
|
|
187 |
|
|
188 | 1 |
$format = $json->format(); |
189 |
|
|
190 | 1 |
if (null !== $indent) { |
191 | 1 |
$format = $format->withIndent($indent); |
192 |
}
|
|
193 |
|
|
194 | 1 |
$formatted = $this->formatter->format( |
195 | 1 |
$normalized, |
196 |
$format
|
|
197 |
);
|
|
198 |
|
|
199 | 1 |
if ($json->encoded() === $formatted->encoded()) { |
200 | 1 |
$io->write(\sprintf( |
201 |
'<info>%s is already normalized.</info>', |
|
202 | 1 |
$composerFile
|
203 |
));
|
|
204 |
|
|
205 | 1 |
return 0; |
206 |
}
|
|
207 |
|
|
208 | 1 |
if (true === $input->getOption('diff') || true === $input->getOption('dry-run')) { |
209 | 1 |
$io->writeError(\sprintf( |
210 |
'<error>%s is not normalized.</error>', |
|
211 | 1 |
$composerFile
|
212 |
));
|
|
213 |
|
|
214 | 1 |
$diff = $this->differ->diff( |
215 | 1 |
$json->encoded(), |
216 | 1 |
$formatted->encoded() |
217 |
);
|
|
218 |
|
|
219 | 1 |
$io->write([ |
220 | 1 |
'', |
221 | 1 |
'<fg=yellow>---------- begin diff ----------</>', |
222 | 1 |
self::formatDiff($diff), |
223 | 1 |
'<fg=yellow>----------- end diff -----------</>', |
224 | 1 |
'', |
225 |
]);
|
|
226 |
}
|
|
227 |
|
|
228 | 1 |
if (true === $input->getOption('dry-run')) { |
229 | 1 |
return 1; |
230 |
}
|
|
231 |
|
|
232 | 1 |
\file_put_contents($composerFile, $formatted); |
233 |
|
|
234 | 1 |
$io->write(\sprintf( |
235 |
'<info>Successfully normalized %s.</info>', |
|
236 | 1 |
$composerFile
|
237 |
));
|
|
238 |
|
|
239 | 1 |
if (true === $input->getOption('no-update-lock') || false === $locker->isLocked()) { |
240 | 1 |
return 0; |
241 |
}
|
|
242 |
|
|
243 | 1 |
$io->write('<info>Updating lock file.</info>'); |
244 |
|
|
245 | 1 |
$application = new Application(); |
246 |
|
|
247 | 1 |
$application->setAutoExit(false); |
248 |
|
|
249 | 1 |
return self::updateLockerInWorkingDirectory( |
250 |
$application, |
|
251 |
$output, |
|
252 | 1 |
\dirname($composerFile) |
253 |
);
|
|
254 |
}
|
|
255 |
|
|
256 |
/**
|
|
257 |
* @throws \RuntimeException
|
|
258 |
*/
|
|
259 | 1 |
private static function indentFrom(Console\Input\InputInterface $input): ?Normalizer\Format\Indent |
260 |
{
|
|
261 |
/** @var null|string $indentSize */
|
|
262 | 1 |
$indentSize = $input->getOption('indent-size'); |
263 |
|
|
264 |
/** @var null|string $indentStyle */
|
|
265 | 1 |
$indentStyle = $input->getOption('indent-style'); |
266 |
|
|
267 | 1 |
if (null === $indentSize && null === $indentStyle) { |
268 | 1 |
return null; |
269 |
}
|
|
270 |
|
|
271 | 1 |
if (null === $indentSize) { |
272 | 1 |
throw new \RuntimeException('When using the indent-style option, an indent size needs to be specified using the indent-size option.'); |
273 |
}
|
|
274 |
|
|
275 | 1 |
if (null === $indentStyle) { |
276 | 1 |
throw new \RuntimeException(\sprintf( |
277 |
'When using the indent-size option, an indent style (one of "%s") needs to be specified using the indent-style option.', |
|
278 | 1 |
\implode('", "', \array_keys(self::$indentStyles)) |
279 |
));
|
|
280 |
}
|
|
281 |
|
|
282 | 1 |
if ((string) (int) $indentSize !== $indentSize || 1 > $indentSize) { |
283 | 1 |
throw new \RuntimeException(\sprintf( |
284 |
'Indent size needs to be an integer greater than 0, but "%s" is not.', |
|
285 | 1 |
$indentSize
|
286 |
));
|
|
287 |
}
|
|
288 |
|
|
289 |
try { |
|
290 | 1 |
$indent = Normalizer\Format\Indent::fromSizeAndStyle( |
291 | 1 |
(int) $indentSize, |
292 |
$indentStyle
|
|
293 |
);
|
|
294 | 1 |
} catch (Normalizer\Exception\InvalidIndentSizeException $exception) { |
295 |
throw new \RuntimeException(\sprintf( |
|
296 |
'Indent size needs to be an integer greater than %d, but "%s" is not.', |
|
297 |
$exception->minimumSize(), |
|
298 |
$exception->size() |
|
299 |
));
|
|
300 | 1 |
} catch (Normalizer\Exception\InvalidIndentStyleException $exception) { |
301 | 1 |
throw new \RuntimeException(\sprintf( |
302 |
'Indent style needs to be one of "%s", but "%s" is not.', |
|
303 | 1 |
\implode('", "', \array_keys(self::$indentStyles)), |
304 | 1 |
$indentStyle
|
305 |
));
|
|
306 |
}
|
|
307 |
|
|
308 | 1 |
return $indent; |
309 |
}
|
|
310 |
|
|
311 |
private static function showValidationErrors(IO\IOInterface $io, string ...$errors): void |
|
312 |
{
|
|
313 |
foreach ($errors as $error) { |
|
314 |
$io->writeError(\sprintf( |
|
315 |
'<error>- %s</error>', |
|
316 |
$error
|
|
317 |
));
|
|
318 |
}
|
|
319 |
|
|
320 |
$io->writeError('<warning>See https://getcomposer.org/doc/04-schema.md for details on the schema</warning>'); |
|
321 |
}
|
|
322 |
|
|
323 | 1 |
private static function formatDiff(string $diff): string |
324 |
{
|
|
325 | 1 |
$lines = \explode( |
326 |
"\n", |
|
327 | 1 |
$diff
|
328 |
);
|
|
329 |
|
|
330 |
$formatted = \array_map(static function (string $line): string { |
|
331 | 1 |
$replaced = \preg_replace( |
332 |
[
|
|
333 |
'/^(\+.*)$/', |
|
334 |
'/^(-.*)$/', |
|
335 |
],
|
|
336 |
[
|
|
337 |
'<fg=green>$1</>', |
|
338 |
'<fg=red>$1</>', |
|
339 |
],
|
|
340 | 1 |
$line
|
341 |
);
|
|
342 |
|
|
343 | 1 |
if (!\is_string($replaced)) { |
344 |
throw Exception\ShouldNotHappen::create(); |
|
345 |
}
|
|
346 |
|
|
347 | 1 |
return $replaced; |
348 | 1 |
}, $lines); |
349 |
|
|
350 | 1 |
return \implode( |
351 |
"\n", |
|
352 | 1 |
$formatted
|
353 |
);
|
|
354 |
}
|
|
355 |
|
|
356 |
/**
|
|
357 |
* @see https://getcomposer.org/doc/03-cli.md#update
|
|
358 |
*
|
|
359 |
* @throws \Exception
|
|
360 |
*/
|
|
361 | 1 |
private static function updateLockerInWorkingDirectory( |
362 |
Console\Application $application, |
|
363 |
Console\Output\OutputInterface $output, |
|
364 |
string $workingDirectory |
|
365 |
): int { |
|
366 | 1 |
return $application->run( |
367 | 1 |
new Console\Input\ArrayInput([ |
368 | 1 |
'command' => 'update', |
369 |
'--ignore-platform-reqs' => true, |
|
370 |
'--lock' => true, |
|
371 |
'--no-autoloader' => true, |
|
372 |
'--no-plugins' => true, |
|
373 |
'--no-scripts' => true, |
|
374 | 1 |
'--working-dir' => $workingDirectory, |
375 |
]),
|
|
376 |
$output
|
|
377 |
);
|
|
378 |
}
|
|
379 |
}
|
Read our documentation on viewing source code .