1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the puli/composer-plugin package. |
5
|
|
|
* |
6
|
|
|
* (c) Bernhard Schussek <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Puli\ComposerPlugin; |
13
|
|
|
|
14
|
|
|
use Composer\Composer; |
15
|
|
|
use Composer\Config; |
16
|
|
|
use Composer\IO\IOInterface; |
17
|
|
|
use Composer\Json\JsonFile; |
18
|
|
|
use Composer\Package\AliasPackage; |
19
|
|
|
use Composer\Package\PackageInterface; |
20
|
|
|
use Composer\Script\Event; |
21
|
|
|
use Composer\Util\RemoteFilesystem; |
22
|
|
|
use Exception; |
23
|
|
|
use RuntimeException; |
24
|
|
|
use Symfony\Component\Filesystem\Filesystem; |
25
|
|
|
use Webmozart\PathUtil\Path; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Implementation of the Puli plugin. |
29
|
|
|
* |
30
|
|
|
* This class is separate from the main {@link PuliPlugin} class so that it can |
31
|
|
|
* be loaded lazily after updating the sources of this package in the project |
32
|
|
|
* that requires the package. |
33
|
|
|
* |
34
|
|
|
* @author Bernhard Schussek <[email protected]> |
35
|
|
|
*/ |
36
|
|
|
class PuliPluginImpl |
37
|
|
|
{ |
38
|
|
|
/** |
39
|
|
|
* The version of the Puli plugin. |
40
|
|
|
*/ |
41
|
|
|
const VERSION = '@package_version@'; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* The minimum version of the Puli CLI. |
45
|
|
|
*/ |
46
|
|
|
const MIN_CLI_VERSION = '1.0.0-beta10'; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* The maximum version of the Puli CLI. |
50
|
|
|
*/ |
51
|
|
|
const MAX_CLI_VERSION = '1.999.99999'; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* The name of the installer. |
55
|
|
|
*/ |
56
|
|
|
const INSTALLER_NAME = 'composer'; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var Composer |
60
|
|
|
*/ |
61
|
|
|
private $composer; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var IOInterface |
65
|
|
|
*/ |
66
|
|
|
private $io; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var Config |
70
|
|
|
*/ |
71
|
|
|
private $config; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @var bool |
75
|
|
|
*/ |
76
|
|
|
private $isDev; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @var PuliRunner |
80
|
|
|
*/ |
81
|
|
|
private $puliRunner; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @var string |
85
|
|
|
*/ |
86
|
|
|
private $rootDir; |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* @var bool |
90
|
|
|
*/ |
91
|
|
|
private $runPreAutoloadDump = true; |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* @var bool |
95
|
|
|
*/ |
96
|
|
|
private $runPostAutoloadDump = true; |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @var bool |
100
|
|
|
*/ |
101
|
|
|
private $runPostInstall = true; |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* @var bool |
105
|
|
|
*/ |
106
|
|
|
private $initialized = false; |
107
|
|
|
|
108
|
42 |
|
public function __construct(Event $event, PuliRunner $puliRunner = null) |
109
|
|
|
{ |
110
|
42 |
|
$this->composer = $event->getComposer(); |
111
|
42 |
|
$this->io = $event->getIO(); |
112
|
42 |
|
$this->config = $this->composer->getConfig(); |
113
|
42 |
|
$this->isDev = $event->isDevMode(); |
114
|
42 |
|
$this->puliRunner = $puliRunner; |
115
|
42 |
|
$this->rootDir = Path::normalize(getcwd()); |
116
|
42 |
|
} |
117
|
|
|
|
118
|
3 |
|
public function preAutoloadDump() |
119
|
|
|
{ |
120
|
|
|
// This method is called multiple times. Run it only once. |
121
|
3 |
|
if (!$this->runPreAutoloadDump) { |
122
|
|
|
return; |
123
|
|
|
} |
124
|
|
|
|
125
|
3 |
|
$this->runPreAutoloadDump = false; |
126
|
|
|
|
127
|
3 |
|
$factoryClass = $this->getConfigKeyFromJsonFile('factory.in.class'); |
128
|
3 |
|
$factoryFile = $this->getConfigKeyFromJsonFile('factory.in.file'); |
129
|
3 |
|
$factoryFile = Path::makeAbsolute($factoryFile, $this->rootDir); |
130
|
|
|
|
131
|
3 |
|
$autoload = $this->composer->getPackage()->getAutoload(); |
132
|
3 |
|
$autoload['classmap'][] = $factoryFile; |
133
|
|
|
|
134
|
3 |
|
$this->composer->getPackage()->setAutoload($autoload); |
135
|
|
|
|
136
|
3 |
|
if (!file_exists($factoryFile)) { |
137
|
3 |
|
$filesystem = new Filesystem(); |
138
|
|
|
// Let Composer find the factory class with a temporary stub |
139
|
|
|
|
140
|
3 |
|
$namespace = explode('\\', ltrim($factoryClass, '\\')); |
141
|
3 |
|
$className = array_pop($namespace); |
142
|
|
|
|
143
|
3 |
|
if (count($namespace)) { |
144
|
3 |
|
$stub = '<?php namespace '.implode('\\', $namespace).'; class '.$className.' {}'; |
145
|
|
|
} else { |
146
|
|
|
$stub = '<?php class '.$className.' {}'; |
147
|
|
|
} |
148
|
|
|
|
149
|
3 |
|
$filesystem->dumpFile($factoryFile, $stub); |
150
|
|
|
} |
151
|
3 |
|
} |
152
|
|
|
|
153
|
5 |
|
public function postAutoloadDump() |
154
|
|
|
{ |
155
|
5 |
|
if (!$this->initialized) { |
156
|
5 |
|
$this->initialize(); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
// This method is called multiple times. Run it only once. |
160
|
5 |
|
if (!$this->runPostAutoloadDump) { |
161
|
1 |
|
return; |
162
|
|
|
} |
163
|
|
|
|
164
|
5 |
|
$this->runPostAutoloadDump = false; |
165
|
|
|
|
166
|
|
|
try { |
167
|
5 |
|
$factoryClass = $this->getConfigKey('factory.in.class'); |
168
|
1 |
|
} catch (PuliRunnerException $e) { |
169
|
1 |
|
$this->printWarning('Could not load Puli configuration', $e); |
170
|
|
|
|
171
|
1 |
|
return; |
172
|
|
|
} |
173
|
|
|
|
174
|
4 |
|
$vendorDir = $this->config->get('vendor-dir'); |
175
|
|
|
|
176
|
|
|
// On TravisCI, $vendorDir is a relative path. Probably an old Composer |
177
|
|
|
// build or something. Usually, $vendorDir should be absolute already. |
178
|
4 |
|
$vendorDir = Path::makeAbsolute($vendorDir, $this->rootDir); |
179
|
|
|
|
180
|
4 |
|
$autoloadFile = $vendorDir.'/autoload.php'; |
181
|
4 |
|
$this->insertFactoryClassConstant($autoloadFile, $factoryClass); |
182
|
4 |
|
$this->setBootstrapFile($autoloadFile); |
183
|
4 |
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Updates the Puli repository after Composer installations/updates. |
187
|
|
|
*/ |
188
|
34 |
|
public function postInstall() |
189
|
|
|
{ |
190
|
34 |
|
if (!$this->initialized) { |
191
|
34 |
|
$this->initialize(); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
// This method is called multiple times. Run it only once. |
195
|
34 |
|
if (!$this->runPostInstall) { |
196
|
3 |
|
return; |
197
|
|
|
} |
198
|
|
|
|
199
|
32 |
|
$this->runPostInstall = false; |
200
|
|
|
|
201
|
32 |
|
$this->io->write('<info>Synchronizing Puli with Composer</info>'); |
202
|
|
|
|
203
|
32 |
|
$rootPackage = $this->composer->getPackage(); |
204
|
32 |
|
$composerPackages = $this->loadComposerPackages(); |
205
|
32 |
|
$prodPackageNames = $this->filterProdPackageNames($composerPackages, $rootPackage); |
206
|
32 |
|
$env = $this->isDev ? PuliPackage::ENV_DEV : PuliPackage::ENV_PROD; |
207
|
|
|
|
208
|
|
|
try { |
209
|
32 |
|
$puliPackages = $this->loadPuliPackages(); |
210
|
1 |
|
} catch (PuliRunnerException $e) { |
211
|
1 |
|
$this->printWarning('Could not load Puli packages', $e); |
212
|
|
|
|
213
|
1 |
|
return; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
// Don't remove non-existing packages in production environment |
217
|
|
|
// Removed packages could be dev dependencies (i.e. "require-dev" |
218
|
|
|
// of the root package or "require" of another dev dependency), and |
219
|
|
|
// we can't find out whether they are since Composer doesn't load them |
220
|
31 |
|
if (PuliPackage::ENV_PROD !== $env) { |
221
|
3 |
|
$this->removeRemovedPackages($composerPackages, $puliPackages); |
222
|
|
|
} |
223
|
|
|
|
224
|
31 |
|
$this->installNewPackages($composerPackages, $prodPackageNames, $puliPackages); |
225
|
|
|
|
226
|
|
|
// Don't print warnings for non-existing packages in production |
227
|
31 |
|
if (PuliPackage::ENV_PROD !== $env) { |
228
|
3 |
|
$this->checkForNotFoundErrors($puliPackages); |
229
|
|
|
} |
230
|
|
|
|
231
|
31 |
|
$this->checkForNotLoadableErrors($puliPackages); |
232
|
31 |
|
$this->adoptComposerName($puliPackages); |
233
|
31 |
|
$this->removePuliDir(); |
234
|
31 |
|
$this->buildPuli(); |
235
|
31 |
|
} |
236
|
|
|
|
237
|
39 |
|
private function initialize() |
238
|
|
|
{ |
239
|
39 |
|
$this->initialized = true; |
240
|
|
|
|
241
|
|
|
// Keep the manually set runner |
242
|
39 |
|
if (null === $this->puliRunner) { |
243
|
|
|
try { |
244
|
|
|
// Add Composer's bin directory in case the "puli" executable is |
245
|
|
|
// installed with Composer |
246
|
|
|
$this->puliRunner = new PuliRunner($this->config->get('bin-dir')); |
247
|
|
|
} catch (RuntimeException $e) { |
248
|
|
|
$this->printWarning('Plugin initialization failed', $e); |
249
|
|
|
$this->runPreAutoloadDump = false; |
250
|
|
|
$this->runPostAutoloadDump = false; |
251
|
|
|
$this->runPostInstall = false; |
252
|
|
|
} |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
// Use the runner to verify if Puli has the right version |
256
|
|
|
try { |
257
|
39 |
|
$this->verifyPuliVersion(); |
258
|
2 |
|
} catch (RuntimeException $e) { |
259
|
2 |
|
$this->printWarning('Version check failed', $e); |
260
|
2 |
|
$this->runPreAutoloadDump = false; |
261
|
2 |
|
$this->runPostAutoloadDump = false; |
262
|
2 |
|
$this->runPostInstall = false; |
263
|
|
|
} |
264
|
39 |
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* @param PackageInterface[] $composerPackages |
268
|
|
|
* @param bool[] $prodPackageNames |
269
|
|
|
* @param PuliPackage[] $puliPackages |
270
|
|
|
*/ |
271
|
31 |
|
private function installNewPackages(array $composerPackages, array $prodPackageNames, array &$puliPackages) |
272
|
|
|
{ |
273
|
31 |
|
$installationManager = $this->composer->getInstallationManager(); |
274
|
|
|
|
275
|
31 |
|
foreach ($composerPackages as $packageName => $package) { |
276
|
21 |
|
if ($package instanceof AliasPackage) { |
277
|
2 |
|
$package = $package->getAliasOf(); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
// We need to normalize the system-dependent paths returned by Composer |
281
|
21 |
|
$installPath = Path::normalize($installationManager->getInstallPath($package)); |
282
|
21 |
|
$env = isset($prodPackageNames[$packageName]) ? PuliPackage::ENV_PROD : PuliPackage::ENV_DEV; |
283
|
|
|
|
284
|
|
|
// Skip meta packages |
285
|
21 |
|
if ('' === $installPath) { |
286
|
1 |
|
continue; |
287
|
|
|
} |
288
|
|
|
|
289
|
20 |
|
if (isset($puliPackages[$packageName])) { |
290
|
15 |
|
$puliPackage = $puliPackages[$packageName]; |
291
|
|
|
|
292
|
|
|
// Only proceed if the install path or environment has changed |
293
|
15 |
|
if ($installPath === $puliPackage->getInstallPath() && $env === $puliPackage->getEnvironment()) { |
294
|
14 |
|
continue; |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
// Only remove packages installed by Composer |
298
|
6 |
|
if (self::INSTALLER_NAME === $puliPackage->getInstallerName()) { |
299
|
5 |
|
$this->io->write(sprintf( |
300
|
5 |
|
'Reinstalling <info>%s</info> (<comment>%s</comment>) in <comment>%s</comment>', |
301
|
|
|
$packageName, |
302
|
5 |
|
Path::makeRelative($installPath, $this->rootDir), |
303
|
|
|
$env |
304
|
|
|
)); |
305
|
|
|
|
306
|
|
|
try { |
307
|
5 |
|
$this->removePackage($packageName); |
308
|
1 |
|
} catch (PuliRunnerException $e) { |
309
|
1 |
|
$this->printPackageWarning('Could not remove package "%s" (at "%s")', $packageName, $installPath, $e); |
310
|
|
|
|
311
|
6 |
|
continue; |
312
|
|
|
} |
313
|
|
|
} |
314
|
|
|
} else { |
315
|
6 |
|
$this->io->write(sprintf( |
316
|
6 |
|
'Installing <info>%s</info> (<comment>%s</comment>) in <comment>%s</comment>', |
317
|
|
|
$packageName, |
318
|
6 |
|
Path::makeRelative($installPath, $this->rootDir), |
319
|
|
|
$env |
320
|
|
|
)); |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
try { |
324
|
11 |
|
$this->installPackage($installPath, $packageName, $env); |
325
|
3 |
|
} catch (PuliRunnerException $e) { |
326
|
3 |
|
$this->printPackageWarning('Could not install package "%s" (at "%s")', $packageName, $installPath, $e); |
327
|
|
|
|
328
|
3 |
|
continue; |
329
|
|
|
} |
330
|
|
|
|
331
|
8 |
|
$puliPackages[$packageName] = new PuliPackage( |
332
|
|
|
$packageName, |
333
|
8 |
|
self::INSTALLER_NAME, |
334
|
|
|
$installPath, |
335
|
8 |
|
PuliPackage::STATE_ENABLED, |
336
|
|
|
$env |
337
|
|
|
); |
338
|
|
|
} |
339
|
31 |
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* @param PackageInterface[] $composerPackages |
343
|
|
|
* @param PuliPackage[] $puliPackages |
344
|
|
|
*/ |
345
|
3 |
|
private function removeRemovedPackages(array $composerPackages, array &$puliPackages) |
346
|
|
|
{ |
347
|
|
|
/** @var PuliPackage[] $notFoundPackages */ |
348
|
|
|
$notFoundPackages = array_filter($puliPackages, function (PuliPackage $package) { |
349
|
3 |
|
return PuliPackage::STATE_NOT_FOUND === $package->getState() |
350
|
3 |
|
&& PuliPluginImpl::INSTALLER_NAME === $package->getInstallerName(); |
351
|
3 |
|
}); |
352
|
|
|
|
353
|
3 |
|
foreach ($notFoundPackages as $packageName => $package) { |
354
|
|
|
// Check whether package was only moved |
355
|
3 |
|
if (isset($composerPackages[$packageName])) { |
356
|
1 |
|
continue; |
357
|
|
|
} |
358
|
|
|
|
359
|
2 |
|
$this->io->write(sprintf( |
360
|
2 |
|
'Removing <info>%s</info> (<comment>%s</comment>)', |
361
|
|
|
$packageName, |
362
|
2 |
|
Path::makeRelative($package->getInstallPath(), $this->rootDir) |
363
|
|
|
)); |
364
|
|
|
|
365
|
|
|
try { |
366
|
2 |
|
$this->removePackage($packageName); |
367
|
1 |
|
} catch (PuliRunnerException $e) { |
368
|
1 |
|
$this->printPackageWarning('Could not remove package "%s" (at "%s")', $packageName, $package->getInstallPath(), $e); |
369
|
|
|
|
370
|
1 |
|
continue; |
371
|
|
|
} |
372
|
|
|
|
373
|
1 |
|
unset($puliPackages[$packageName]); |
374
|
|
|
} |
375
|
3 |
|
} |
376
|
|
|
|
377
|
3 |
View Code Duplication |
private function checkForNotFoundErrors(array $puliPackages) |
|
|
|
|
378
|
|
|
{ |
379
|
|
|
/** @var PuliPackage[] $notFoundPackages */ |
380
|
3 |
|
$notFoundPackages = array_filter($puliPackages, |
381
|
|
|
function (PuliPackage $package) { |
382
|
3 |
|
return PuliPackage::STATE_NOT_FOUND === $package->getState() |
383
|
3 |
|
&& PuliPluginImpl::INSTALLER_NAME === $package->getInstallerName(); |
384
|
3 |
|
}); |
385
|
|
|
|
386
|
3 |
|
foreach ($notFoundPackages as $package) { |
387
|
2 |
|
$this->printPackageWarning( |
388
|
2 |
|
'The package "%s" (at "%s") could not be found', |
389
|
2 |
|
$package->getName(), |
390
|
2 |
|
$package->getInstallPath() |
391
|
|
|
); |
392
|
|
|
} |
393
|
3 |
|
} |
394
|
|
|
|
395
|
31 |
View Code Duplication |
private function checkForNotLoadableErrors(array $puliPackages) |
|
|
|
|
396
|
|
|
{ |
397
|
|
|
/** @var PuliPackage[] $notLoadablePackages */ |
398
|
|
|
$notLoadablePackages = array_filter($puliPackages, function (PuliPackage $package) { |
399
|
31 |
|
return PuliPackage::STATE_NOT_LOADABLE === $package->getState() |
400
|
31 |
|
&& PuliPluginImpl::INSTALLER_NAME === $package->getInstallerName(); |
401
|
31 |
|
}); |
402
|
|
|
|
403
|
31 |
|
foreach ($notLoadablePackages as $package) { |
404
|
1 |
|
$this->printPackageWarning( |
405
|
1 |
|
'The package "%s" (at "%s") could not be loaded', |
406
|
1 |
|
$package->getName(), |
407
|
1 |
|
$package->getInstallPath() |
408
|
|
|
); |
409
|
|
|
} |
410
|
31 |
|
} |
411
|
|
|
|
412
|
31 |
|
private function adoptComposerName(array $puliPackages) |
413
|
|
|
{ |
414
|
31 |
|
$rootDir = $this->rootDir; |
415
|
|
|
|
416
|
|
|
/** @var PuliPackage[] $rootPackages */ |
417
|
31 |
|
$rootPackages = array_filter($puliPackages, function (PuliPackage $package) use ($rootDir) { |
418
|
31 |
|
return !$package->getInstallerName() && $rootDir === $package->getInstallPath(); |
419
|
31 |
|
}); |
420
|
|
|
|
421
|
31 |
|
if (0 === count($rootPackages)) { |
422
|
|
|
// This should never happen |
423
|
|
|
$this->printWarning('No root package could be found'); |
424
|
|
|
|
425
|
|
|
return; |
426
|
|
|
} |
427
|
|
|
|
428
|
31 |
|
if (count($rootPackages) > 1) { |
429
|
|
|
// This should never happen |
430
|
|
|
$this->printWarning('More than one root package was found'); |
431
|
|
|
|
432
|
|
|
return; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
/** @var PuliPackage $rootPackage */ |
436
|
31 |
|
$rootPackage = reset($rootPackages); |
437
|
31 |
|
$name = $rootPackage->getName(); |
438
|
31 |
|
$newName = $this->composer->getPackage()->getName(); |
439
|
|
|
|
440
|
|
|
// Rename the root package after changing the name in composer.json |
441
|
31 |
|
if ($name !== $newName) { |
442
|
|
|
try { |
443
|
2 |
|
$this->renamePackage($name, $newName); |
444
|
1 |
|
} catch (PuliRunnerException $e) { |
445
|
1 |
|
$this->printWarning(sprintf( |
446
|
1 |
|
'Could not rename root package to "%s"', |
447
|
|
|
$newName |
448
|
|
|
), $e); |
449
|
|
|
} |
450
|
|
|
} |
451
|
31 |
|
} |
452
|
|
|
|
453
|
4 |
|
private function insertFactoryClassConstant($autoloadFile, $factoryClass) |
454
|
|
|
{ |
455
|
4 |
|
if (!file_exists($autoloadFile)) { |
456
|
|
|
throw new PuliPluginException(sprintf( |
457
|
|
|
'Could not adjust autoloader: The file %s was not found.', |
458
|
|
|
$autoloadFile |
459
|
|
|
)); |
460
|
|
|
} |
461
|
|
|
|
462
|
4 |
|
$this->io->write('<info>Generating the "PULI_FACTORY_CLASS" constant</info>'); |
463
|
|
|
|
464
|
4 |
|
$contents = file_get_contents($autoloadFile); |
465
|
4 |
|
$escFactoryClass = var_export($factoryClass, true); |
466
|
4 |
|
$constant = "if (!defined('PULI_FACTORY_CLASS')) {\n"; |
467
|
4 |
|
$constant .= sprintf(" define('PULI_FACTORY_CLASS', %s);\n", $escFactoryClass); |
468
|
4 |
|
$constant .= "}\n\n"; |
469
|
|
|
|
470
|
|
|
// Regex modifiers: |
471
|
|
|
// "m": \s matches newlines |
472
|
|
|
// "D": $ matches at EOF only |
473
|
|
|
// Translation: insert before the last "return" in the file |
474
|
4 |
|
$contents = preg_replace('/\n(?=return [^;]+;\s*$)/mD', "\n".$constant, |
475
|
|
|
$contents); |
476
|
|
|
|
477
|
4 |
|
file_put_contents($autoloadFile, $contents); |
478
|
4 |
|
} |
479
|
|
|
|
480
|
4 |
|
private function setBootstrapFile($autoloadFile) |
481
|
|
|
{ |
482
|
4 |
|
$bootstrapFile = $this->getConfigKey('bootstrap-file'); |
483
|
|
|
|
484
|
|
|
// Don't change user-defined bootstrap files |
485
|
4 |
|
if (!empty($bootstrapFile)) { |
486
|
1 |
|
return; |
487
|
|
|
} |
488
|
|
|
|
489
|
3 |
|
$relAutoloadFile = Path::makeRelative($autoloadFile, $this->rootDir); |
490
|
|
|
|
491
|
3 |
|
$this->io->write(sprintf('<info>Setting "bootstrap-file" to "%s"</info>', $relAutoloadFile)); |
492
|
|
|
|
493
|
3 |
|
$this->setConfigKey('bootstrap-file', $relAutoloadFile); |
494
|
3 |
|
} |
495
|
|
|
|
496
|
|
|
/** |
497
|
|
|
* Loads Composer's currently installed packages. |
498
|
|
|
* |
499
|
|
|
* @return PackageInterface[] The installed packages indexed by their names |
500
|
|
|
*/ |
501
|
32 |
|
private function loadComposerPackages() |
502
|
|
|
{ |
503
|
32 |
|
$repository = $this->composer->getRepositoryManager()->getLocalRepository(); |
504
|
32 |
|
$packages = array(); |
505
|
|
|
|
506
|
32 |
|
foreach ($repository->getPackages() as $package) { |
507
|
|
|
/* @var PackageInterface $package */ |
508
|
21 |
|
$packages[$package->getName()] = $package; |
509
|
|
|
} |
510
|
|
|
|
511
|
32 |
|
return $packages; |
512
|
|
|
} |
513
|
|
|
|
514
|
3 |
|
private function getConfigKeyFromJsonFile($key) |
|
|
|
|
515
|
|
|
{ |
516
|
3 |
|
$value = null; |
|
|
|
|
517
|
3 |
|
$jsonPath = realpath(substr($this->config->get('vendor-dir'), 0, -strlen($this->config->get('vendor-dir', Config::RELATIVE_PATHS)))).DIRECTORY_SEPARATOR.'puli.json'; |
518
|
3 |
|
if (file_exists($jsonPath)) { |
519
|
|
|
$jsonFile = new JsonFile($jsonPath, new RemoteFilesystem($this->io)); |
520
|
|
|
$config = $jsonFile->read(); |
521
|
|
|
if (empty($config['version']) || '1.0' !== $config['version']) { |
522
|
|
|
throw new \RuntimeException('Invalid configuration version schema of puli.json file!'); |
523
|
|
|
} |
524
|
|
|
} |
525
|
|
|
switch ($key) { |
526
|
3 |
View Code Duplication |
case 'factory.in.file': |
|
|
|
|
527
|
3 |
|
if (empty($config) || empty($config['config']['factory']['in']['file'])) { |
528
|
3 |
|
$value = '.puli/GeneratedPuliFactory'; |
529
|
|
|
} else { |
530
|
|
|
$value = $config['config']['factory']['in']['file']; |
531
|
|
|
} |
532
|
3 |
|
break; |
533
|
3 |
View Code Duplication |
case 'factory.in.class': |
|
|
|
|
534
|
3 |
|
if (empty($config) || empty($config['config']['factory']['in']['class'])) { |
535
|
3 |
|
$value = 'Puli\\GeneratedPuliFactory'; |
536
|
|
|
} else { |
537
|
|
|
$value = $config['config']['factory']['in']['class']; |
538
|
|
|
} |
539
|
3 |
|
break; |
540
|
|
|
default: |
541
|
|
|
throw new \RuntimeException(sprintf('Cannot extract key "%s" from config!', $key)); |
542
|
|
|
} |
543
|
|
|
|
544
|
3 |
|
return $value; |
545
|
|
|
} |
546
|
|
|
|
547
|
36 |
|
private function getConfigKey($key) |
548
|
|
|
{ |
549
|
36 |
|
$value = trim($this->puliRunner->run('config %key% --parsed', array( |
550
|
36 |
|
'key' => $key, |
551
|
|
|
))); |
552
|
|
|
|
553
|
|
|
switch ($value) { |
554
|
35 |
|
case 'null': |
555
|
1 |
|
return null; |
556
|
35 |
|
case 'true': |
557
|
|
|
return true; |
558
|
35 |
|
case 'false': |
559
|
|
|
return false; |
560
|
|
|
default: |
561
|
35 |
|
return $value; |
562
|
|
|
} |
563
|
|
|
} |
564
|
|
|
|
565
|
3 |
|
private function setConfigKey($key, $value) |
566
|
|
|
{ |
567
|
3 |
|
$this->puliRunner->run('config %key% %value%', array( |
568
|
3 |
|
'key' => $key, |
569
|
3 |
|
'value' => $value, |
570
|
|
|
)); |
571
|
3 |
|
} |
572
|
|
|
|
573
|
|
|
/** |
574
|
|
|
* @return PuliPackage[] |
575
|
|
|
*/ |
576
|
32 |
|
private function loadPuliPackages() |
577
|
|
|
{ |
578
|
32 |
|
$packages = array(); |
579
|
|
|
|
580
|
32 |
|
$output = $this->puliRunner->run('package --list --format %format%', array( |
581
|
32 |
|
'format' => '%name%;%installer%;%install_path%;%state%;%env%', |
582
|
|
|
)); |
583
|
|
|
|
584
|
|
|
// PuliRunner replaces \r\n by \n for those Windows boxes |
585
|
31 |
|
foreach (explode("\n", $output) as $packageLine) { |
586
|
31 |
|
if (!$packageLine) { |
587
|
31 |
|
continue; |
588
|
|
|
} |
589
|
|
|
|
590
|
31 |
|
$packageParts = explode(';', $packageLine); |
591
|
|
|
|
592
|
31 |
|
$packages[$packageParts[0]] = new PuliPackage( |
593
|
31 |
|
$packageParts[0], |
594
|
31 |
|
$packageParts[1], |
595
|
31 |
|
$packageParts[2], |
596
|
31 |
|
$packageParts[3], |
597
|
31 |
|
$packageParts[4] |
598
|
|
|
); |
599
|
|
|
} |
600
|
|
|
|
601
|
31 |
|
return $packages; |
602
|
|
|
} |
603
|
|
|
|
604
|
11 |
|
private function installPackage($installPath, $packageName, $env) |
605
|
|
|
{ |
606
|
11 |
|
$env = PuliPackage::ENV_DEV === $env ? ' --dev' : ''; |
607
|
|
|
|
608
|
11 |
|
$this->puliRunner->run('package --install %path% %package_name% --installer %installer%'.$env, array( |
609
|
11 |
|
'path' => $installPath, |
610
|
11 |
|
'package_name' => $packageName, |
611
|
11 |
|
'installer' => self::INSTALLER_NAME, |
612
|
|
|
)); |
613
|
8 |
|
} |
614
|
|
|
|
615
|
7 |
|
private function removePackage($packageName) |
616
|
|
|
{ |
617
|
7 |
|
$this->puliRunner->run('package --delete %package_name%', array( |
618
|
7 |
|
'package_name' => $packageName, |
619
|
|
|
)); |
620
|
5 |
|
} |
621
|
|
|
|
622
|
31 |
|
private function removePuliDir() |
623
|
|
|
{ |
624
|
31 |
|
$relativePuliDir = rtrim($this->getConfigKey('puli-dir'), '/'); |
625
|
|
|
|
626
|
31 |
|
$puliDir = Path::makeAbsolute($relativePuliDir, $this->rootDir); |
627
|
|
|
|
628
|
|
|
// Only remove existing sub-directories of the root directory |
629
|
31 |
|
if (!file_exists($puliDir) || 0 !== strpos($puliDir, $this->rootDir.'/')) { |
630
|
30 |
|
return; |
631
|
|
|
} |
632
|
|
|
|
633
|
1 |
|
$this->io->write(sprintf('<info>Deleting the "%s" directory</info>', $relativePuliDir)); |
634
|
|
|
|
635
|
|
|
// Remove the .puli directory to prevent upgrade problems |
636
|
1 |
|
$filesystem = new Filesystem(); |
637
|
1 |
|
$filesystem->remove($puliDir); |
638
|
1 |
|
} |
639
|
|
|
|
640
|
31 |
|
private function buildPuli() |
641
|
|
|
{ |
642
|
31 |
|
$this->io->write('<info>Running "puli build"</info>'); |
643
|
|
|
|
644
|
31 |
|
$this->puliRunner->run('build'); |
645
|
31 |
|
} |
646
|
|
|
|
647
|
2 |
|
private function renamePackage($name, $newName) |
648
|
|
|
{ |
649
|
2 |
|
$this->puliRunner->run('package --rename %old_name% %new_name%', array( |
650
|
2 |
|
'old_name' => $name, |
651
|
2 |
|
'new_name' => $newName, |
652
|
|
|
)); |
653
|
1 |
|
} |
654
|
|
|
|
655
|
|
|
/** |
656
|
|
|
* @param $message |
657
|
|
|
* @param Exception|null $exception |
658
|
|
|
*/ |
659
|
12 |
|
private function printWarning($message, Exception $exception = null) |
660
|
|
|
{ |
661
|
12 |
|
if (!$exception) { |
662
|
3 |
|
$reasonPhrase = ''; |
663
|
10 |
|
} elseif ($this->io->isVerbose()) { |
664
|
|
|
$reasonPhrase = $exception instanceof PuliRunnerException |
665
|
|
|
? $exception->getFullError() |
666
|
|
|
: $exception->getMessage()."\n\n".$exception->getTraceAsString(); |
667
|
|
|
} else { |
668
|
10 |
|
$reasonPhrase = $exception instanceof PuliRunnerException |
669
|
8 |
|
? $exception->getShortError() |
670
|
10 |
|
: $exception->getMessage(); |
671
|
|
|
} |
672
|
|
|
|
673
|
12 |
|
$this->io->writeError(sprintf( |
674
|
12 |
|
'<warning>Warning: %s%s</warning>', |
675
|
|
|
$message, |
676
|
12 |
|
$reasonPhrase ? ': '.$reasonPhrase : '.' |
677
|
|
|
)); |
678
|
12 |
|
} |
679
|
|
|
|
680
|
7 |
|
private function printPackageWarning($message, $packageName, $installPath, PuliRunnerException $exception = null) |
681
|
|
|
{ |
682
|
7 |
|
$this->printWarning(sprintf( |
683
|
|
|
$message, |
684
|
|
|
$packageName, |
685
|
7 |
|
Path::makeRelative($installPath, $this->rootDir) |
686
|
|
|
), $exception); |
687
|
7 |
|
} |
688
|
|
|
|
689
|
32 |
|
private function filterProdPackageNames(array $composerPackages, PackageInterface $package, array &$result = array()) |
690
|
|
|
{ |
691
|
|
|
// Resolve aliases |
692
|
32 |
|
if ($package instanceof AliasPackage) { |
693
|
2 |
|
$package = $package->getAliasOf(); |
694
|
|
|
} |
695
|
|
|
|
696
|
|
|
// Package was processed already |
697
|
32 |
|
if (isset($result[$package->getName()])) { |
698
|
|
|
return $result; |
699
|
|
|
} |
700
|
|
|
|
701
|
32 |
|
$result[$package->getName()] = true; |
702
|
|
|
|
703
|
|
|
// Recursively filter package names |
704
|
32 |
|
foreach ($package->getRequires() as $packageName => $link) { |
705
|
21 |
|
if (isset($composerPackages[$packageName])) { |
706
|
21 |
|
$this->filterProdPackageNames($composerPackages, $composerPackages[$packageName], $result); |
707
|
|
|
} |
708
|
|
|
} |
709
|
|
|
|
710
|
32 |
|
return $result; |
711
|
|
|
} |
712
|
|
|
|
713
|
39 |
|
private function verifyPuliVersion() |
714
|
|
|
{ |
715
|
39 |
|
$versionString = $this->puliRunner->run('-V'); |
716
|
|
|
|
717
|
39 |
|
if (!preg_match('~^Puli version (\S+)$~', $versionString, $matches)) { |
718
|
|
|
throw new RuntimeException(sprintf( |
719
|
|
|
'Could not determine Puli version. "puli -V" returned: %s', |
720
|
|
|
$versionString |
721
|
|
|
)); |
722
|
|
|
} |
723
|
|
|
|
724
|
|
|
// the development build of the plugin is always considered compatible |
725
|
|
|
// with the development build of the CLI |
726
|
|
|
// Split strings to prevent replacement during release |
727
|
39 |
|
if ('@package_'.'version@' === self::VERSION && '@package_'.'version@' === $matches[1]) { |
728
|
1 |
|
return; |
729
|
|
|
} |
730
|
|
|
|
731
|
38 |
View Code Duplication |
if (version_compare($matches[1], self::MIN_CLI_VERSION, '<')) { |
|
|
|
|
732
|
1 |
|
throw new RuntimeException(sprintf( |
733
|
|
|
'Found an unsupported version of the Puli CLI: %s. Please '. |
734
|
|
|
'upgrade to version %s or higher. You can also install the '. |
735
|
1 |
|
'puli/cli dependency at version %s in your project.', |
736
|
1 |
|
$matches[1], |
737
|
1 |
|
self::MIN_CLI_VERSION, |
738
|
1 |
|
self::MIN_CLI_VERSION |
739
|
|
|
)); |
740
|
|
|
} |
741
|
|
|
|
742
|
37 |
View Code Duplication |
if (version_compare($matches[1], self::MAX_CLI_VERSION, '>')) { |
|
|
|
|
743
|
1 |
|
throw new RuntimeException(sprintf( |
744
|
|
|
'Found an unsupported version of the Puli CLI: %s. Please '. |
745
|
|
|
'downgrade to a lower version than %s. You can also install '. |
746
|
1 |
|
'the puli/cli dependency in your project.', |
747
|
1 |
|
$matches[1], |
748
|
1 |
|
self::MAX_CLI_VERSION |
749
|
|
|
)); |
750
|
|
|
} |
751
|
36 |
|
} |
752
|
|
|
} |
753
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.