forked from symfony/flex
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature symfony#241 Add support for unpacking Composer packages (fabpot)
This PR was squashed before being merged into the 1.0-dev branch (closes symfony#241). Discussion ---------- Add support for unpacking Composer packages This PR is short but packed (pun intended) with great features. Basically, it helps fix an issue people have with Flex: starting a new project. Commits ------- e4bdd74 Add support for unpacking Composer packages
- Loading branch information
Showing
6 changed files
with
319 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Flex\Command; | ||
|
||
use Composer\Command\BaseCommand; | ||
use Composer\Config\JsonConfigSource; | ||
use Composer\Factory; | ||
use Composer\Installer; | ||
use Composer\Json\JsonFile; | ||
use Composer\Package\Locker; | ||
use Composer\Package\Version\VersionParser; | ||
use Symfony\Component\Console\Input\InputArgument; | ||
use Symfony\Component\Console\Input\InputInterface; | ||
use Symfony\Component\Console\Input\InputOption; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
use Symfony\Flex\PackageResolver; | ||
use Symfony\Flex\Unpacker; | ||
use Symfony\Flex\Unpack\Operation; | ||
|
||
class UnpackCommand extends BaseCommand | ||
{ | ||
public function __construct(PackageResolver $resolver) | ||
{ | ||
$this->resolver = $resolver; | ||
|
||
parent::__construct(); | ||
} | ||
|
||
protected function configure() | ||
{ | ||
$this->setName('unpack') | ||
->setDescription('Unpack a Symfony pack.') | ||
->setDefinition(array( | ||
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Installed packages to unpack.'), | ||
new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages'), | ||
)) | ||
; | ||
} | ||
|
||
protected function execute(InputInterface $input, OutputInterface $output) | ||
{ | ||
$composer = $this->getComposer(); | ||
$packages = $this->resolver->resolve($input->getArgument('packages'), true); | ||
$io = $this->getIo(); | ||
$json = new JsonFile(Factory::getComposerFile()); | ||
$manipulator = new JsonConfigSource($json); | ||
$locker = $composer->getLocker(); | ||
$lockData = $locker->getLockData(); | ||
$installedRepo = $composer->getRepositoryManager()->getLocalRepository(); | ||
$versionParser = new VersionParser(); | ||
|
||
$op = new Operation(true, $input->getOption('sort-packages') || $composer->getConfig()->get('sort-packages')); | ||
foreach ($versionParser->parseNameVersionPairs($packages) as $package) { | ||
if (null === $pkg = $installedRepo->findPackage($package['name'], '*')) { | ||
$io->writeError(sprintf('<error>Package %s is not installed</>', $package['name'])); | ||
|
||
return 1; | ||
} | ||
|
||
$dev = false; | ||
foreach ($lockData['packages-dev'] as $p) { | ||
if ($package['name'] === $p['name']) { | ||
$dev = true; | ||
|
||
break; | ||
} | ||
} | ||
|
||
$op->addPackage($package['name'], '*', $dev); | ||
} | ||
|
||
$unpacker = new Unpacker($composer); | ||
$result = $unpacker->unpack($op); | ||
|
||
// remove the packages themselves | ||
if (!$result->getUnpacked()) { | ||
$io->writeError('<info>Nothing to unpack</>'); | ||
return; | ||
} | ||
|
||
foreach ($result->getUnpacked() as $pkg) { | ||
$io->writeError(sprintf('<info>Unpacked %s dependencies</>', $pkg->getName())); | ||
} | ||
|
||
foreach ($result->getUnpacked() as $package) { | ||
$manipulator->removeLink('require-dev', $package->getName()); | ||
foreach ($lockData['packages-dev'] as $i => $pkg) { | ||
if ($package->getName() === $pkg['name']) { | ||
unset($lockData['packages-dev'][$i]); | ||
} | ||
} | ||
$manipulator->removeLink('require', $package->getName()); | ||
foreach ($lockData['packages'] as $i => $pkg) { | ||
if ($package->getName() === $pkg['name']) { | ||
unset($lockData['packages'][$i]); | ||
} | ||
} | ||
} | ||
$lockData['packages'] = array_values($lockData['packages']); | ||
$lockData['packages-dev'] = array_values($lockData['packages-dev']); | ||
$lockData['content-hash'] = $locker->getContentHash(file_get_contents($json->getPath())); | ||
$lockFile = new JsonFile(substr($json->getPath(), 0, -4).'lock', null, $io); | ||
$lockFile->write($lockData); | ||
|
||
// force removal of files under vendor/ | ||
$locker = new Locker($io, $lockFile, $composer->getRepositoryManager(), $composer->getInstallationManager(), file_get_contents($json->getPath())); | ||
$composer->setLocker($locker); | ||
$install = Installer::create($io, $composer); | ||
$install | ||
->setDevMode(true) | ||
->setDumpAutoloader(false) | ||
->setRunScripts(false) | ||
->setSkipSuggest(true) | ||
->setIgnorePlatformRequirements(true) | ||
; | ||
|
||
return $install->run(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Flex\Unpack; | ||
|
||
class Operation | ||
{ | ||
private $packages; | ||
private $unpack; | ||
private $sort; | ||
|
||
public function __construct(bool $unpack, bool $sort) | ||
{ | ||
$this->unpack = $unpack; | ||
$this->sort = $sort; | ||
} | ||
|
||
public function addPackage(string $name, string $version, bool $dev) | ||
{ | ||
$this->packages[] = [ | ||
'name' => $name, | ||
'version' => $version, | ||
'dev' => $dev, | ||
]; | ||
} | ||
|
||
public function getPackages(): array | ||
{ | ||
return $this->packages; | ||
} | ||
|
||
public function shouldUnpack(): bool | ||
{ | ||
return $this->unpack; | ||
} | ||
|
||
public function shouldSort(): bool | ||
{ | ||
return $this->sort; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Flex\Unpack; | ||
|
||
use Composer\Package\PackageInterface; | ||
|
||
class Result | ||
{ | ||
private $unpacked = []; | ||
private $required = []; | ||
|
||
public function addUnpacked(PackageInterface $package) | ||
{ | ||
$this->unpacked[] = $package; | ||
} | ||
|
||
/** | ||
* @return PackageInterface[] | ||
*/ | ||
public function getUnpacked(): array | ||
{ | ||
return $this->unpacked; | ||
} | ||
|
||
public function addRequired(string $package) | ||
{ | ||
$this->required[] = $package; | ||
} | ||
|
||
/** | ||
* @return string[] | ||
*/ | ||
public function getRequired(): array | ||
{ | ||
// we need at least one package for the command to work properly | ||
return $this->required ?: ['symfony/flex']; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Flex; | ||
|
||
use Composer\Composer; | ||
use Composer\Factory; | ||
use Composer\Json\JsonFile; | ||
use Composer\Json\JsonManipulator; | ||
use Composer\Package\Link; | ||
use Composer\Package\Package; | ||
use Symfony\Flex\PackageResolver; | ||
use Symfony\Flex\Unpack\Operation; | ||
use Symfony\Flex\Unpack\Result; | ||
|
||
class Unpacker | ||
{ | ||
private $composer; | ||
|
||
public function __construct(Composer $composer) | ||
{ | ||
$this->composer = $composer; | ||
} | ||
|
||
public function unpack(Operation $op): Result | ||
{ | ||
$result = new Result(); | ||
$json = new JsonFile(Factory::getComposerFile()); | ||
$manipulator = new JsonManipulator(file_get_contents($json->getPath())); | ||
foreach ($op->getPackages() as $package) { | ||
$pkg = $this->composer->getRepositoryManager()->findPackage($package['name'], $package['version'] ?: '*'); | ||
|
||
// not unpackable or no --unpack flag or empty packs (markers) | ||
if ( | ||
'symfony-pack' !== $pkg->getType() || | ||
!$op->shouldUnpack() || | ||
0 === count($pkg->getRequires()) + count($pkg->getDevRequires()) | ||
) { | ||
$result->addRequired($package['name'].($package['version'] ? ':'.$package['version'] : '')); | ||
|
||
continue; | ||
} | ||
|
||
$result->addUnpacked($pkg); | ||
foreach ($pkg->getRequires() as $link) { | ||
if ('php' === $link->getTarget()) { | ||
continue; | ||
} | ||
|
||
if (!$manipulator->addLink($package['dev'] ? 'require-dev' : 'require', $link->getTarget(), $link->getPrettyConstraint(), $op->shouldSort())) { | ||
throw new \RuntimeException(sprintf('Unable to unpack package "%s".', $link->getTarget())); | ||
} | ||
} | ||
} | ||
|
||
file_put_contents($json->getPath(), $manipulator->getContents()); | ||
|
||
return $result; | ||
} | ||
} |