Skip to content

Commit

Permalink
Add merge-scripts option. (#136)
Browse files Browse the repository at this point in the history
Optionally merge the `scripts` section of included configuration files.

Closes #135
  • Loading branch information
grasmash authored and bd808 committed Mar 13, 2017
1 parent e24d531 commit 1b37cd3
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 2 deletions.
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ Usage
"replace": false,
"merge-dev": true,
"merge-extra": false,
"merge-extra-deep": false
"merge-extra-deep": false,
"merge-scripts": false
}
}
}
Expand Down Expand Up @@ -90,7 +91,9 @@ in the top-level composer.json file:
(optional, see [merge-dev](#merge-dev) below)
* [suggest](https://getcomposer.org/doc/04-schema.md#suggest)
* [extra](https://getcomposer.org/doc/04-schema.md#extra)
(optional, see [merge-extra](#merge-extra) below)
(optional, see [merge-extra](#merge-extra) below)*
* [scripts](https://getcomposer.org/doc/04-schema.md#scripts)
(optional, see [merge-scripts](#merge-scripts) below)


### require
Expand Down Expand Up @@ -140,6 +143,16 @@ they are processed by Composer.
Note that `merge-plugin` sections are excluded from the merge process, but are
always processed by the plugin unless [recursion](#recurse) is disabled.

### merge-scripts

A `"merge-scripts": true` setting enables merging the contents of the
`scripts` section of included files as well. The normal merge mode for the
scripts section is to accept the first version of any key found (e.g. a key in
the master config wins over the version found in any imported config). If
`replace` mode is active ([see above](#replace)) then this behavior changes
and the last key found will win (e.g. the key in the master config is replaced
by the key in the imported config).


Running tests
-------------
Expand Down
29 changes: 29 additions & 0 deletions src/Merge/ExtraPackage.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ public function mergeInto(RootPackageInterface $root, PluginState $state)

$this->mergeExtra($root, $state);

$this->mergeScripts($root, $state);

if ($state->isDevMode()) {
$this->mergeDevInto($root, $state);
} else {
Expand Down Expand Up @@ -441,6 +443,33 @@ public function mergeExtra(RootPackageInterface $root, PluginState $state)
}
}

/**
* Merge scripts config into a RootPackageInterface
*
* @param RootPackageInterface $root
* @param PluginState $state
*/
public function mergeScripts(RootPackageInterface $root, PluginState $state)
{
$scripts = $this->package->getScripts();
if (!$state->shouldMergeScripts() || empty($scripts)) {
return;
}

$rootScripts = $root->getScripts();
$unwrapped = self::unwrapIfNeeded($root, 'setScripts');

if ($state->replaceDuplicateLinks()) {
$unwrapped->setScripts(
array_merge($rootScripts, $scripts)
);
} else {
$unwrapped->setScripts(
array_merge($scripts, $rootScripts)
);
}
}

/**
* Merges two arrays either via arrayMergeDeep or via array_merge.
*
Expand Down
22 changes: 22 additions & 0 deletions src/Merge/PluginState.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ class PluginState
*/
protected $mergeExtraDeep = false;

/**
* Whether to merge the scripts section.
*
* @var bool $mergeScripts
*/
protected $mergeScripts = false;

/**
* @var bool $firstInstall
*/
Expand Down Expand Up @@ -135,6 +142,7 @@ public function loadSettings()
'merge-dev' => true,
'merge-extra' => false,
'merge-extra-deep' => false,
'merge-scripts' => false,
),
isset($extra['merge-plugin']) ? $extra['merge-plugin'] : array()
);
Expand All @@ -148,6 +156,7 @@ public function loadSettings()
$this->mergeDev = (bool)$config['merge-dev'];
$this->mergeExtra = (bool)$config['merge-extra'];
$this->mergeExtraDeep = (bool)$config['merge-extra-deep'];
$this->mergeScripts = (bool)$config['merge-scripts'];
}

/**
Expand Down Expand Up @@ -374,5 +383,18 @@ public function shouldMergeExtraDeep()
{
return $this->mergeExtraDeep;
}


/**
* Should the scripts section be merged?
*
* By default, the scripts section is not merged.
*
* @return bool
*/
public function shouldMergeScripts()
{
return $this->mergeScripts;
}
}
// vim:sw=4:ts=4:sts=4:et:
113 changes: 113 additions & 0 deletions tests/phpunit/MergePluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,117 @@ public function provideDeepMerge()
);
}

/**
* Given a root package with an scripts section
* and a composer.local.json with an extra section with no conflicting keys
* When the plugin is run
* Then the root package scripts section should be extended with content from the local config.
*
* @param bool $fireInit Fire the INIT event?
*
* @dataProvider provideFireInit
*/
public function testMergeScripts($fireInit)
{
$that = $this;
$dir = $this->fixtureDir(__FUNCTION__);

$root = $this->rootFromJson("{$dir}/composer.json");

$root->setScripts(Argument::type('array'))->will(
function ($args) use ($that) {
$scripts = $args[0];
$that->assertEquals(2, count($scripts));
$that->assertArrayHasKey('example-script', $scripts);
$that->assertEquals(2, count($scripts['example-script']));
$that->assertArrayHasKey('example-script2', $scripts);
}
)->shouldBeCalled();

$root->getRepositories()->shouldNotBeCalled();
$root->getConflicts()->shouldNotBeCalled();
$root->getReplaces()->shouldNotBeCalled();
$root->getProvides()->shouldNotBeCalled();
$root->getSuggests()->shouldNotBeCalled();

$scriptsInstalls = $this->triggerPlugin($root->reveal(), $dir, $fireInit);

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

/**
* Given a root package with an scripts section
* and a composer.local.json with an extra section with a conflicting key
* When the plugin is run
* Then the version in the root package should win.
*/
public function testMergeScriptsConflict()
{
$that = $this;
$dir = $this->fixtureDir(__FUNCTION__);

$root = $this->rootFromJson("{$dir}/composer.json");

$root->setScripts(Argument::type('array'))->will(
function ($args) use ($that) {
$scripts = $args[0];
$that->assertEquals(3, count($scripts));
$that->assertArrayHasKey('example-script2', $scripts);
$that->assertArrayHasKey('example-script3', $scripts);
$that->assertEquals("echo 'goodbye world'", $scripts['example-script2']);
$that->assertEquals(1, count($scripts['example-script3']));
$that->assertEquals("echo 'adios world'", $scripts['example-script3'][0]);
}
)->shouldBeCalled();

$root->getRepositories()->shouldNotBeCalled();
$root->getConflicts()->shouldNotBeCalled();
$root->getReplaces()->shouldNotBeCalled();
$root->getProvides()->shouldNotBeCalled();
$root->getSuggests()->shouldNotBeCalled();

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

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

/**
* Given a root package with a scripts section
* and replace mode is active
* and a composer.local.json with a scripts section with a conflicting key
* When the plugin is run
* Then the version in the composer.local.json package should win.
*/
public function testMergeScriptsConflictReplace()
{
$that = $this;
$dir = $this->fixtureDir(__FUNCTION__);

$root = $this->rootFromJson("{$dir}/composer.json");

$root->setScripts(Argument::type('array'))->will(
function ($args) use ($that) {
$scripts = $args[0];
$that->assertEquals(3, count($scripts));
$that->assertArrayHasKey('example-script', $scripts);
$that->assertArrayHasKey('example-script2', $scripts);
$that->assertEquals(1, count($scripts['example-script2']));
$that->assertEquals("echo 'hello world'", $scripts['example-script2'][0]);
$that->assertEquals(1, count($scripts['example-script3']));
$that->assertEquals("echo 'hola world'", $scripts['example-script3'][0]);
}
)->shouldBeCalled();

$root->getRepositories()->shouldNotBeCalled();
$root->getConflicts()->shouldNotBeCalled();
$root->getReplaces()->shouldNotBeCalled();
$root->getProvides()->shouldNotBeCalled();
$root->getSuggests()->shouldNotBeCalled();

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

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

/**
* @dataProvider provideOnPostPackageInstall
Expand Down Expand Up @@ -1311,6 +1422,7 @@ protected function rootFromJson($file)
'provide' => array(),
'suggest' => array(),
'extra' => array(),
'scripts' => array(),
'autoload' => array(),
'autoload-dev' => array(),
'minimum-stability' => 'stable',
Expand Down Expand Up @@ -1351,6 +1463,7 @@ protected function rootFromJson($file)
$root->getProvides()->willReturn($data['provide']);
$root->getSuggests()->willReturn($data['suggest']);
$root->getExtra()->willReturn($data['extra'])->shouldBeCalled();
$root->getScripts()->willReturn($data['scripts']);
$root->getAutoload()->willReturn($data['autoload']);
$root->getDevAutoload()->willReturn($data['autoload-dev']);

Expand Down
8 changes: 8 additions & 0 deletions tests/phpunit/fixtures/testMergeScripts/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extra": {
"merge-plugin": {
"merge-scripts": true,
"include": "composer.local.json"
}
}
}
9 changes: 9 additions & 0 deletions tests/phpunit/fixtures/testMergeScripts/composer.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"scripts": {
"example-script": [
"echo 'hello world'",
"echo 'hello again world'"
],
"example-script2": "echo 'hello world'"
}
}
14 changes: 14 additions & 0 deletions tests/phpunit/fixtures/testMergeScriptsConflict/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extra": {
"merge-plugin": {
"merge-scripts": true,
"include": "composer.local.json"
}
},
"scripts": {
"example-script2": "echo 'goodbye world'",
"example-script3": [
"echo 'adios world'"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"scripts": {
"example-script": [
"echo 'hello world'",
"echo 'hello again world'"
],
"example-script2": "echo 'hello world'",
"example-script3": "echo 'hola world'"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extra": {
"merge-plugin": {
"replace": true,
"merge-scripts": true,
"include": "composer.local.json"
}
},
"scripts": {
"example-script2": "echo 'goodbye world'",
"example-script3": "echo 'adios world'"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"scripts": {
"example-script": [
"echo 'hello world'",
"echo 'hello again world'"
],
"example-script2": "echo 'hello world'",
"example-script3": [
"echo 'hola world'"
]
}
}

0 comments on commit 1b37cd3

Please sign in to comment.