Failed Conditions
Pull Request — master (#49)
by Helmut
04:31 queued 02:07
created

PuliPluginImpl::loadComposerPackages()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.4285
cc 2
eloc 6
nc 2
nop 0
crap 2
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->getConfigKey('factory.in.class');
128
        $factoryFile = $this->getConfigKey('factory.in.file');
129
        $factoryFile = Path::makeAbsolute($factoryFile, $this->rootDir);
130
131
        $autoload = $this->composer->getPackage()->getAutoload();
132
        $autoload['classmap'][] = $factoryFile;
133
134
        $this->composer->getPackage()->setAutoload($autoload);
135
136
        if (!file_exists($factoryFile)) {
137
            $filesystem = new Filesystem();
138
            // Let Composer find the factory class with a temporary stub
139
140
            $namespace = explode('\\', ltrim($factoryClass, '\\'));
141
            $className = array_pop($namespace);
142
143
            if (count($namespace)) {
144
                $stub = '<?php namespace '.implode('\\', $namespace).'; class '.$className.' {}';
145
            } else {
146
                $stub = '<?php class '.$className.' {}';
147
            }
148
149
            $filesystem->dumpFile($factoryFile, $stub);
150
        }
151
    }
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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 39
    private function getConfigKey($key)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
515
    {
516 39
        if (null === $this->puliRunner) {
517
            $puliJsonPath = realpath(substr($this->config->get('vendor-dir'), 0, -strlen($this->config->get('vendor-dir', Config::RELATIVE_PATHS)))).DIRECTORY_SEPARATOR.'puli.json';
518
            if (file_exists($puliJsonPath)) {
519
                $puliJsonFile = new JsonFile($puliJsonPath, new RemoteFilesystem($this->io));
520
                $puliConfig = $puliJsonFile->read();
521
                if (empty($puliConfig['version']) || '1.0' !== $puliConfig['version']) {
522
                    throw new \RuntimeException('Invalid configuration version schema!');
523
                }
524
            }
525
            switch ($key) {
526 View Code Duplication
                case 'factory.in.file':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
527
                    if (empty($puliConfig) || empty($puliConfig['config']['factory']['in']['file'])) {
528
                        return '.puli/GeneratedPuliFactory';
529
                    }
530
531
                    return $puliConfig['config']['factory']['in']['file'];
532
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
533 View Code Duplication
                case 'factory.in.class':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
534
                    if (empty($puliConfig) || empty($puliConfig['config']['factory']['in']['class'])) {
535
                        return 'Puli\\GeneratedPuliFactory';
536
                    }
537
538
                    return $puliConfig['config']['factory']['in']['class'];
539
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
540
                default:
541
                    throw new \RuntimeException(sprintf('Cannot extract key "%s" from config!', $key));
542
            }
543
        }
544
545 39
        $value = trim($this->puliRunner->run('config %key% --parsed', array(
546 39
            'key' => $key,
547
        )));
548
549
        switch ($value) {
550 35
            case 'null':
551 1
                return null;
552 35
            case 'true':
553
                return true;
554 35
            case 'false':
555
                return false;
556
            default:
557 35
                return $value;
558
        }
559
    }
560
561 3
    private function setConfigKey($key, $value)
562
    {
563 3
        $this->puliRunner->run('config %key% %value%', array(
564 3
            'key' => $key,
565 3
            'value' => $value,
566
        ));
567 3
    }
568
569
    /**
570
     * @return PuliPackage[]
571
     */
572 32
    private function loadPuliPackages()
573
    {
574 32
        $packages = array();
575
576 32
        $output = $this->puliRunner->run('package --list --format %format%', array(
577 32
            'format' => '%name%;%installer%;%install_path%;%state%;%env%',
578
        ));
579
580
        // PuliRunner replaces \r\n by \n for those Windows boxes
581 31
        foreach (explode("\n", $output) as $packageLine) {
582 31
            if (!$packageLine) {
583 31
                continue;
584
            }
585
586 31
            $packageParts = explode(';', $packageLine);
587
588 31
            $packages[$packageParts[0]] = new PuliPackage(
589 31
                $packageParts[0],
590 31
                $packageParts[1],
591 31
                $packageParts[2],
592 31
                $packageParts[3],
593 31
                $packageParts[4]
594
            );
595
        }
596
597 31
        return $packages;
598
    }
599
600 11
    private function installPackage($installPath, $packageName, $env)
601
    {
602 11
        $env = PuliPackage::ENV_DEV === $env ? ' --dev' : '';
603
604 11
        $this->puliRunner->run('package --install %path% %package_name% --installer %installer%'.$env, array(
605 11
            'path' => $installPath,
606 11
            'package_name' => $packageName,
607 11
            'installer' => self::INSTALLER_NAME,
608
        ));
609 8
    }
610
611 7
    private function removePackage($packageName)
612
    {
613 7
        $this->puliRunner->run('package --delete %package_name%', array(
614 7
            'package_name' => $packageName,
615
        ));
616 5
    }
617
618 31
    private function removePuliDir()
619
    {
620 31
        $relativePuliDir = rtrim($this->getConfigKey('puli-dir'), '/');
621
622 31
        $puliDir = Path::makeAbsolute($relativePuliDir, $this->rootDir);
623
624
        // Only remove existing sub-directories of the root directory
625 31
        if (!file_exists($puliDir) || 0 !== strpos($puliDir, $this->rootDir.'/')) {
626 30
            return;
627
        }
628
629 1
        $this->io->write(sprintf('<info>Deleting the "%s" directory</info>', $relativePuliDir));
630
631
        // Remove the .puli directory to prevent upgrade problems
632 1
        $filesystem = new Filesystem();
633 1
        $filesystem->remove($puliDir);
634 1
    }
635
636 31
    private function buildPuli()
637
    {
638 31
        $this->io->write('<info>Running "puli build"</info>');
639
640 31
        $this->puliRunner->run('build');
641 31
    }
642
643 2
    private function renamePackage($name, $newName)
644
    {
645 2
        $this->puliRunner->run('package --rename %old_name% %new_name%', array(
646 2
            'old_name' => $name,
647 2
            'new_name' => $newName,
648
        ));
649 1
    }
650
651
    /**
652
     * @param                $message
653
     * @param Exception|null $exception
654
     */
655 12
    private function printWarning($message, Exception $exception = null)
656
    {
657 12
        if (!$exception) {
658 3
            $reasonPhrase = '';
659 10
        } elseif ($this->io->isVerbose()) {
660
            $reasonPhrase = $exception instanceof PuliRunnerException
661
                ? $exception->getFullError()
662
                : $exception->getMessage()."\n\n".$exception->getTraceAsString();
663
        } else {
664 10
            $reasonPhrase = $exception instanceof PuliRunnerException
665 8
                ? $exception->getShortError()
666 10
                : $exception->getMessage();
667
        }
668
669 12
        $this->io->writeError(sprintf(
670 12
            '<warning>Warning: %s%s</warning>',
671
            $message,
672 12
            $reasonPhrase ? ': '.$reasonPhrase : '.'
673
        ));
674 12
    }
675
676 7
    private function printPackageWarning($message, $packageName, $installPath, PuliRunnerException $exception = null)
677
    {
678 7
        $this->printWarning(sprintf(
679
            $message,
680
            $packageName,
681 7
            Path::makeRelative($installPath, $this->rootDir)
682
        ), $exception);
683 7
    }
684
685 32
    private function filterProdPackageNames(array $composerPackages, PackageInterface $package, array &$result = array())
686
    {
687
        // Resolve aliases
688 32
        if ($package instanceof AliasPackage) {
689 2
            $package = $package->getAliasOf();
690
        }
691
692
        // Package was processed already
693 32
        if (isset($result[$package->getName()])) {
694
            return $result;
695
        }
696
697 32
        $result[$package->getName()] = true;
698
699
        // Recursively filter package names
700 32
        foreach ($package->getRequires() as $packageName => $link) {
701 21
            if (isset($composerPackages[$packageName])) {
702 21
                $this->filterProdPackageNames($composerPackages, $composerPackages[$packageName], $result);
703
            }
704
        }
705
706 32
        return $result;
707
    }
708
709 39
    private function verifyPuliVersion()
710
    {
711 39
        $versionString = $this->puliRunner->run('-V');
712
713 39
        if (!preg_match('~^Puli version (\S+)$~', $versionString, $matches)) {
714
            throw new RuntimeException(sprintf(
715
                'Could not determine Puli version. "puli -V" returned: %s',
716
                $versionString
717
            ));
718
        }
719
720
        // the development build of the plugin is always considered compatible
721
        // with the development build of the CLI
722
        // Split strings to prevent replacement during release
723 39
        if ('@package_'.'version@' === self::VERSION && '@package_'.'version@' === $matches[1]) {
724 1
            return;
725
        }
726
727 38 View Code Duplication
        if (version_compare($matches[1], self::MIN_CLI_VERSION, '<')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
728 1
            throw new RuntimeException(sprintf(
729
                'Found an unsupported version of the Puli CLI: %s. Please '.
730
                'upgrade to version %s or higher. You can also install the '.
731 1
                'puli/cli dependency at version %s in your project.',
732 1
                $matches[1],
733 1
                self::MIN_CLI_VERSION,
734 1
                self::MIN_CLI_VERSION
735
            ));
736
        }
737
738 37 View Code Duplication
        if (version_compare($matches[1], self::MAX_CLI_VERSION, '>')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
739 1
            throw new RuntimeException(sprintf(
740
                'Found an unsupported version of the Puli CLI: %s. Please '.
741
                'downgrade to a lower version than %s. You can also install '.
742 1
                'the puli/cli dependency in your project.',
743 1
                $matches[1],
744 1
                self::MAX_CLI_VERSION
745
            ));
746
        }
747 36
    }
748
}
749