Skip to content

Commit e58cbda

Browse files
Add generate:flex-endpoint command
1 parent a2ebf6f commit e58cbda

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed

run

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require __DIR__.'/vendor/autoload_runtime.php';
55

66
use App\DiffRecipeVersionsCommand;
7+
use App\GenerateFlexEndpointCommand;
78
use App\GenerateFlexIndexCommand;
89
use App\LintManifestsCommand;
910
use App\LintPackagesCommand;
@@ -16,6 +17,7 @@ return function() {
1617
$app = new Application();
1718

1819
$app->add(new DiffRecipeVersionsCommand());
20+
$app->add(new GenerateFlexEndpointCommand());
1921
$app->add(new GenerateFlexIndexCommand());
2022
$app->add(new LintManifestsCommand());
2123
$app->add(new LintPackagesCommand());
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php
2+
3+
/*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
namespace App;
11+
12+
use Symfony\Component\Console\Attribute\AsCommand;
13+
use Symfony\Component\Console\Command\Command;
14+
use Symfony\Component\Console\Input\InputArgument;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
use Symfony\Component\HttpClient\HttpClient;
18+
19+
#[AsCommand(name: 'generate:flex-endpoint', description: 'Generates the json files required by Flex')]
20+
class GenerateFlexEndpointCommand extends Command
21+
{
22+
protected function configure(): void
23+
{
24+
$this
25+
->addArgument('repository', InputArgument::REQUIRED, 'The name of the GitHub repository')
26+
->addArgument('source_branch', InputArgument::REQUIRED, 'The source branch of recipes')
27+
->addArgument('flex_branch', InputArgument::REQUIRED, 'The branch of the target Flex endpoint')
28+
->addArgument('output_directory', InputArgument::REQUIRED, 'The directory where generated files should be stored')
29+
->addOption('contrib')
30+
;
31+
}
32+
33+
protected function execute(InputInterface $input, OutputInterface $output): int
34+
{
35+
$repository = $input->getArgument('repository');
36+
$sourceBranch = $input->getArgument('source_branch');
37+
$flexBranch = $input->getArgument('flex_branch');
38+
$outputDir = $input->getArgument('output_directory');
39+
$contrib = $input->getOption('contrib');
40+
41+
$aliases = $recipes = [];
42+
43+
// stdin usually generated by `git ls-tree HEAD */*/*`
44+
45+
while (false !== $line = fgets(STDIN)) {
46+
[$tree, $package] = explode("\t", trim($line));
47+
[,, $tree] = explode(' ', $tree);
48+
49+
if (!file_exists($package.'/manifest.json')) {
50+
continue;
51+
}
52+
53+
$manifest = json_decode(file_get_contents($package.'/manifest.json'), true);
54+
$version = substr($package, 1 + strrpos($package, '/'));
55+
$package = substr($package, 0, -1 - \strlen($version));
56+
57+
$this->generatePackageJson($repository, $sourceBranch, $contrib, $package, $version, $manifest, $tree, $outputDir);
58+
59+
foreach ($manifest['aliases'] ?? [] as $alias) {
60+
$aliases[$alias] = $package;
61+
$aliases[str_replace('-', '', $alias)] = $package;
62+
}
63+
64+
if (0 === strpos($package, 'symfony/') && '-pack' !== substr($package, -5)) {
65+
$alias = substr($package, 8);
66+
$aliases[$alias] = $package;
67+
$aliases[str_replace('-', '', $alias)] = $package;
68+
}
69+
70+
$recipes[$package][] = $version;
71+
usort($recipes[$package], 'strnatcmp');
72+
}
73+
74+
uksort($aliases, 'strnatcmp');
75+
uksort($recipes, 'strnatcmp');
76+
77+
file_put_contents($outputDir.'/index.json', json_encode([
78+
'aliases' => $aliases,
79+
'recipes' => $recipes,
80+
'versions' => $contrib ? [] : HttpClient::create()->request('GET', 'https://flex.symfony.com/versions.json')->toArray(),
81+
'git_url' => sprintf('https://github.com/%s.git', $repository),
82+
'tree_template' => sprintf('https://github.com/%s/tree/{tree}/{package}/{version}', $repository),
83+
'recipe_template' => sprintf('https://raw.githubusercontent.com/%s/%s/{package_dotted}.{version}.json', $repository, $flexBranch),
84+
'is_contrib' => $contrib,
85+
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
86+
87+
return 0;
88+
}
89+
90+
private function generatePackageJson(string $repository, string $sourceBranch, bool $contrib, string $package, string $version, array $manifest, string $tree, string $outputDir)
91+
{
92+
$files = [];
93+
$it = new \RecursiveDirectoryIterator($package.'/'.$version);
94+
$it->setFlags($it::SKIP_DOTS | $it::FOLLOW_SYMLINKS);
95+
96+
foreach (new \RecursiveIteratorIterator($it) as $path => $file) {
97+
$file = substr($path, 1 + \strlen($package.'/'.$version));
98+
if (is_dir($path) || \in_array($file, ['manifest.json', 'post-install.txt', 'Makefile'], true)) {
99+
continue;
100+
}
101+
$contents = file_get_contents($path);
102+
$isUtf8 = preg_match('//u', $contents);
103+
$files[$file] = [
104+
'contents' => $isUtf8 ? $contents : base64_encode($contents),
105+
'executable' => is_executable($path),
106+
'encoding' => $isUtf8 ? '' : 'base64',
107+
];
108+
}
109+
110+
file_put_contents(sprintf('%s/%s.%s.json', $outputDir, str_replace('/', '.', $package), $version), json_encode([
111+
'locks' => [
112+
$package => [
113+
'version' => $version,
114+
'recipe' => [
115+
'repo' => $version,
116+
'branch' => $sourceBranch,
117+
'version' => $version,
118+
'ref' => $tree,
119+
],
120+
],
121+
],
122+
'manifests' => [
123+
$package => [
124+
'repository' => sprintf('github.com/%s', $repository),
125+
'package' => $package,
126+
'version' => $version,
127+
'manifest' => $manifest,
128+
'files' => $files,
129+
'origin' => sprintf('%s:%[email protected]/%s:%s', $package, $version, $repository, $sourceBranch),
130+
'not_installable' => false,
131+
'is_contrib' => $contrib,
132+
],
133+
],
134+
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
135+
}
136+
}

0 commit comments

Comments
 (0)