Failed Conditions
Push — master ( 7d7406...164ad0 )
by Bernhard
06:03
created

PuliPluginImpl::setConfigKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.4286
ccs 5
cts 5
cp 1
cc 1
eloc 4
nc 1
nop 2
crap 1
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\Package\AliasPackage;
18
use Composer\Package\PackageInterface;
19
use Composer\Script\Event;
20
use Exception;
21
use RuntimeException;
22
use Symfony\Component\Filesystem\Filesystem;
23
use Webmozart\PathUtil\Path;
24
25
/**
26
 * Implementation of the Puli plugin.
27
 *
28
 * This class is separate from the main {@link PuliPlugin} class so that it can
29
 * be loaded lazily after updating the sources of this package in the project
30
 * that requires the package.
31
 *
32
 * @author Bernhard Schussek <[email protected]>
33
 */
34
class PuliPluginImpl
35
{
36
    /**
37
     * The version of the Puli plugin.
38
     */
39
    const VERSION = '@package_version@';
40
41
    /**
42
     * The minimum version of the Puli CLI.
43
     */
44
    const MIN_CLI_VERSION = '1.0.0-beta10';
45
46
    /**
47
     * The maximum version of the Puli CLI.
48
     */
49
    const MAX_CLI_VERSION = '1.999.99999';
50
51
    /**
52
     * The name of the installer.
53
     */
54
    const INSTALLER_NAME = 'composer';
55
56
    /**
57
     * @var Composer
58
     */
59
    private $composer;
60
61
    /**
62
     * @var IOInterface
63
     */
64
    private $io;
65
66
    /**
67
     * @var Config
68
     */
69
    private $config;
70
71
    /**
72
     * @var bool
73
     */
74
    private $isDev;
75
76
    /**
77
     * @var PuliRunner
78
     */
79
    private $puliRunner;
80
81
    /**
82
     * @var string
83
     */
84
    private $rootDir;
85
86
    /**
87
     * @var bool
88
     */
89
    private $runPostAutoloadDump = true;
90
91
    /**
92
     * @var bool
93
     */
94
    private $runPostInstall = true;
95
96
    /**
97
     * @var bool
98
     */
99
    private $initialized = false;
100
101 43
    public function __construct(Event $event, PuliRunner $puliRunner = null)
102
    {
103 43
        $this->composer = $event->getComposer();
104 43
        $this->io = $event->getIO();
105 43
        $this->config = $this->composer->getConfig();
106 43
        $this->isDev = $event->isDevMode();
107 43
        $this->puliRunner = $puliRunner;
108 43
        $this->rootDir = Path::normalize(getcwd());
109 43
    }
110
111 9
    public function postAutoloadDump()
112
    {
113 9
        if (!$this->initialized) {
114 9
            $this->initialize();
115
        }
116
117
        // This method is called twice. Run it only once.
118 9
        if (!$this->runPostAutoloadDump) {
119 1
            return;
120
        }
121
122 9
        $this->runPostAutoloadDump = false;
123
124 9
        $vendorDir = $this->config->get('vendor-dir');
125
126
        // On TravisCI, $vendorDir is a relative path. Probably an old Composer
127
        // build or something. Usually, $vendorDir should be absolute already.
128 9
        $vendorDir = Path::makeAbsolute($vendorDir, $this->rootDir);
129
130 9
        $autoloadFile = $vendorDir.'/autoload.php';
131 9
        $classMapFile = $vendorDir.'/composer/autoload_classmap.php';
132
133
        try {
134 9
            $factoryClass = $this->getConfigKey('factory.in.class');
135 8
            $factoryFile = $this->getConfigKey('factory.in.file');
136 2
        } catch (PuliRunnerException $e) {
137 2
            $this->printWarning('Could not load Puli configuration', $e);
138
139 2
            return;
140
        }
141
142 7
        $factoryFile = Path::makeAbsolute($factoryFile, $this->rootDir);
0 ignored issues
show
Bug introduced by
It seems like $factoryFile can also be of type boolean or null; however, Webmozart\PathUtil\Path::makeAbsolute() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
143
144 7
        $this->insertFactoryClassConstant($autoloadFile, $factoryClass);
145 6
        $this->insertFactoryClassMap($classMapFile, $vendorDir, $factoryClass, $factoryFile);
146 5
        $this->setBootstrapFile($autoloadFile);
147 5
    }
148
149
    /**
150
     * Updates the Puli repository after Composer installations/updates.
151
     */
152 34
    public function postInstall()
153
    {
154 34
        if (!$this->initialized) {
155 34
            $this->initialize();
156
        }
157
158
        // This method is called twice. Run it only once.
159 34
        if (!$this->runPostInstall) {
160 3
            return;
161
        }
162
163 32
        $this->runPostInstall = false;
164
165 32
        $this->io->write('<info>Synchronizing Puli with Composer</info>');
166
167 32
        $rootPackage = $this->composer->getPackage();
168 32
        $composerPackages = $this->loadComposerPackages();
169 32
        $prodPackageNames = $this->filterProdPackageNames($composerPackages, $rootPackage);
170 32
        $env = $this->isDev ? PuliPackage::ENV_DEV : PuliPackage::ENV_PROD;
171
172
        try {
173 32
            $puliPackages = $this->loadPuliPackages();
174 1
        } catch (PuliRunnerException $e) {
175 1
            $this->printWarning('Could not load Puli packages', $e);
176
177 1
            return;
178
        }
179
180
        // Don't remove non-existing packages in production environment
181
        // Removed packages could be dev dependencies (i.e. "require-dev"
182
        // of the root package or "require" of another dev dependency), and
183
        // we can't find out whether they are since Composer doesn't load them
184 31
        if (PuliPackage::ENV_PROD !== $env) {
185 3
            $this->removeRemovedPackages($composerPackages, $puliPackages);
186
        }
187
188 31
        $this->installNewPackages($composerPackages, $prodPackageNames, $puliPackages);
189
190
        // Don't print warnings for non-existing packages in production
191 31
        if (PuliPackage::ENV_PROD !== $env) {
192 3
            $this->checkForNotFoundErrors($puliPackages);
193
        }
194
195 31
        $this->checkForNotLoadableErrors($puliPackages);
196 31
        $this->adoptComposerName($puliPackages);
197 31
        $this->removePuliDir();
198 31
        $this->buildPuli();
199 31
    }
200
201 43
    private function initialize()
202
    {
203 43
        $this->initialized = true;
204
205
        // Keep the manually set runner
206 43
        if (null === $this->puliRunner) {
207
            try {
208
                // Add Composer's bin directory in case the "puli" executable is
209
                // installed with Composer
210
                $this->puliRunner = new PuliRunner($this->config->get('bin-dir'));
211
            } catch (RuntimeException $e) {
212
                $this->printWarning('Plugin initialization failed', $e);
213
                $this->runPostAutoloadDump = false;
214
                $this->runPostInstall = false;
215
            }
216
        }
217
218
        // Use the runner to verify if Puli has the right version
219
        try {
220 43
            $this->verifyPuliVersion();
221 2
        } catch (RuntimeException $e) {
222 2
            $this->printWarning('Version check failed', $e);
223 2
            $this->runPostAutoloadDump = false;
224 2
            $this->runPostInstall = false;
225
        }
226 43
    }
227
228
    /**
229
     * @param PackageInterface[] $composerPackages
230
     * @param bool[]             $prodPackageNames
231
     * @param PuliPackage[]      $puliPackages
232
     */
233 31
    private function installNewPackages(array $composerPackages, array $prodPackageNames, array &$puliPackages)
234
    {
235 31
        $installationManager = $this->composer->getInstallationManager();
236
237 31
        foreach ($composerPackages as $packageName => $package) {
238 21
            if ($package instanceof AliasPackage) {
239 2
                $package = $package->getAliasOf();
240
            }
241
242
            // We need to normalize the system-dependent paths returned by Composer
243 21
            $installPath = Path::normalize($installationManager->getInstallPath($package));
244 21
            $env = isset($prodPackageNames[$packageName]) ? PuliPackage::ENV_PROD : PuliPackage::ENV_DEV;
245
246
            // Skip meta packages
247 21
            if ('' === $installPath) {
248 1
                continue;
249
            }
250
251 20
            if (isset($puliPackages[$packageName])) {
252 15
                $puliPackage = $puliPackages[$packageName];
253
254
                // Only proceed if the install path or environment has changed
255 15
                if ($installPath === $puliPackage->getInstallPath() && $env === $puliPackage->getEnvironment()) {
256 14
                    continue;
257
                }
258
259
                // Only remove packages installed by Composer
260 6
                if (self::INSTALLER_NAME === $puliPackage->getInstallerName()) {
261 5
                    $this->io->write(sprintf(
262 5
                        'Reinstalling <info>%s</info> (<comment>%s</comment>) in <comment>%s</comment>',
263
                        $packageName,
264 5
                        Path::makeRelative($installPath, $this->rootDir),
265
                        $env
266
                    ));
267
268
                    try {
269 5
                        $this->removePackage($packageName);
270 1
                    } catch (PuliRunnerException $e) {
271 1
                        $this->printPackageWarning('Could not remove package "%s" (at "%s")', $packageName, $installPath, $e);
272
273 6
                        continue;
274
                    }
275
                }
276
            } else {
277 6
                $this->io->write(sprintf(
278 6
                    'Installing <info>%s</info> (<comment>%s</comment>) in <comment>%s</comment>',
279
                    $packageName,
280 6
                    Path::makeRelative($installPath, $this->rootDir),
281
                    $env
282
                ));
283
            }
284
285
            try {
286 11
                $this->installPackage($installPath, $packageName, $env);
287 3
            } catch (PuliRunnerException $e) {
288 3
                $this->printPackageWarning('Could not install package "%s" (at "%s")', $packageName, $installPath, $e);
289
290 3
                continue;
291
            }
292
293 8
            $puliPackages[$packageName] = new PuliPackage(
294
                $packageName,
295 8
                self::INSTALLER_NAME,
296
                $installPath,
297 8
                PuliPackage::STATE_ENABLED,
298
                $env
299
            );
300
        }
301 31
    }
302
303
    /**
304
     * @param PackageInterface[] $composerPackages
305
     * @param PuliPackage[]      $puliPackages
306
     */
307 3
    private function removeRemovedPackages(array $composerPackages, array &$puliPackages)
308
    {
309
        /** @var PuliPackage[] $notFoundPackages */
310
        $notFoundPackages = array_filter($puliPackages, function (PuliPackage $package) {
311 3
            return PuliPackage::STATE_NOT_FOUND === $package->getState()
312 3
                && PuliPluginImpl::INSTALLER_NAME === $package->getInstallerName();
313 3
        });
314
315 3
        foreach ($notFoundPackages as $packageName => $package) {
316
            // Check whether package was only moved
317 3
            if (isset($composerPackages[$packageName])) {
318 1
                continue;
319
            }
320
321 2
            $this->io->write(sprintf(
322 2
                'Removing <info>%s</info> (<comment>%s</comment>)',
323
                $packageName,
324 2
                Path::makeRelative($package->getInstallPath(), $this->rootDir)
325
            ));
326
327
            try {
328 2
                $this->removePackage($packageName);
329 1
            } catch (PuliRunnerException $e) {
330 1
                $this->printPackageWarning('Could not remove package "%s" (at "%s")', $packageName, $package->getInstallPath(), $e);
331
332 1
                continue;
333
            }
334
335 1
            unset($puliPackages[$packageName]);
336
        }
337 3
    }
338
339 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...
340
    {
341
        /** @var PuliPackage[] $notFoundPackages */
342 3
        $notFoundPackages = array_filter($puliPackages,
343
            function (PuliPackage $package) {
344 3
                return PuliPackage::STATE_NOT_FOUND === $package->getState()
345 3
                && PuliPluginImpl::INSTALLER_NAME === $package->getInstallerName();
346 3
            });
347
348 3
        foreach ($notFoundPackages as $package) {
349 2
            $this->printPackageWarning(
350 2
                'The package "%s" (at "%s") could not be found',
351 2
                $package->getName(),
352 2
                $package->getInstallPath()
353
            );
354
        }
355 3
    }
356
357 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...
358
    {
359
        /** @var PuliPackage[] $notLoadablePackages */
360
        $notLoadablePackages = array_filter($puliPackages, function (PuliPackage $package) {
361 31
            return PuliPackage::STATE_NOT_LOADABLE === $package->getState()
362 31
                && PuliPluginImpl::INSTALLER_NAME === $package->getInstallerName();
363 31
        });
364
365 31
        foreach ($notLoadablePackages as $package) {
366 1
            $this->printPackageWarning(
367 1
                'The package "%s" (at "%s") could not be loaded',
368 1
                $package->getName(),
369 1
                $package->getInstallPath()
370
            );
371
        }
372 31
    }
373
374 31
    private function adoptComposerName(array $puliPackages)
375
    {
376 31
        $rootDir = $this->rootDir;
377
378
        /** @var PuliPackage[] $rootPackages */
379 31
        $rootPackages = array_filter($puliPackages, function (PuliPackage $package) use ($rootDir) {
380 31
            return !$package->getInstallerName() && $rootDir === $package->getInstallPath();
381 31
        });
382
383 31
        if (0 === count($rootPackages)) {
384
            // This should never happen
385
            $this->printWarning('No root package could be found');
386
387
            return;
388
        }
389
390 31
        if (count($rootPackages) > 1) {
391
            // This should never happen
392
            $this->printWarning('More than one root package was found');
393
394
            return;
395
        }
396
397
        /** @var PuliPackage $rootPackage */
398 31
        $rootPackage = reset($rootPackages);
399 31
        $name = $rootPackage->getName();
400 31
        $newName = $this->composer->getPackage()->getName();
401
402
        // Rename the root package after changing the name in composer.json
403 31
        if ($name !== $newName) {
404
            try {
405 2
                $this->renamePackage($name, $newName);
406 1
            } catch (PuliRunnerException $e) {
407 1
                $this->printWarning(sprintf(
408 1
                    'Could not rename root package to "%s"',
409
                    $newName
410
                ), $e);
411
            }
412
        }
413 31
    }
414
415 7
    private function insertFactoryClassConstant($autoloadFile, $factoryClass)
416
    {
417 7
        if (!file_exists($autoloadFile)) {
418 1
            throw new PuliPluginException(sprintf(
419 1
                'Could not adjust autoloader: The file %s was not found.',
420
                $autoloadFile
421
            ));
422
        }
423
424 6
        $this->io->write('<info>Generating the "PULI_FACTORY_CLASS" constant</info>');
425
426 6
        $contents = file_get_contents($autoloadFile);
427 6
        $escFactoryClass = var_export($factoryClass, true);
428 6
        $constant = "if (!defined('PULI_FACTORY_CLASS')) {\n";
429 6
        $constant .= sprintf("    define('PULI_FACTORY_CLASS', %s);\n", $escFactoryClass);
430 6
        $constant .= "}\n\n";
431
432
        // Regex modifiers:
433
        // "m": \s matches newlines
434
        // "D": $ matches at EOF only
435
        // Translation: insert before the last "return" in the file
436 6
        $contents = preg_replace('/\n(?=return [^;]+;\s*$)/mD', "\n".$constant,
437
            $contents);
438
439 6
        file_put_contents($autoloadFile, $contents);
440 6
    }
441
442 6
    private function insertFactoryClassMap($classMapFile, $vendorDir, $factoryClass, $factoryFile)
443
    {
444 6
        if (!file_exists($classMapFile)) {
445 1
            throw new PuliPluginException(sprintf(
446 1
                'Could not adjust autoloader: The file %s was not found.',
447
                $classMapFile
448
            ));
449
        }
450
451 5
        $this->io->write(sprintf('<info>Registering "%s" with the class-map autoloader</info>', $factoryClass));
452
453 5
        $relFactoryFile = Path::makeRelative($factoryFile, $vendorDir);
454 5
        $escFactoryClass = var_export($factoryClass, true);
455 5
        $escFactoryFile = var_export('/'.$relFactoryFile, true);
456 5
        $classMap = sprintf("\n    %s => \$vendorDir . %s,", $escFactoryClass, $escFactoryFile);
457
458 5
        $contents = file_get_contents($classMapFile);
459
460
        // Regex modifiers:
461
        // "m": \s matches newlines
462
        // "D": $ matches at EOF only
463
        // Translation: insert before the last ");" in the file
464 5
        $contents = preg_replace('/\n(?=\);\s*$)/mD', "\n".$classMap, $contents);
465
466 5
        file_put_contents($classMapFile, $contents);
467 5
    }
468
469 5
    private function setBootstrapFile($autoloadFile)
470
    {
471 5
        $bootstrapFile = $this->getConfigKey('bootstrap-file');
472
473
        // Don't change user-defined bootstrap files
474 5
        if (!empty($bootstrapFile)) {
475 1
            return;
476
        }
477
478 4
        $relAutoloadFile = Path::makeRelative($autoloadFile, $this->rootDir);
479
480 4
        $this->io->write(sprintf('<info>Setting "bootstrap-file" to "%s"</info>', $relAutoloadFile));
481
482 4
        $this->setConfigKey('bootstrap-file', $relAutoloadFile);
483 4
    }
484
485
    /**
486
     * Loads Composer's currently installed packages.
487
     *
488
     * @return PackageInterface[] The installed packages indexed by their names.
489
     */
490 32
    private function loadComposerPackages()
491
    {
492 32
        $repository = $this->composer->getRepositoryManager()->getLocalRepository();
493 32
        $packages = array();
494
495 32
        foreach ($repository->getPackages() as $package) {
496
            /* @var PackageInterface $package */
497 21
            $packages[$package->getName()] = $package;
498
        }
499
500 32
        return $packages;
501
    }
502
503 40
    private function getConfigKey($key)
504
    {
505 40
        $value = trim($this->puliRunner->run('config %key% --parsed', array(
506 40
            'key' => $key,
507
        )));
508
509
        switch ($value) {
510 39
            case 'null':
511 1
                return null;
512
            case 'true':
513
                return true;
514 39
            case 'false':
515
                return false;
516
            default:
517 39
                return $value;
518
        }
519
    }
520
521 4
    private function setConfigKey($key, $value)
522
    {
523 4
        $this->puliRunner->run('config %key% %value%', array(
524 4
            'key' => $key,
525 4
            'value' => $value,
526
        ));
527 4
    }
528
529
    /**
530
     * @return PuliPackage[]
531
     */
532 32
    private function loadPuliPackages()
533
    {
534 32
        $packages = array();
535
536 32
        $output = $this->puliRunner->run('package --list --format %format%', array(
537 32
            'format' => '%name%;%installer%;%install_path%;%state%;%env%',
538
        ));
539
540
        // PuliRunner replaces \r\n by \n for those Windows boxes
541 31
        foreach (explode("\n", $output) as $packageLine) {
542 31
            if (!$packageLine) {
543 31
                continue;
544
            }
545
546 31
            $packageParts = explode(';', $packageLine);
547
548 31
            $packages[$packageParts[0]] = new PuliPackage(
549 31
                $packageParts[0],
550 31
                $packageParts[1],
551 31
                $packageParts[2],
552 31
                $packageParts[3],
553 31
                $packageParts[4]
554
            );
555
        }
556
557 31
        return $packages;
558
    }
559
560 11
    private function installPackage($installPath, $packageName, $env)
561
    {
562 11
        $env = PuliPackage::ENV_DEV === $env ? ' --dev' : '';
563
564 11
        $this->puliRunner->run('package --install %path% %package_name% --installer %installer%'.$env, array(
565 11
            'path' => $installPath,
566 11
            'package_name' => $packageName,
567 11
            'installer' => self::INSTALLER_NAME,
568
        ));
569 8
    }
570
571 7
    private function removePackage($packageName)
572
    {
573 7
        $this->puliRunner->run('package --delete %package_name%', array(
574 7
            'package_name' => $packageName,
575
        ));
576 5
    }
577
578 31
    private function removePuliDir()
579
    {
580 31
        $relativePuliDir = rtrim($this->getConfigKey('puli-dir'), '/');
581
582 31
        $puliDir = Path::makeAbsolute($relativePuliDir, $this->rootDir);
583
584
        // Only remove existing sub-directories of the root directory
585 31
        if (!file_exists($puliDir) || 0 !== strpos($puliDir, $this->rootDir.'/')) {
586 30
            return;
587
        }
588
589 1
        $this->io->write(sprintf('<info>Deleting the "%s" directory</info>', $relativePuliDir));
590
591
        // Remove the .puli directory to prevent upgrade problems
592 1
        $filesystem = new Filesystem();
593 1
        $filesystem->remove($puliDir);
594 1
    }
595
596 31
    private function buildPuli()
597
    {
598 31
        $this->io->write('<info>Running "puli build"</info>');
599
600 31
        $this->puliRunner->run('build');
601 31
    }
602
603 2
    private function renamePackage($name, $newName)
604
    {
605 2
        $this->puliRunner->run('package --rename %old_name% %new_name%', array(
606 2
            'old_name' => $name,
607 2
            'new_name' => $newName,
608
        ));
609 1
    }
610
611
    /**
612
     * @param                $message
613
     * @param Exception|null $exception
614
     */
615 13
    private function printWarning($message, Exception $exception = null)
616
    {
617 13
        if (!$exception) {
618 3
            $reasonPhrase = '';
619 11
        } elseif ($this->io->isVerbose()) {
620
            $reasonPhrase = $exception instanceof PuliRunnerException
621
                ? $exception->getFullError()
622
                : $exception->getMessage()."\n\n".$exception->getTraceAsString();
623
        } else {
624 11
            $reasonPhrase = $exception instanceof PuliRunnerException
625 9
                ? $exception->getShortError()
626 11
                : $exception->getMessage();
627
        }
628
629 13
        $this->io->writeError(sprintf(
630 13
            '<warning>Warning: %s%s</warning>',
631
            $message,
632 13
            $reasonPhrase ? ': '.$reasonPhrase : '.'
633
        ));
634 13
    }
635
636 7
    private function printPackageWarning($message, $packageName, $installPath, PuliRunnerException $exception = null)
637
    {
638 7
        $this->printWarning(sprintf(
639
            $message,
640
            $packageName,
641 7
            Path::makeRelative($installPath, $this->rootDir)
642
        ), $exception);
643 7
    }
644
645 32
    private function filterProdPackageNames(array $composerPackages, PackageInterface $package, array &$result = array())
646
    {
647
        // Resolve aliases
648 32
        if ($package instanceof AliasPackage) {
649 2
            $package = $package->getAliasOf();
650
        }
651
652
        // Package was processed already
653 32
        if (isset($result[$package->getName()])) {
654
            return $result;
655
        }
656
657 32
        $result[$package->getName()] = true;
658
659
        // Recursively filter package names
660 32
        foreach ($package->getRequires() as $packageName => $link) {
661 21
            if (isset($composerPackages[$packageName])) {
662 21
                $this->filterProdPackageNames($composerPackages, $composerPackages[$packageName], $result);
663
            }
664
        }
665
666 32
        return $result;
667
    }
668
669 43
    private function verifyPuliVersion()
670
    {
671 43
        $versionString = $this->puliRunner->run('-V');
672
673 43
        if (!preg_match('~^Puli version (\S+)$~', $versionString, $matches)) {
674
            throw new RuntimeException(sprintf(
675
                'Could not determine Puli version. "puli -V" returned: %s',
676
                $versionString
677
            ));
678
        }
679
680
        // the development build of the plugin is always considered compatible
681
        // with the development build of the CLI
682
        // Split strings to prevent replacement during release
683 43
        if ('@package_'.'version@' === self::VERSION && '@package_'.'version@' === $matches[1]) {
684 1
            return;
685
        }
686
687 42 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...
688 1
            throw new RuntimeException(sprintf(
689
                'Found an unsupported version of the Puli CLI: %s. Please '.
690
                'upgrade to version %s or higher. You can also install the '.
691 1
                'puli/cli dependency at version %s in your project.',
692 1
                $matches[1],
693 1
                self::MIN_CLI_VERSION,
694 1
                self::MIN_CLI_VERSION
695
            ));
696
        }
697
698 41 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...
699 1
            throw new RuntimeException(sprintf(
700
                'Found an unsupported version of the Puli CLI: %s. Please '.
701
                'downgrade to a lower version than %s. You can also install '.
702 1
                'the puli/cli dependency in your project.',
703 1
                $matches[1],
704 1
                self::MAX_CLI_VERSION
705
            ));
706
        }
707 40
    }
708
}
709