Skip to content

Commit 3bc44a3

Browse files
committed
Real composer scripts
1 parent 9c61279 commit 3bc44a3

File tree

3 files changed

+359
-0
lines changed

3 files changed

+359
-0
lines changed

Diff for: src/Configurator.php

+1
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,

Diff for: src/Configurator/ComposerCommandsConfigurator.php

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

0 commit comments

Comments
 (0)