Skip to content

Commit a8b88c4

Browse files
Fix unpack logic
1 parent ccc4d2d commit a8b88c4

File tree

8 files changed

+95
-37
lines changed

8 files changed

+95
-37
lines changed

src/Downloader.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Composer\DependencyResolver\Operation\UpdateOperation;
1919
use Composer\IO\IOInterface;
2020
use Composer\Json\JsonFile;
21+
use Composer\Package\BasePackage;
2122
use Composer\Util\Http\Response as ComposerResponse;
2223
use Composer\Util\HttpDownloader;
2324
use Composer\Util\Loop;
@@ -316,6 +317,27 @@ public function removeRecipeFromIndex(string $packageName, string $version)
316317
unset($this->index[$packageName][$version]);
317318
}
318319

320+
public function getSymfonyPacks(array $packages)
321+
{
322+
$packs = [];
323+
foreach ($this->composer->getRepositoryManager()->getRepositories() as $repo) {
324+
if (!$packages) {
325+
break;
326+
}
327+
328+
$result = $repo->loadPackages($packages, BasePackage::$stabilities, []);
329+
330+
foreach ($result['packages'] ?? [] as $package) {
331+
if ('symfony-pack' === $package->getType()) {
332+
$packs[$package->getName()] = true;
333+
}
334+
unset($packages[$package->getName()]);
335+
}
336+
}
337+
338+
return array_keys($packs);
339+
}
340+
319341
/**
320342
* Fetches and decodes JSON HTTP response bodies.
321343
*/

src/Flex.php

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,12 @@ class Flex implements PluginInterface, EventSubscriberInterface
7777
private $operations = [];
7878
private $lock;
7979
private $displayThanksReminder = 0;
80-
private $dryRun = false;
8180
private $reinstall;
8281
private static $activated = true;
8382
private static $aliasResolveCommands = [
8483
'require' => true,
8584
'update' => false,
8685
'remove' => false,
87-
'unpack' => true,
8886
];
8987
private $filter;
9088

@@ -108,6 +106,8 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__)
108106
}
109107
}
110108

109+
$composer->getInstallationManager()->addInstaller(new SymfonyPackInstaller($io));
110+
111111
$this->composer = $composer;
112112
$this->io = $io;
113113
$this->config = $composer->getConfig();
@@ -122,7 +122,7 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__)
122122

123123
$symfonyRequire = preg_replace('/\.x$/', '.x-dev', getenv('SYMFONY_REQUIRE') ?: ($composer->getPackage()->getExtra()['symfony']['require'] ?? ''));
124124

125-
$rfs = Factory::createHttpDownloader($this->io, $this->config);
125+
$rfs = $composer->getLoop()->getHttpDownloader();
126126

127127
$this->downloader = $downloader = new Downloader($composer, $io, $rfs);
128128

@@ -221,14 +221,6 @@ public function configureInstaller()
221221
foreach ($backtrace as $trace) {
222222
if (isset($trace['object']) && $trace['object'] instanceof Installer) {
223223
$this->installer = $trace['object']->setSuggestedPackagesReporter(new SuggestedPackagesReporter(new NullIO()));
224-
225-
$updateAllowList = \Closure::bind(function () {
226-
return $this->updateAllowList;
227-
}, $this->installer, $this->installer)();
228-
229-
if (['php' => 0] === $updateAllowList) {
230-
$this->dryRun = true; // prevent recipes from being uninstalled when removing a pack
231-
}
232224
}
233225

234226
if (isset($trace['object']) && $trace['object'] instanceof GlobalCommand) {
@@ -254,7 +246,6 @@ public function configureProject(Event $event)
254246
$file = Factory::getComposerFile();
255247
$contents = file_get_contents($file);
256248
$manipulator = new JsonManipulator($contents);
257-
$json = JsonFile::parseJson($contents);
258249

259250
// new projects are most of the time proprietary
260251
$manipulator->addMainKey('license', 'proprietary');
@@ -351,7 +342,7 @@ public function update(Event $event, $operations = [])
351342

352343
file_put_contents($file, $manipulator->getContents());
353344

354-
$this->reinstall($event, true);
345+
$this->reinstall($event);
355346
}
356347

357348
public function install(Event $event)
@@ -736,9 +727,9 @@ private function formatOrigin(Recipe $recipe): string
736727
return \sprintf('<info>%s</> (<comment>>=%s</>): From %s', $matches[1], $matches[2], 'auto-generated recipe' === $matches[3] ? '<comment>'.$matches[3].'</>' : $matches[3]);
737728
}
738729

739-
private function shouldRecordOperation(OperationInterface $operation, bool $isDevMode, ?Composer $composer = null): bool
730+
private function shouldRecordOperation(OperationInterface $operation, bool $isDevMode, Composer $composer): bool
740731
{
741-
if ($this->dryRun || $this->reinstall) {
732+
if ($this->reinstall) {
742733
return false;
743734
}
744735

@@ -750,7 +741,7 @@ private function shouldRecordOperation(OperationInterface $operation, bool $isDe
750741

751742
// when Composer runs with --no-dev, ignore uninstall operations on packages from require-dev
752743
if (!$isDevMode && $operation instanceof UninstallOperation) {
753-
foreach (($composer ?? $this->composer)->getLocker()->getLockData()['packages-dev'] as $p) {
744+
foreach ($composer->getLocker()->getLockData()['packages-dev'] as $p) {
754745
if ($package->getName() === $p['name']) {
755746
return false;
756747
}
@@ -794,7 +785,7 @@ private function unpack(Event $event)
794785
}
795786
}
796787

797-
$unpacker = new Unpacker($this->composer, new PackageResolver($this->downloader), $this->dryRun);
788+
$unpacker = new Unpacker($this->composer, new PackageResolver($this->downloader));
798789
$result = $unpacker->unpack($unpackOp);
799790

800791
if (!$result->getUnpacked()) {
@@ -807,18 +798,17 @@ private function unpack(Event $event)
807798
}
808799

809800
$unpacker->updateLock($result, $this->io);
810-
811-
$this->reinstall($event, false);
812801
}
813802

814-
private function reinstall(Event $event, bool $update)
803+
private function reinstall(Event $event)
815804
{
816805
$this->reinstall = false;
817806
$event->stopPropagation();
818807

819808
$ed = $this->composer->getEventDispatcher();
820809
$disableScripts = !method_exists($ed, 'setRunScripts') || !((array) $ed)["\0*\0runScripts"];
821810
$composer = Factory::create($this->io, null, false, $disableScripts);
811+
$composer->getInstallationManager()->addInstaller(new SymfonyPackInstaller($this->io));
822812

823813
$installer = clone $this->installer;
824814
$installer->__construct(
@@ -836,10 +826,6 @@ private function reinstall(Event $event, bool $update)
836826
$installer->setPlatformRequirementFilter(((array) $this->installer)["\0*\0platformRequirementFilter"]);
837827
}
838828

839-
if (!$update) {
840-
$installer->setUpdateAllowList(['php']);
841-
}
842-
843829
$installer->run();
844830

845831
$this->io->write($this->postInstallOutput);

src/PackageResolver.php

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Composer\Factory;
1515
use Composer\Package\Version\VersionParser;
1616
use Composer\Repository\PlatformRepository;
17+
use Composer\Semver\Constraint\MatchAllConstraint;
1718

1819
/**
1920
* @author Fabien Potencier <fabien@symfony.com>
@@ -45,26 +46,41 @@ public function resolve(array $arguments = [], bool $isRequire = false): array
4546
// second pass to resolve versions
4647
$versionParser = new VersionParser();
4748
$requires = [];
49+
$toGuess = [];
4850
foreach ($versionParser->parseNameVersionPairs($packages) as $package) {
49-
$requires[] = $package['name'].$this->parseVersion($package['name'], $package['version'] ?? '', $isRequire);
51+
$version = $this->parseVersion($package['name'], $package['version'] ?? '', $isRequire);
52+
if ('' !== $version) {
53+
unset($toGuess[$package['name']]);
54+
} elseif (!isset($requires[$package['name']])) {
55+
$toGuess[$package['name']] = new MatchAllConstraint();
56+
}
57+
$requires[$package['name']] = $package['name'].$version;
5058
}
5159

52-
return array_unique($requires);
60+
if ($toGuess && $isRequire) {
61+
foreach ($this->downloader->getSymfonyPacks($toGuess) as $package) {
62+
$requires[$package] .= ':*';
63+
}
64+
}
65+
66+
return array_values($requires);
5367
}
5468

5569
public function parseVersion(string $package, string $version, bool $isRequire): string
5670
{
71+
$guess = 'guess' === ($version ?: 'guess');
72+
5773
if (0 !== strpos($package, 'symfony/')) {
58-
return $version ? ':'.$version : '';
74+
return $guess ? '' : ':'.$version;
5975
}
6076

6177
$versions = $this->downloader->getVersions();
6278

6379
if (!isset($versions['splits'][$package])) {
64-
return $version ? ':'.$version : '';
80+
return $guess ? '': ':'.$version;
6581
}
6682

67-
if (!$version || '*' === $version) {
83+
if ($guess || '*' === $version) {
6884
try {
6985
$config = @json_decode(file_get_contents(Factory::getComposerFile()), true);
7086
} finally {

src/SymfonyPackInstaller.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Flex;
13+
14+
use Composer\Installer\MetapackageInstaller;
15+
16+
class SymfonyPackInstaller extends MetapackageInstaller
17+
{
18+
public function supports($packageType): bool
19+
{
20+
return 'symfony-pack' === $packageType;
21+
}
22+
}

src/Unpacker.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,12 @@ class Unpacker
2929
{
3030
private $composer;
3131
private $resolver;
32-
private $dryRun;
3332
private $versionParser;
3433

35-
public function __construct(Composer $composer, PackageResolver $resolver, bool $dryRun)
34+
public function __construct(Composer $composer, PackageResolver $resolver)
3635
{
3736
$this->composer = $composer;
3837
$this->resolver = $resolver;
39-
$this->dryRun = $dryRun;
4038
$this->versionParser = new VersionParser();
4139
}
4240

@@ -131,7 +129,7 @@ public function unpack(Operation $op, ?Result $result = null, &$links = [], bool
131129
}
132130
}
133131

134-
if ($this->dryRun || 1 < \func_num_args()) {
132+
if (1 < \func_num_args()) {
135133
return $result;
136134
}
137135

@@ -140,6 +138,12 @@ public function unpack(Operation $op, ?Result $result = null, &$links = [], bool
140138
$jsonStored = json_decode($jsonContent, true);
141139
$jsonManipulator = new JsonManipulator($jsonContent);
142140

141+
foreach ($result->getUnpacked() as $pkg) {
142+
$localRepo->removePackage($pkg);
143+
$jsonManipulator->removeSubNode('require', $pkg->getName());
144+
$jsonManipulator->removeSubNode('require-dev', $pkg->getName());
145+
}
146+
143147
foreach ($links as $link) {
144148
// nothing to do, package is already present in the "require" section
145149
if (isset($jsonStored['require'][$link['name']])) {
@@ -197,9 +201,7 @@ public function updateLock(Result $result, IOInterface $io): void
197201
$lockData['content-hash'] = Locker::getContentHash($jsonContent);
198202
$lockFile = new JsonFile(substr($json->getPath(), 0, -4).'lock', null, $io);
199203

200-
if (!$this->dryRun) {
201-
$lockFile->write($lockData);
202-
}
204+
$lockFile->write($lockData);
203205

204206
// force removal of files under vendor/
205207
$locker = new Locker($io, $lockFile, $this->composer->getInstallationManager(), $jsonContent);

tests/FlexTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Composer\Config;
1616
use Composer\DependencyResolver\Operation\InstallOperation;
1717
use Composer\Factory;
18+
use Composer\Installer\InstallationManager;
1819
use Composer\Installer\PackageEvent;
1920
use Composer\IO\BufferIO;
2021
use Composer\Package\Link;
@@ -29,6 +30,8 @@
2930
use Composer\Script\Event;
3031
use Composer\Script\ScriptEvents;
3132
use Composer\Semver\Constraint\MatchAllConstraint;
33+
use Composer\Util\HttpDownloader;
34+
use Composer\Util\Loop;
3235
use PHPUnit\Framework\TestCase;
3336
use Symfony\Component\Console\Output\OutputInterface;
3437
use Symfony\Flex\Configurator;
@@ -458,6 +461,10 @@ private function mockComposer(Locker $locker, RootPackageInterface $package, ?Co
458461
$composer->setConfig($config);
459462
$composer->setLocker($locker);
460463
$composer->setPackage($package);
464+
$composer->setInstallationManager($this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock());
465+
466+
$loop = new Loop(new HttpDownloader(new BufferIO('', OutputInterface::VERBOSITY_VERBOSE), $config));
467+
$composer->setLoop($loop);
461468

462469
return $composer;
463470
}

tests/PackageResolverTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ private function getResolver()
128128
'validator' => 'symfony/validator',
129129
'lock' => 'symfony/lock',
130130
]);
131+
$downloader->expects($this->any())
132+
->method('getSymfonyPacks')
133+
->willReturn([]);
131134

132135
return new PackageResolver($downloader);
133136
}

tests/UnpackerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public function testDoNotDuplicateEntry(): void
7171

7272
$resolver = $this->getMockBuilder(PackageResolver::class)->disableOriginalConstructor()->getMock();
7373

74-
$unpacker = new Unpacker($composer, $resolver, false);
74+
$unpacker = new Unpacker($composer, $resolver);
7575

7676
$operation = new Operation(true, false);
7777
$operation->addPackage('pack_foo', '*', false);

0 commit comments

Comments
 (0)