Skip to content

Commit e814e9d

Browse files
committed
feature symfony#901 Real composer scripts
1 parent c36d11d commit e814e9d

File tree

3 files changed

+361
-0
lines changed

3 files changed

+361
-0
lines changed

src/Configurator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public function __construct(Composer $composer, IOInterface $io, Options $option
4141
'container' => Configurator\ContainerConfigurator::class,
4242
'makefile' => Configurator\MakefileConfigurator::class,
4343
'composer-scripts' => Configurator\ComposerScriptsConfigurator::class,
44+
'composer-commands' => Configurator\ComposerCommandsConfigurator::class,
4445
'gitignore' => Configurator\GitignoreConfigurator::class,
4546
'dockerfile' => Configurator\DockerfileConfigurator::class,
4647
'docker-compose' => Configurator\DockerComposeConfigurator::class,
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Configurator;
13+
14+
use Composer\Factory;
15+
use Composer\Json\JsonFile;
16+
use Composer\Json\JsonManipulator;
17+
use Symfony\Flex\Lock;
18+
use Symfony\Flex\Recipe;
19+
use Symfony\Flex\Update\RecipeUpdate;
20+
21+
/**
22+
* @author Marcin Morawski <[email protected]>
23+
*/
24+
class ComposerCommandsConfigurator extends AbstractConfigurator
25+
{
26+
public function configure(Recipe $recipe, $scripts, Lock $lock, array $options = [])
27+
{
28+
$json = new JsonFile(Factory::getComposerFile());
29+
30+
file_put_contents($json->getPath(), $this->configureScripts($scripts, $json));
31+
}
32+
33+
public function unconfigure(Recipe $recipe, $scripts, Lock $lock)
34+
{
35+
$json = new JsonFile(Factory::getComposerFile());
36+
37+
$manipulator = new JsonManipulator(file_get_contents($json->getPath()));
38+
foreach ($scripts as $key => $command) {
39+
$manipulator->removeProperty('scripts.' . $key);
40+
}
41+
42+
file_put_contents($json->getPath(), $manipulator->getContents());
43+
}
44+
45+
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
46+
{
47+
$json = new JsonFile(Factory::getComposerFile());
48+
$jsonPath = ltrim(str_replace($recipeUpdate->getRootDir(), '', $json->getPath()), '/\\');
49+
50+
$recipeUpdate->setOriginalFile(
51+
$jsonPath,
52+
$this->configureScripts($originalConfig, $json)
53+
);
54+
$recipeUpdate->setNewFile(
55+
$jsonPath,
56+
$this->configureScripts($newConfig, $json)
57+
);
58+
}
59+
60+
private function configureScripts(array $scripts, JsonFile $json): string
61+
{
62+
$manipulator = new JsonManipulator(file_get_contents($json->getPath()));
63+
foreach ($scripts as $cmdName => $script) {
64+
$manipulator->addProperty('scripts.' . $cmdName, $script);
65+
}
66+
67+
return $manipulator->getContents();
68+
}
69+
}
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Tests\Configurator;
13+
14+
use Composer\Composer;
15+
use Composer\IO\IOInterface;
16+
use Composer\Util\Platform;
17+
use PHPUnit\Framework\TestCase;
18+
use Symfony\Flex\Configurator\ComposerCommandsConfigurator;
19+
use Symfony\Flex\Configurator\ComposerScriptsConfigurator;
20+
use Symfony\Flex\Lock;
21+
use Symfony\Flex\Options;
22+
use Symfony\Flex\Recipe;
23+
use Symfony\Flex\Update\RecipeUpdate;
24+
25+
class ComposerCommandConfiguratorTest extends TestCase
26+
{
27+
protected function setUp(): void
28+
{
29+
@mkdir(FLEX_TEST_DIR);
30+
if (method_exists(Platform::class, 'putEnv')) {
31+
Platform::putEnv('COMPOSER', FLEX_TEST_DIR.'/composer.json');
32+
} else {
33+
putenv('COMPOSER='.FLEX_TEST_DIR.'/composer.json');
34+
}
35+
}
36+
37+
protected function tearDown(): void
38+
{
39+
@unlink(FLEX_TEST_DIR.'/composer.json');
40+
@rmdir(FLEX_TEST_DIR);
41+
if (method_exists(Platform::class, 'clearEnv')) {
42+
Platform::clearEnv('COMPOSER');
43+
} else {
44+
putenv('COMPOSER');
45+
}
46+
}
47+
48+
public function providerConfigureData(): iterable
49+
{
50+
yield 'without_scripts_block' => [
51+
new \stdClass(),
52+
<<<EOF
53+
{
54+
"scripts": {
55+
"do:cool-stuff": "symfony-cmd"
56+
}
57+
}
58+
59+
EOF
60+
];
61+
62+
yield 'with_existing_command' => [
63+
[
64+
'scripts' => [
65+
'foo' => 'bar'
66+
],
67+
],
68+
<<<EOF
69+
{
70+
"scripts": {
71+
"foo": "bar",
72+
"do:cool-stuff": "symfony-cmd"
73+
}
74+
}
75+
76+
EOF
77+
];
78+
79+
yield 'with_existing_auto_scripts' => [
80+
[
81+
'scripts' => [
82+
'auto-scripts' => [
83+
'cache:clear' => 'symfony-cmd',
84+
'assets:install %PUBLIC_DIR%' => 'symfony-cmd',
85+
],
86+
'post-install-cmd' => ['@auto-scripts'],
87+
'post-update-cmd' => ['@auto-scripts'],
88+
],
89+
],
90+
<<<EOF
91+
{
92+
"scripts": {
93+
"auto-scripts": {
94+
"cache:clear": "symfony-cmd",
95+
"assets:install %PUBLIC_DIR%": "symfony-cmd"
96+
},
97+
"post-install-cmd": [
98+
"@auto-scripts"
99+
],
100+
"post-update-cmd": [
101+
"@auto-scripts"
102+
],
103+
"do:cool-stuff": "symfony-cmd"
104+
}
105+
}
106+
107+
EOF
108+
109+
];
110+
}
111+
112+
/**
113+
* @dataProvider providerConfigureData
114+
*/
115+
public function testConfigure($composerSchema, string $expectedComposerJson)
116+
{
117+
file_put_contents(FLEX_TEST_DIR.'/composer.json', json_encode($composerSchema, \JSON_PRETTY_PRINT));
118+
119+
$configurator = new ComposerCommandsConfigurator(
120+
$this->createMock(Composer::class),
121+
$this->createMock(IOInterface::class),
122+
new Options(['root-dir' => FLEX_TEST_DIR])
123+
);
124+
125+
$recipe = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock();
126+
$lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock();
127+
128+
$configurator->configure($recipe, [
129+
'do:cool-stuff' => 'symfony-cmd',
130+
], $lock);
131+
$this->assertEquals(
132+
$expectedComposerJson,
133+
file_get_contents(FLEX_TEST_DIR.'/composer.json')
134+
);
135+
}
136+
137+
public function providerForUnconfigureMethod(): iterable
138+
{
139+
yield 'unconfigure_one_command_with_auto_scripts' => [
140+
[
141+
'scripts' => [
142+
'auto-scripts' => [
143+
'cache:clear' => 'symfony-cmd',
144+
'assets:install %PUBLIC_DIR%' => 'symfony-cmd',
145+
],
146+
'post-install-cmd' => ['@auto-scripts'],
147+
'post-update-cmd' => ['@auto-scripts'],
148+
'do:cool-stuff' => 'symfony-cmd',
149+
'do:another-cool-stuff' => 'symfony-cmd-2',
150+
],
151+
],
152+
<<<EOF
153+
{
154+
"scripts": {
155+
"auto-scripts": {
156+
"cache:clear": "symfony-cmd",
157+
"assets:install %PUBLIC_DIR%": "symfony-cmd"
158+
},
159+
"post-install-cmd": [
160+
"@auto-scripts"
161+
],
162+
"post-update-cmd": [
163+
"@auto-scripts"
164+
],
165+
"do:another-cool-stuff": "symfony-cmd-2"
166+
}
167+
}
168+
169+
EOF
170+
];
171+
172+
yield 'unconfigure_command' => [
173+
[
174+
'scripts' => [
175+
'do:another-cool-stuff' => 'symfony-cmd-2',
176+
'do:cool-stuff' => 'symfony-cmd',
177+
],
178+
],
179+
<<<EOF
180+
{
181+
"scripts": {
182+
"do:another-cool-stuff": "symfony-cmd-2"
183+
}
184+
}
185+
186+
EOF
187+
];
188+
}
189+
190+
/**
191+
* @dataProvider providerForUnconfigureMethod
192+
*/
193+
public function testUnconfigure($composerSchema, string $expectedComposerJson)
194+
{
195+
file_put_contents(FLEX_TEST_DIR.'/composer.json', json_encode($composerSchema, \JSON_PRETTY_PRINT));
196+
197+
$configurator = new ComposerCommandsConfigurator(
198+
$this->createMock(Composer::class),
199+
$this->createMock(IOInterface::class),
200+
new Options(['root-dir' => FLEX_TEST_DIR])
201+
);
202+
203+
$recipe = $this->createMock(Recipe::class);
204+
$lock = $this->createMock(Lock::class);
205+
206+
$configurator->unconfigure($recipe, [
207+
'do:cool-stuff' => 'symfony-cmd',
208+
], $lock);
209+
$this->assertEquals(
210+
$expectedComposerJson,
211+
file_get_contents(FLEX_TEST_DIR.'/composer.json')
212+
);
213+
}
214+
215+
public function testUpdate()
216+
{
217+
$configurator = new ComposerCommandsConfigurator(
218+
$this->createMock(Composer::class),
219+
$this->createMock(IOInterface::class),
220+
new Options(['root-dir' => FLEX_TEST_DIR])
221+
);
222+
223+
$recipeUpdate = new RecipeUpdate(
224+
$this->createMock(Recipe::class),
225+
$this->createMock(Recipe::class),
226+
$this->createMock(Lock::class),
227+
FLEX_TEST_DIR
228+
);
229+
230+
file_put_contents(FLEX_TEST_DIR.'/composer.json', json_encode([
231+
'scripts' => [
232+
'auto-scripts' => [
233+
'cache:clear' => 'symfony-cmd',
234+
'assets:install %PUBLIC_DIR%' => 'symfony-cmd',
235+
],
236+
'post-install-cmd' => ['@auto-scripts'],
237+
'post-update-cmd' => ['@auto-scripts'],
238+
'foo' => 'bar',
239+
],
240+
], \JSON_PRETTY_PRINT));
241+
242+
$configurator->update(
243+
$recipeUpdate,
244+
['foo' => 'bar'],
245+
['foo' => 'baz', 'do:cool-stuff' => 'symfony-cmd']
246+
);
247+
248+
$expectedComposerJsonOriginal = <<<EOF
249+
{
250+
"scripts": {
251+
"auto-scripts": {
252+
"cache:clear": "symfony-cmd",
253+
"assets:install %PUBLIC_DIR%": "symfony-cmd"
254+
},
255+
"post-install-cmd": [
256+
"@auto-scripts"
257+
],
258+
"post-update-cmd": [
259+
"@auto-scripts"
260+
],
261+
"foo": "bar"
262+
}
263+
}
264+
265+
EOF
266+
;
267+
$this->assertSame(['composer.json' => $expectedComposerJsonOriginal], $recipeUpdate->getOriginalFiles());
268+
269+
$expectedComposerJsonNew = <<<EOF
270+
{
271+
"scripts": {
272+
"auto-scripts": {
273+
"cache:clear": "symfony-cmd",
274+
"assets:install %PUBLIC_DIR%": "symfony-cmd"
275+
},
276+
"post-install-cmd": [
277+
"@auto-scripts"
278+
],
279+
"post-update-cmd": [
280+
"@auto-scripts"
281+
],
282+
"foo": "baz",
283+
"do:cool-stuff": "symfony-cmd"
284+
}
285+
}
286+
287+
EOF
288+
;
289+
$this->assertSame(['composer.json' => $expectedComposerJsonNew], $recipeUpdate->getNewFiles());
290+
}
291+
}

0 commit comments

Comments
 (0)