Skip to content

Commit a8d5c94

Browse files
committed
Command to inspect a recipe
1 parent 0b6ec56 commit a8d5c94

File tree

1 file changed

+162
-18
lines changed

1 file changed

+162
-18
lines changed

src/Command/RecipesCommand.php

Lines changed: 162 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Composer\Command\BaseCommand;
1515
use Composer\Factory;
16+
use Symfony\Component\Console\Input\InputArgument;
1617
use Symfony\Component\Console\Input\InputInterface;
1718
use Symfony\Component\Console\Output\OutputInterface;
1819
use Symfony\Flex\InformationOperation;
@@ -24,30 +25,42 @@ class RecipesCommand extends BaseCommand
2425
/** @var \Symfony\Flex\Flex */
2526
private $flex;
2627

28+
/** @var Lock */
29+
private $symfonyLock;
30+
2731
public function __construct(/* cannot be type-hinted */ $flex)
2832
{
2933
$this->flex = $flex;
34+
$this->symfonyLock = new Lock(getenv('SYMFONY_LOCKFILE') ?: str_replace('composer.json', 'symfony.lock', Factory::getComposerFile()));
3035

3136
parent::__construct();
3237
}
3338

3439
protected function configure()
3540
{
36-
$this->setName('symfony:recipes:show')
37-
->setAliases(['symfony:recipes'])
41+
$this->setName('symfony:recipes')
3842
->setDescription('Shows information about all available recipes.')
43+
->setDefinition([
44+
new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect, if not provided all packages are.'),
45+
])
3946
;
4047
}
4148

4249
protected function execute(InputInterface $input, OutputInterface $output)
4350
{
44-
$locker = $this->getComposer()->getLocker();
45-
$lockData = $locker->getLockData();
51+
$installedRepo = $this->getComposer()->getRepositoryManager()->getLocalRepository();
4652

47-
// Merge all packages installed
48-
$packages = array_merge($lockData['packages'], $lockData['packages-dev']);
53+
// Inspect one or all packages
54+
$package = $input->getArgument('package');
55+
if (null !== $package) {
56+
$packages = [0 => ['name' => strtolower($package)]];
57+
} else {
58+
$locker = $this->getComposer()->getLocker();
59+
$lockData = $locker->getLockData();
4960

50-
$installedRepo = $this->getComposer()->getRepositoryManager()->getLocalRepository();
61+
// Merge all packages installed
62+
$packages = array_merge($lockData['packages'], $lockData['packages-dev']);
63+
}
5164

5265
$operations = [];
5366
foreach ($packages as $key => $value) {
@@ -63,12 +76,21 @@ protected function execute(InputInterface $input, OutputInterface $output)
6376
$recipes = $this->flex->fetchRecipes($operations);
6477
ksort($recipes);
6578

66-
if (\count($recipes) <= 0) {
79+
$nbRecipe = \count($recipes);
80+
if ($nbRecipe <= 0) {
6781
$this->getIO()->writeError('<error>No recipe found</error>');
6882

6983
return 1;
7084
}
7185

86+
// Display the information about a specific recipe
87+
if (1 === $nbRecipe) {
88+
$this->displayPackageInformation(current($recipes));
89+
90+
return 0;
91+
}
92+
93+
// display a resume of all packages
7294
$write = [
7395
'',
7496
'<bg=blue;fg=white> </>',
@@ -77,27 +99,23 @@ protected function execute(InputInterface $input, OutputInterface $output)
7799
'',
78100
];
79101

80-
$symfonyLock = new Lock(getenv('SYMFONY_LOCKFILE') ?: str_replace('composer.json', 'symfony.lock', Factory::getComposerFile()));
81-
82102
/** @var Recipe $recipe */
83103
foreach ($recipes as $name => $recipe) {
84-
$lockRef = $symfonyLock->get($name)['recipe']['ref'] ?? null;
104+
$lockRef = $this->symfonyLock->get($name)['recipe']['ref'] ?? null;
85105

86106
$additional = '';
87-
if ($recipe->isAuto()) {
88-
$additional = '<comment>(auto recipe)</comment>';
89-
} elseif (null === $lockRef && null !== $recipe->getRef()) {
90-
$additional = '<comment>(new recipe available)</comment>';
107+
if (null === $lockRef && null !== $recipe->getRef()) {
108+
$additional = '<comment>(recipe not installed)</comment>';
91109
} elseif ($recipe->getRef() !== $lockRef) {
92110
$additional = '<comment>(update available)</comment>';
93111
}
94112
$write[] = sprintf(' * %s %s', $name, $additional);
95113
}
96114

97115
// TODO : Uncomment the lines following the implemented features
98-
//$write[] = '';
99-
//$write[] = '<fg=blue>Run</>:';
100-
//$write[] = ' * composer symfony:recipes vendor/package to see details about a recipe.';
116+
$write[] = '';
117+
$write[] = '<fg=blue>Run</>:';
118+
$write[] = ' * composer symfony:recipes vendor/package to see details about a recipe.';
101119
//$write[] = ' * composer symfony:recipes:update vendor/package to update that recipe.';
102120
//$write[] = ' * composer symfony:recipes:blame to see a tree of all of the installed/updated files.';
103121
$write[] = '';
@@ -106,4 +124,130 @@ protected function execute(InputInterface $input, OutputInterface $output)
106124

107125
return 0;
108126
}
127+
128+
private function displayPackageInformation(Recipe $recipe)
129+
{
130+
$recipeLock = $this->symfonyLock->get($recipe->getName());
131+
132+
$lockRef = $recipeLock['recipe']['ref'] ?? null;
133+
$lockFiles = $recipeLock['files'] ?? null;
134+
135+
$status = '<comment>up to date</comment>';
136+
if ($recipe->isAuto()) {
137+
$status = '<comment>auto-generated recipe</comment>';
138+
} elseif (null === $lockRef && null !== $recipe->getRef()) {
139+
$status = '<comment>recipe not installed</comment>';
140+
} elseif ($recipe->getRef() !== $lockRef) {
141+
$status = '<comment>upate available</comment>';
142+
}
143+
144+
$io = $this->getIO();
145+
$io->write('<info>name</info> : '.$recipe->getName());
146+
$io->write('<info>version</info> : '.$recipeLock['version']);
147+
$io->write('<info>repo</info> : '.$recipeLock['recipe']['repo']);
148+
$io->write('<info>sha</info> : '.$lockRef);
149+
$io->write('<info>status</info> : '.$status);
150+
151+
if (null !== $lockFiles) {
152+
$io->write('<info>files</info> : ');
153+
$io->write('');
154+
155+
$tree = $this->generateFilesTree($lockFiles);
156+
157+
$this->displayFilesTree($tree);
158+
}
159+
160+
// TODO : Uncomment the line when the feature will be implemented
161+
//$io->write([
162+
// '',
163+
// 'Update this recipe by running composer symfony:recipe:update symfony/framework-bundle',
164+
//]);
165+
}
166+
167+
private function generateFilesTree(array $files): array
168+
{
169+
$tree = [];
170+
foreach ($files as $file) {
171+
$path = explode('/', $file);
172+
173+
$tree = array_merge_recursive($tree, $this->addNode($path));
174+
}
175+
176+
return $tree;
177+
}
178+
179+
private function addNode(array $node): array
180+
{
181+
$current = array_shift($node);
182+
183+
$subTree = [];
184+
if (null !== $current) {
185+
$subTree[$current] = $this->addNode($node);
186+
}
187+
188+
return $subTree;
189+
}
190+
191+
/**
192+
* TODO : We do not display file modification information with Configurator like ComposerScripts, Container, DockerComposer, Dockerfile, Env, Gitignore and Makefile.
193+
*/
194+
private function displayFilesTree(array $tree)
195+
{
196+
$endKey = array_key_last($tree);
197+
foreach ($tree as $dir => $files) {
198+
$treeBar = '';
199+
$total = \count($files);
200+
if (0 === $total || $endKey === $dir) {
201+
$treeBar = '';
202+
}
203+
204+
$info = sprintf(
205+
'%s──%s',
206+
$treeBar,
207+
$dir
208+
);
209+
$this->writeTreeLine($info);
210+
211+
$treeBar = str_replace('', ' ', $treeBar);
212+
213+
$this->displayTree($files, $treeBar);
214+
}
215+
}
216+
217+
private function displayTree(array $tree, $previousTreeBar = '', $level = 1)
218+
{
219+
$previousTreeBar = str_replace('', '', $previousTreeBar);
220+
$treeBar = $previousTreeBar.'';
221+
222+
$i = 0;
223+
$total = \count($tree);
224+
225+
foreach ($tree as $dir => $files) {
226+
++$i;
227+
if ($i === $total) {
228+
$treeBar = $previousTreeBar.'';
229+
}
230+
231+
$info = sprintf(
232+
'%s──%s',
233+
$treeBar,
234+
$dir
235+
);
236+
$this->writeTreeLine($info);
237+
238+
$treeBar = str_replace('', ' ', $treeBar);
239+
240+
$this->displayTree($files, $treeBar, $level + 1);
241+
}
242+
}
243+
244+
private function writeTreeLine($line)
245+
{
246+
$io = $this->getIO();
247+
if (!$io->isDecorated()) {
248+
$line = str_replace(['', '', '──', ''], ['`-', '|-', '-', '|'], $line);
249+
}
250+
251+
$io->write($line);
252+
}
109253
}

0 commit comments

Comments
 (0)