diff --git a/src/Plugin.php b/src/Plugin.php
index 1872a7b..4439d40 100644
--- a/src/Plugin.php
+++ b/src/Plugin.php
@@ -4,7 +4,6 @@
use Composer\Composer;
use Composer\DependencyResolver\Operation\InstallOperation;
-use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Pool;
use Composer\EventDispatcher\EventDispatcher;
use Composer\EventDispatcher\EventSubscriberInterface;
@@ -37,6 +36,9 @@ class Plugin implements PluginInterface, EventSubscriberInterface
/** @var Composer */
private $composer;
+ /** @var string[] */
+ private $installedPackages;
+
/** @var IOInterface */
private $io;
@@ -58,6 +60,11 @@ public function activate(Composer $composer, IOInterface $io)
{
$this->composer = $composer;
$this->io = $io;
+
+ $installedPackages = $this->composer->getRepositoryManager()->getLocalRepository()->getPackages();
+ foreach ($installedPackages as $package) {
+ $this->installedPackages[$package->getName()] = $package->getPrettyVersion();
+ }
}
public function onPostPackage(PackageEvent $event)
@@ -145,14 +152,28 @@ private function runInstaller(RootPackageInterface $rootPackage, array $packages
private function promptForPackageVersion($name)
{
- $validator = function ($input) {
- $input = trim($input);
- return $input ?: false;
- };
+ // Package is currently installed. Add it to root composer.json
+ if (isset($this->installedPackages[$name])) {
+ $this->io->write(sprintf(
+ 'Added package %s to composer.json with constraint %s;'
+ . ' to upgrade, run composer require %s:VERSION',
+ $name,
+ '^' . $this->installedPackages[$name],
+ $name
+ ));
+
+ return '^' . $this->installedPackages[$name];
+ }
$constraint = $this->io->askAndValidate(
- sprintf('Enter the version of %s to require (or leave blank to use the latest version): ', $name),
- $validator
+ sprintf(
+ 'Enter the version of %s to require (or leave blank to use the latest version): ',
+ $name
+ ),
+ function ($input) {
+ $input = trim($input);
+ return $input ?: false;
+ }
);
if ($constraint === false) {
@@ -184,11 +205,11 @@ private function createInstaller(Composer $composer, IOInterface $io, RootPackag
);
}
- private function hasPackage($name)
+ private function hasPackage($package)
{
$requires = $this->composer->getPackage()->getRequires();
- foreach ($requires as $link) {
- if (strtolower($link->getTarget()) === strtolower($name)) {
+ foreach ($requires as $name => $link) {
+ if (strtolower($name) === strtolower($package)) {
return true;
}
}
diff --git a/test/PluginTest.php b/test/PluginTest.php
index ba3a38e..288a8d3 100644
--- a/test/PluginTest.php
+++ b/test/PluginTest.php
@@ -18,6 +18,7 @@
use Composer\Package\RootPackageInterface;
use Composer\Package\Version\VersionSelector;
use Composer\Repository\RepositoryManager;
+use Composer\Repository\WritableRepositoryInterface;
use org\bovigo\vfs\vfsStream;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
@@ -37,11 +38,24 @@ class PluginTest extends TestCase
/** @var IOInterface|ObjectProphecy */
private $io;
+ /** @var WritableRepositoryInterface */
+ private $localRepository;
+
protected function setUp()
{
parent::setUp();
+ $this->localRepository = $this->prophesize(WritableRepositoryInterface::class);
+ $this->localRepository->getPackages()->willReturn([]);
+
+ $repositoryManager = $this->prophesize(RepositoryManager::class);
+ $repositoryManager->getLocalRepository()->willReturn($this->localRepository->reveal());
+
$this->composer = $this->prophesize(Composer::class);
+ $this->composer->getRepositoryManager()
+ ->willReturn($repositoryManager->reveal())
+ ->shouldBeCalled();
+
$this->io = $this->prophesize(IOInterface::class);
$this->plugin = new Plugin();
@@ -244,7 +258,8 @@ public function testInstallSingleDependencyOnPackageUpdate()
$this->composer->getConfig()->willReturn($config->reveal());
$this->io->askAndValidate(
- 'Enter the version of extra-dependency-foo to require (or leave blank to use the latest version): ',
+ 'Enter the version of extra-dependency-foo to require'
+ . ' (or leave blank to use the latest version): ',
Argument::type('callable')
)->willReturn('17.0.1-dev');
@@ -316,7 +331,8 @@ public function testInstallSingleDependencyOnPackageInstall()
$this->composer->getConfig()->willReturn($config->reveal());
$this->io->askAndValidate(
- 'Enter the version of extra-dependency-foo to require (or leave blank to use the latest version): ',
+ 'Enter the version of extra-dependency-foo to require'
+ . ' (or leave blank to use the latest version): ',
Argument::type('callable')
)->willReturn('17.0.1-dev');
@@ -396,7 +412,8 @@ public function testInstallOneDependenciesWhenOneIsAlreadyInstalled()
$this->composer->getConfig()->willReturn($config->reveal());
$this->io->askAndValidate(
- 'Enter the version of extra-dependency-foo to require (or leave blank to use the latest version): ',
+ 'Enter the version of extra-dependency-foo to require'
+ . ' (or leave blank to use the latest version): ',
Argument::type('callable')
)->willReturn('17.0.1-dev');
@@ -474,7 +491,8 @@ public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersion()
$this->composer->getConfig()->willReturn($config->reveal());
$this->io->askAndValidate(
- 'Enter the version of extra-dependency-foo to require (or leave blank to use the latest version): ',
+ 'Enter the version of extra-dependency-foo to require'
+ . ' (or leave blank to use the latest version): ',
Argument::type('callable')
)->willReturn(false);
@@ -500,6 +518,11 @@ public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersion()
$this->setUpPool();
$this->assertNull($this->plugin->onPostPackage($event->reveal()));
+
+ $json = file_get_contents(vfsStream::url('project/composer.json'));
+ $composer = json_decode($json, true);
+ $this->assertTrue(isset($composer['require']['extra-dependency-foo']));
+ $this->assertSame('13.4.2', $composer['require']['extra-dependency-foo']);
}
public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersionNotFoundMatchingPackage()
@@ -532,7 +555,8 @@ public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersionNo
$this->composer->getConfig()->willReturn($config->reveal());
$this->io->askAndValidate(
- 'Enter the version of extra-dependency-foo to require (or leave blank to use the latest version): ',
+ 'Enter the version of extra-dependency-foo to require'
+ . ' (or leave blank to use the latest version): ',
Argument::type('callable')
)->willReturn(false);
@@ -551,6 +575,82 @@ public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersionNo
$this->plugin->onPostPackage($event->reveal());
}
+ public function testUpdateComposerWithCurrentlyInstalledVersion()
+ {
+ $installedPackage = $this->prophesize(PackageInterface::class);
+ $installedPackage->getName()->willReturn('extra-dependency-foo');
+ $installedPackage->getPrettyVersion()->willReturn('0.5.1');
+
+ $this->localRepository->getPackages()->willReturn([$installedPackage->reveal()]);
+ $this->plugin->activate($this->composer->reveal(), $this->io->reveal());
+
+ /** @var PackageInterface|ObjectProphecy $package */
+ $package = $this->prophesize(PackageInterface::class);
+ $package->getName()->willReturn('some/component');
+ $package->getExtra()->willReturn([
+ 'dependency' => [
+ 'extra-dependency-foo',
+ ],
+ ]);
+
+ $operation = $this->prophesize(InstallOperation::class);
+ $operation->getPackage()->willReturn($package->reveal());
+
+ $event = $this->prophesize(PackageEvent::class);
+ $event->isDevMode()->willReturn(true);
+ $event->getOperation()->willReturn($operation->reveal());
+
+ $config = $this->prophesize(Config::class);
+ $config->get('sort-packages')->willReturn(true);
+ $config->get(Argument::any())->willReturn(null);
+
+ $rootPackage = $this->prophesize(RootPackageInterface::class);
+ $rootPackage->getRequires()->willReturn([]);
+ $rootPackage->setRequires(Argument::that(function ($arguments) {
+ if (! is_array($arguments)) {
+ return false;
+ }
+
+ if (! isset($arguments['extra-dependency-foo'])) {
+ return false;
+ }
+
+ $argument = $arguments['extra-dependency-foo'];
+
+ if (! $argument instanceof Link) {
+ return false;
+ }
+
+ if ($argument->getTarget() !== 'extra-dependency-foo') {
+ return false;
+ }
+
+ if ($argument->getConstraint()->getPrettyString() !== '^0.5.1') {
+ return false;
+ }
+
+ if ($argument->getDescription() !== 'requires') {
+ return false;
+ }
+
+ return true;
+ }))->shouldBeCalled();
+ $rootPackage->getMinimumStability()->willReturn('stable');
+
+ $this->composer->getPackage()->willReturn($rootPackage);
+ $this->composer->getConfig()->willReturn($config->reveal());
+
+ $this->setUpComposerInstaller(['extra-dependency-foo']);
+ $this->setUpComposerJson();
+
+ $this->assertNull($this->plugin->onPostPackage($event->reveal()));
+
+ $json = file_get_contents(vfsStream::url('project/composer.json'));
+ $composer = json_decode($json, true);
+ $this->assertTrue(isset($composer['require']['extra-dependency-foo']));
+ $this->assertSame('^0.5.1', $composer['require']['extra-dependency-foo']);
+ }
+
public function testComposerInstallerFactory()
{
$r = new ReflectionProperty($this->plugin, 'installerFactory');
@@ -563,9 +663,6 @@ public function testComposerInstallerFactory()
$this->composer->getDownloadManager()
->willReturn($this->prophesize(DownloadManager::class))
->shouldBeCalled();
- $this->composer->getRepositoryManager()
- ->willReturn($this->prophesize(RepositoryManager::class))
- ->shouldBeCalled();
$this->composer->getLocker()
->willReturn($this->prophesize(Locker::class))
->shouldBeCalled();