Skip to content

Commit c408c0a

Browse files
authored
Composer generation: support for arbitrary folder structure of repositories (magento#720)
1 parent e880b2b commit c408c0a

File tree

22 files changed

+391
-309
lines changed

22 files changed

+391
-309
lines changed

src/Command/Dev/UpdateComposer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
117117
$composer = $this->composerGenerator->generate($gitOptions['repositories']);
118118

119119
if (!empty($gitOptions['clear_magento_module_requirements'])) {
120-
$this->clearModuleRequirements->generate($gitOptions['repositories']);
121-
$composer['scripts']['install-from-git'][] = 'php ' . ClearModuleRequirements::SCRIPT_PATH;
120+
$clearRequirementsScript = $this->clearModuleRequirements->generate();
121+
$composer['scripts']['install-from-git'][] = 'php ' . $clearRequirementsScript;
122122
}
123123

124124
$this->file->filePutContents(

src/Command/Dev/UpdateComposer/ClearModuleRequirements.php

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -44,55 +44,14 @@ public function __construct(
4444
/**
4545
* Generates script for clearing module requirements that run after composer install.
4646
*
47-
* @param array $repos
48-
* @return void
47+
* @return string script name
48+
*
49+
* @throws \Magento\MagentoCloud\Filesystem\FileSystemException
4950
*/
50-
public function generate(array $repos)
51+
public function generate()
5152
{
5253
$rootDirectory = $this->directoryList->getMagentoRoot();
53-
$clearModulesFilePath = $rootDirectory . '/' . self::SCRIPT_PATH;
54-
$stringRepos = var_export($repos, true);
55-
$singlePackageType = ComposerGenerator::REPO_TYPE_SINGLE_PACKAGE;
56-
57-
$clearModulesCode = <<<CODE
58-
<?php
59-
\$repos = {$stringRepos};
60-
61-
function clearRequirements(\$dir) {
62-
if (!file_exists(\$dir . '/composer.json')) {
63-
return;
64-
}
65-
66-
\$composerJson = json_decode(file_get_contents(\$dir . '/composer.json'), true);
67-
68-
foreach (\$composerJson['require'] as \$requireName => \$requireVersion) {
69-
if (preg_match('{^(magento\/|elasticsearch\/)}i', \$requireName)) {
70-
unset(\$composerJson['require'][\$requireName]);
71-
}
72-
}
73-
74-
file_put_contents(
75-
\$dir . '/composer.json',
76-
json_encode(\$composerJson, JSON_PRETTY_PRINT)
77-
);
78-
}
79-
80-
foreach (\$repos as \$repoName => \$repoOptions) {
81-
\$repoDir = __DIR__ .'/' . \$repoName;
82-
83-
if (isset(\$repoOptions['type']) && \$repoOptions['type'] == '{$singlePackageType}') {
84-
clearRequirements(\$repoDir);
85-
continue;
86-
}
87-
88-
foreach (glob(\$repoDir . '/app/code/Magento/*') as \$moduleDir) {
89-
clearRequirements(\$moduleDir);
90-
}
91-
}
92-
93-
CODE;
94-
$this->file->filePutContents($clearModulesFilePath, $clearModulesCode);
95-
54+
$this->file->copy(__DIR__ . '/' . self::SCRIPT_PATH . '.tpl', $rootDirectory . '/' . self::SCRIPT_PATH);
9655
$gitIgnore = $this->file->fileGetContents($rootDirectory . '/.gitignore');
9756
if (strpos($gitIgnore ?? '', self::SCRIPT_PATH) === false) {
9857
$this->file->filePutContents(
@@ -101,5 +60,6 @@ function clearRequirements(\$dir) {
10160
FILE_APPEND
10261
);
10362
}
63+
return self::SCRIPT_PATH;
10464
}
10565
}

src/Command/Dev/UpdateComposer/ComposerGenerator.php

Lines changed: 61 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,12 @@
1111
use Magento\MagentoCloud\Filesystem\Driver\File;
1212
use Magento\MagentoCloud\Filesystem\FileSystemException;
1313
use Magento\MagentoCloud\Package\MagentoVersion;
14-
use Magento\MagentoCloud\Package\UndefinedPackageException;
1514

1615
/**
1716
* Generates composer.json data for installation from git.
1817
*/
1918
class ComposerGenerator
2019
{
21-
public const REPO_TYPE_SINGLE_PACKAGE = 'single-package';
22-
23-
/**
24-
* Type for packages with modules in the root directory such as Inventory
25-
*/
26-
public const REPO_TYPE_FLAT_STRUCTURE = 'flat-structure';
27-
2820
/**
2921
* @var DirectoryList
3022
*/
@@ -40,29 +32,35 @@ class ComposerGenerator
4032
*/
4133
private $file;
4234

35+
/**
36+
* @var string
37+
*/
38+
private $excludeRepoPathsPattern;
39+
4340
/**
4441
* @param DirectoryList $directoryList
4542
* @param MagentoVersion $magentoVersion
4643
* @param File $file
44+
* @param string $excludeRepoPathsPattern
4745
*/
4846
public function __construct(
4947
DirectoryList $directoryList,
5048
MagentoVersion $magentoVersion,
51-
File $file
49+
File $file,
50+
$excludeRepoPathsPattern = '/^((?!test|Test|dev).)*$/'
5251
) {
5352
$this->directoryList = $directoryList;
5453
$this->magentoVersion = $magentoVersion;
5554
$this->file = $file;
55+
$this->excludeRepoPathsPattern = $excludeRepoPathsPattern;
5656
}
5757

5858
/**
5959
* Generates composer.json data for installation from git.
6060
*
6161
* @param array $repoOptions
6262
* @return array
63-
* @throws UndefinedPackageException
6463
* @throws FileSystemException
65-
*
6664
* @codeCoverageIgnore
6765
*/
6866
public function generate(array $repoOptions): array
@@ -78,26 +76,38 @@ public function generate(array $repoOptions): array
7876
$composer['require'] += ['magento/ece-tools' => '2002.0.*'];
7977
}
8078

81-
foreach (array_keys($repoOptions) as $repoName) {
82-
$repoComposerJsonPath = $this->directoryList->getMagentoRoot() . '/' . $repoName . '/composer.json';
83-
if (!$this->file->isExists($repoComposerJsonPath)) {
84-
continue;
79+
$preparePackagesScripts = [];
80+
foreach (array_keys($repoOptions) as $repoDir) {
81+
$baseRepoFolder = $this->directoryList->getMagentoRoot() . '/' . $repoDir;
82+
83+
$dirComposerJson = $baseRepoFolder . '/composer.json';
84+
if ($this->file->isExists($dirComposerJson)) {
85+
$dirPackageInfo = json_decode($this->file->fileGetContents($dirComposerJson), true);
86+
if (isset($dirPackageInfo['type']) && $dirPackageInfo['type'] == 'project') {
87+
$composer['require'] = array_merge($composer['require'], $dirPackageInfo['require']);
88+
}
8589
}
8690

87-
$repoComposer = $this->file->fileGetContents($repoComposerJsonPath);
88-
$composer['require'] = array_merge(
89-
$composer['require'],
90-
json_decode($repoComposer, true)['require']
91-
);
92-
}
93-
94-
foreach (array_keys($composer['require']) as $packageName) {
95-
if (preg_match('/magento\/framework|magento\/module/', $packageName)) {
96-
$composer['require'][$packageName] = '*';
91+
$repoPackages = $this->findPackages($baseRepoFolder);
92+
foreach ($repoPackages as $packageName => $packagePath) {
93+
$composer['repositories'][$packageName] = [
94+
'type' => 'path',
95+
'url' => $repoDir . '/' . $packagePath,
96+
'options' => [
97+
'symlink' => false,
98+
]
99+
];
100+
$composer['require'][$packageName] = '*@dev';
97101
}
102+
$excludeRepoStr = empty($repoPackages) ? '' : "--exclude='" . join("' --exclude='", $repoPackages) . "' ";
103+
$preparePackagesScripts[] = sprintf(
104+
"rsync -azhm --stats $excludeRepoStr--exclude='dev/tests' --exclude='.git' " .
105+
"--exclude='composer.json' --exclude='composer.lock' ./%s/ ./",
106+
$repoDir
107+
);
98108
}
99-
100-
$composer = $this->addModules($repoOptions, $composer);
109+
$composer['scripts']['prepare-packages'] = $preparePackagesScripts;
110+
$composer['scripts']['post-install-cmd'] = ['@prepare-packages'];
101111

102112
return $composer;
103113
}
@@ -112,13 +122,14 @@ public function getInstallFromGitScripts(array $repoOptions): array
112122
$installFromGitScripts[] = 'rm -rf ' . implode(' ', array_keys($repoOptions));
113123

114124
foreach ($repoOptions as $repoName => $gitOption) {
115-
$gitCloneCommand = 'git clone -b %s --single-branch --depth 1 %s %s';
116-
125+
$gitRef = $gitOption['ref'] ?? $gitOption['branch'];
117126
$installFromGitScripts[] = sprintf(
118-
$gitCloneCommand,
119-
$gitOption['branch'],
127+
'git clone %s "%s" && git --git-dir="%s/.git" --work-tree="%s" checkout %s',
120128
$gitOption['repo'],
121-
$repoName
129+
$repoName,
130+
$repoName,
131+
$repoName,
132+
$gitRef
122133
);
123134
}
124135

@@ -130,26 +141,10 @@ public function getInstallFromGitScripts(array $repoOptions): array
130141
*
131142
* @param array $repoOptions
132143
* @return array
133-
* @throws UndefinedPackageException
134144
*/
135145
private function getBaseComposer(array $repoOptions): array
136146
{
137147
$installFromGitScripts = $this->getInstallFromGitScripts($repoOptions);
138-
139-
$preparePackagesScripts = [];
140-
141-
foreach ($repoOptions as $repoName => $gitOption) {
142-
if ($this->isSinglePackage($gitOption) || $this->isFlatStructurePackage($gitOption)) {
143-
continue;
144-
}
145-
146-
$preparePackagesScripts[] = sprintf(
147-
"rsync -azh --stats --exclude='app/code/Magento/' --exclude='app/i18n/' --exclude='app/design/' "
148-
. "--exclude='dev/tests' --exclude='lib/internal/Magento' --exclude='.git' ./%s/ ./",
149-
$repoName
150-
);
151-
}
152-
153148
$composer = [
154149
'name' => 'magento/cloud-dev',
155150
'description' => 'eCommerce Platform for Growth',
@@ -162,16 +157,6 @@ private function getBaseComposer(array $repoOptions): array
162157
'ce/bin/magento',
163158
],
164159
'repositories' => [
165-
'magento/framework' => [
166-
'type' => 'path',
167-
'url' => './ce/lib/internal/Magento/Framework/',
168-
'transport-options' => [
169-
'symlink' => false,
170-
],
171-
'options' => [
172-
'symlink' => false,
173-
],
174-
],
175160
],
176161
'require' => [
177162
],
@@ -192,111 +177,44 @@ private function getBaseComposer(array $repoOptions): array
192177
],
193178
'scripts' => [
194179
'install-from-git' => $installFromGitScripts,
195-
'prepare-packages' => $preparePackagesScripts,
196180
'pre-install-cmd' => [
197181
'@install-from-git',
198182
],
199183
'pre-update-cmd' => [
200184
'@install-from-git',
201185
],
202-
'post-install-cmd' => [
203-
'@prepare-packages',
204-
],
205186
],
206187
];
207188

208189
return $composer;
209190
}
210191

211192
/**
212-
* Adds modules and repositories to composer.json.
193+
* Find Composer packages in the folder (recursively)
213194
*
214-
* @param array $repoOptions
215-
* @param array $composer
195+
* @param string $path
216196
* @return array
217197
* @throws FileSystemException
218-
*
219-
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
220-
*
221-
* @codeCoverageIgnore
222198
*/
223-
private function addModules(array $repoOptions, array $composer): array
199+
private function findPackages(string $path)
224200
{
225-
foreach ($repoOptions as $repoName => $gitOption) {
226-
$baseRepoFolder = $this->directoryList->getMagentoRoot() . '/' . $repoName;
227-
if ($this->isSinglePackage($gitOption)) {
228-
$this->addModule($baseRepoFolder, $composer, '*');
229-
continue;
230-
}
231-
232-
if ($this->isFlatStructurePackage($gitOption)) {
233-
foreach (glob($baseRepoFolder . '/*') as $dir) {
234-
$this->addModule($dir, $composer);
235-
}
236-
continue;
237-
}
238-
239-
foreach (glob($baseRepoFolder . '/app/code/Magento/*') as $dir) {
240-
$this->addModule($dir, $composer);
241-
}
242-
foreach (glob($baseRepoFolder . '/app/design/*/Magento/*/') as $dir) {
243-
$this->addModule($dir, $composer);
244-
}
245-
foreach (glob($baseRepoFolder . '/app/design/*/Magento/*/') as $dir) {
246-
$this->addModule($dir, $composer);
247-
}
248-
if ($this->file->isDirectory($baseRepoFolder . '/lib/internal/Magento/Framework/')) {
249-
foreach (glob($baseRepoFolder . '/lib/internal/Magento/Framework/*') as $dir) {
250-
$this->addModule($dir, $composer);
251-
}
201+
$path = rtrim($path, '\\/');
202+
$packageTypes = ['magento2-module', 'magento2-theme', 'magento2-language', 'magento2-library'];
203+
$pathLength = strlen($path . '/');
204+
205+
$dirIterator = $this->file->getRecursiveFileIterator(
206+
$path,
207+
'/composer.json$/',
208+
$this->excludeRepoPathsPattern
209+
);
210+
$packages = [];
211+
foreach ($dirIterator as $currentFileInfo) {
212+
$packageInfo = json_decode($this->file->fileGetContents($currentFileInfo->getPathName()), true);
213+
if (isset($packageInfo['type']) && in_array($packageInfo['type'], $packageTypes)) {
214+
$packages[$packageInfo['name']] = substr($currentFileInfo->getPath(), $pathLength);
252215
}
253216
}
254217

255-
return $composer;
256-
}
257-
258-
/**
259-
* Add single module to composer json
260-
*
261-
* @param string $dir
262-
* @param array $composer
263-
* @param string|null $version
264-
* @throws FileSystemException
265-
*/
266-
private function addModule(string $dir, array &$composer, string $version = null): void
267-
{
268-
if (!$this->file->isExists($dir . '/composer.json')) {
269-
return;
270-
}
271-
272-
$dirComposer = json_decode($this->file->fileGetContents($dir . '/composer.json'), true);
273-
$composer['repositories'][$dirComposer['name']] = [
274-
'type' => 'path',
275-
'url' => ltrim(str_replace($this->directoryList->getMagentoRoot(), '', $dir), '/'),
276-
'options' => [
277-
'symlink' => false,
278-
],
279-
];
280-
$composer['require'][$dirComposer['name']] = $version ?? $dirComposer['version'] ?? '*';
281-
}
282-
283-
/**
284-
* @param array $repoOptions
285-
* @return bool
286-
*/
287-
private function isSinglePackage(array $repoOptions): bool
288-
{
289-
return isset($repoOptions['type']) && $repoOptions['type'] === self::REPO_TYPE_SINGLE_PACKAGE;
290-
}
291-
292-
/**
293-
* Checks that package has option type and it equal to @see ComposerGenerator::REPO_TYPE_FLAT_STRUCTURE
294-
*
295-
* @param array $repoOptions
296-
* @return bool
297-
*/
298-
private function isFlatStructurePackage(array $repoOptions): bool
299-
{
300-
return isset($repoOptions['type']) && $repoOptions['type'] === self::REPO_TYPE_FLAT_STRUCTURE;
218+
return $packages;
301219
}
302220
}

0 commit comments

Comments
 (0)