Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/Merge/ExtraPackage.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,19 @@ public function mergeInto(RootPackageInterface $root, PluginState $state)
$this->mergeReferences($root);
}

/**
* Merge just the dev portion into a RootPackageInterface
*
* @param RootPackageInterface $root
* @param PluginState $state
*/
public function mergeDev(RootPackageInterface $root, PluginState $state)
{
$this->mergeRequires('require-dev', $root, $state);
$this->mergeAutoload('devAutoload', $root);
$this->mergeReferences($root);
}

/**
* Add a collection of repositories described by the given configuration
* to the given package and the global repository manager.
Expand Down
69 changes: 57 additions & 12 deletions src/MergePlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

use Composer\Composer;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\EventDispatcher\Event as BaseEvent;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Factory;
use Composer\Installer;
Expand All @@ -26,7 +27,7 @@
use Composer\IO\IOInterface;
use Composer\Package\RootPackageInterface;
use Composer\Plugin\PluginInterface;
use Composer\Script\Event;
use Composer\Script\Event as ScriptEvent;
use Composer\Script\ScriptEvents;

/**
Expand Down Expand Up @@ -86,6 +87,11 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
*/
const PACKAGE_NAME = 'wikimedia/composer-merge-plugin';

/**
* Name of the composer 1.1 init event.
*/
const COMPAT_PLUGINEVENTS_INIT = 'init';

/**
* @var Composer $composer
*/
Expand All @@ -102,12 +108,19 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
protected $logger;

/**
* Files that have already been processed
* Files that have already been fully processed
*
* @var string[] $loadedFiles
*/
protected $loadedFiles = array();

/**
* Files that have already been partially processed
*
* @var string[] $partiallyLoadedFiles
*/
protected $partiallyLoadedFiles = array();

/**
* {@inheritdoc}
*/
Expand All @@ -124,6 +137,10 @@ public function activate(Composer $composer, IOInterface $io)
public static function getSubscribedEvents()
{
return array(
// Use our own constant to make this event optional. Once
// composer-1.1 is required, this can use PluginEvents::INIT
// instead.
self::COMPAT_PLUGINEVENTS_INIT => 'onInit',
InstallerEvents::PRE_DEPENDENCIES_SOLVING => 'onDependencySolve',
PackageEvents::POST_PACKAGE_INSTALL => 'onPostPackageInstall',
ScriptEvents::POST_INSTALL_CMD => 'onPostInstallOrUpdate',
Expand All @@ -134,14 +151,30 @@ public static function getSubscribedEvents()
);
}

/**
* Handle an event callback for initialization.
*
* @param \Composer\EventDispatcher\Event $event
*/
public function onInit(BaseEvent $event)
{
$this->state->loadSettings();
// It is not possible to know if the user specified --dev or --no-dev
// so assume it is false. The dev section will be merged later when
// the other events fire.
$this->state->setDevMode(false);
$this->mergeFiles($this->state->getIncludes(), false);
$this->mergeFiles($this->state->getRequires(), true);
}

/**
* Handle an event callback for an install, update or dump command by
* checking for "merge-plugin" in the "extra" data and merging package
* contents if found.
*
* @param Event $event
* @param ScriptEvent $event
*/
public function onInstallUpdateOrDump(Event $event)
public function onInstallUpdateOrDump(ScriptEvent $event)
{
$this->state->loadSettings();
$this->state->setDevMode($event->isDevMode());
Expand Down Expand Up @@ -196,16 +229,28 @@ function ($files, $pattern) use ($required) {
*/
protected function mergeFile(RootPackageInterface $root, $path)
{
if (isset($this->loadedFiles[$path])) {
$this->logger->debug("Already merged <comment>$path</comment>");
if (isset($this->loadedFiles[$path]) ||
(isset($this->partiallyLoadedFiles[$path]) && !$this->state->isDevMode())) {
$this->logger->debug("Already merged <comment>$path</comment> completely");
return;
} else {
$this->loadedFiles[$path] = true;
}
$this->logger->info("Loading <comment>{$path}</comment>...");

$package = new ExtraPackage($path, $this->composer, $this->logger);
$package->mergeInto($root, $this->state);

// If something was already loaded, merge just the dev section.
if (isset($this->partiallyLoadedFiles[$path])) {
$this->logger->info("Loading -dev sections of <comment>{$path}</comment>...");
$package->mergeDev($root, $this->state);
} else {
$this->logger->info("Loading <comment>{$path}</comment>...");
$package->mergeInto($root, $this->state);
}

if ($this->state->isDevMode()) {
$this->loadedFiles[$path] = true;
} else {
$this->partiallyLoadedFiles[$path] = true;
}

if ($this->state->recurseIncludes()) {
$this->mergeFiles($package->getIncludes(), false);
Expand Down Expand Up @@ -266,9 +311,9 @@ public function onPostPackageInstall(PackageEvent $event)
* plugin was installed during the run then trigger an update command to
* process any merge-patterns in the current config.
*
* @param Event $event
* @param ScriptEvent $event
*/
public function onPostInstallOrUpdate(Event $event)
public function onPostInstallOrUpdate(ScriptEvent $event)
{
// @codeCoverageIgnoreStart
if ($this->state->isFirstInstall()) {
Expand Down
65 changes: 58 additions & 7 deletions tests/phpunit/MergePluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use Composer\Package\Package;
use Composer\Package\RootPackage;
use Composer\Package\Version\VersionParser;
use Composer\Plugin\PluginEvents;
use Composer\Script\Event;
use Composer\Script\ScriptEvents;
use Prophecy\Argument;
Expand Down Expand Up @@ -71,11 +72,12 @@ protected function setUp()
public function testSubscribedEvents()
{
$subscriptions = MergePlugin::getSubscribedEvents();
$this->assertEquals(7, count($subscriptions));
$this->assertEquals(8, count($subscriptions));
$this->assertArrayHasKey(
InstallerEvents::PRE_DEPENDENCIES_SOLVING,
$subscriptions
);
$this->assertArrayHasKey(MergePlugin::COMPAT_PLUGINEVENTS_INIT, $subscriptions);
$this->assertArrayHasKey(ScriptEvents::PRE_INSTALL_CMD, $subscriptions);
$this->assertArrayHasKey(ScriptEvents::PRE_UPDATE_CMD, $subscriptions);
$this->assertArrayHasKey(ScriptEvents::PRE_AUTOLOAD_DUMP, $subscriptions);
Expand Down Expand Up @@ -306,6 +308,13 @@ function ($args) use ($that) {
$this->assertEquals(2, count($extraInstalls));
$this->assertEquals('monolog/monolog', $extraInstalls[0][0]);
$this->assertEquals('foo', $extraInstalls[1][0]);

$extraInstalls = $this->triggerPlugin($root->reveal(), $dir, true);

$this->assertEquals(2, count($extraInstalls));
$this->assertEquals('monolog/monolog', $extraInstalls[0][0]);
$this->assertEquals('foo', $extraInstalls[1][0]);

}


Expand Down Expand Up @@ -386,6 +395,10 @@ function ($args) use ($that) {
$extraInstalls = $this->triggerPlugin($root->reveal(), $dir);

$this->assertEquals(0, count($extraInstalls));

$extraInstalls = $this->triggerPlugin($root->reveal(), $dir, true);

$this->assertEquals(0, count($extraInstalls));
}


Expand Down Expand Up @@ -444,6 +457,10 @@ function ($args) use ($that, &$expects) {
$extraInstalls = $this->triggerPlugin($root->reveal(), $dir);

$this->assertEquals(0, count($extraInstalls));

$extraInstalls = $this->triggerPlugin($root->reveal(), $dir, true);

$this->assertEquals(0, count($extraInstalls));
}


Expand Down Expand Up @@ -747,6 +764,7 @@ function () use ($repoManager) {
$alias = $alias->reveal();

$this->triggerPlugin($alias, $dir);
$this->triggerPlugin($alias, $dir, true);
}


Expand Down Expand Up @@ -942,6 +960,10 @@ function ($args) use ($that) {

$extraInstalls = $this->triggerPlugin($root->reveal(), $dir);
$this->assertEquals(0, count($extraInstalls));

$extraInstalls = $this->triggerPlugin($root->reveal(), $dir, true);
$this->assertEquals(0, count($extraInstalls));

}


Expand Down Expand Up @@ -998,34 +1020,63 @@ function ($args, $root) {
}
)->shouldBeCalled();

$second_call = function ($args) use ($that) {
$references = $args[0];
$that->assertEquals(3, count($references));

$that->assertArrayHasKey('foo/bar', $references);
$that->assertArrayHasKey('monolog/monolog', $references);
$that->assertArrayHasKey('foo/baz', $references);

$that->assertSame($references['foo/bar'], '1234567');
$that->assertSame($references['monolog/monolog'], 'cb641a8');
$that->assertSame($references['foo/baz'], 'abc1234');
};

$root->setReferences(Argument::type('array'))->will($second_call);
$extraInstalls = $this->triggerPlugin($root->reveal(), $dir);

// Test with onInit event.
$root->setReferences(Argument::type('array'))->will(
function ($args) use ($that) {
function ($args) use ($that, $root, $second_call) {
$references = $args[0];
$that->assertEquals(3, count($references));
$that->assertEquals(2, count($references));

$that->assertArrayHasKey('foo/bar', $references);
$that->assertArrayHasKey('monolog/monolog', $references);
$that->assertArrayHasKey('foo/baz', $references);

$that->assertSame($references['foo/bar'], '1234567');
$that->assertSame($references['monolog/monolog'], 'cb641a8');
$that->assertSame($references['foo/baz'], 'abc1234');

// onInit does parse without require-dev, so this is called a
// second time when onInstallUpdateOrDump() fires with the dev
// section parsed as well.
$root->setReferences(Argument::type('array'))->will($second_call);
}
);
$extraInstalls = $this->triggerPlugin($root->reveal(), $dir);

$extraInstalls = $this->triggerPlugin($root->reveal(), $dir, true);
}


/**
* @param RootPackage $package
* @param string $directory Working directory for composer run
* @param bool $use_init_event If the init event should be triggered.
* @return array Constrains added by MergePlugin::onDependencySolve
*/
protected function triggerPlugin($package, $directory)
protected function triggerPlugin($package, $directory, $use_init_event = false)
{
chdir($directory);
$this->composer->getPackage()->willReturn($package);

$event = new \Composer\EventDispatcher\Event(
MergePlugin::COMPAT_PLUGINEVENTS_INIT
);
if ($use_init_event) {
$this->fixture->onInit($event);
}

$event = new Event(
ScriptEvents::PRE_INSTALL_CMD,
$this->composer->reveal(),
Expand Down