Skip to content

Commit

Permalink
Real composer scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
morawskim committed Jun 10, 2022
1 parent 1d04c55 commit ad92e20
Show file tree
Hide file tree
Showing 3 changed files with 359 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/Configurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function __construct(Composer $composer, IOInterface $io, Options $option
'container' => Configurator\ContainerConfigurator::class,
'makefile' => Configurator\MakefileConfigurator::class,
'composer-scripts' => Configurator\ComposerScriptsConfigurator::class,
'composer-commands' => Configurator\ComposerCommandsConfigurator::class,
'gitignore' => Configurator\GitignoreConfigurator::class,
'dockerfile' => Configurator\DockerfileConfigurator::class,
'docker-compose' => Configurator\DockerComposeConfigurator::class,
Expand Down
69 changes: 69 additions & 0 deletions src/Configurator/ComposerCommandsConfigurator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Flex\Configurator;

use Composer\Factory;
use Composer\Json\JsonFile;
use Composer\Json\JsonManipulator;
use Symfony\Flex\Lock;
use Symfony\Flex\Recipe;
use Symfony\Flex\Update\RecipeUpdate;

/**
* @author Marcin Morawski <[email protected]>
*/
class ComposerCommandsConfigurator extends AbstractConfigurator
{
public function configure(Recipe $recipe, $scripts, Lock $lock, array $options = [])
{
$json = new JsonFile(Factory::getComposerFile());

file_put_contents($json->getPath(), $this->configureScripts($scripts, $json));
}

public function unconfigure(Recipe $recipe, $scripts, Lock $lock)
{
$json = new JsonFile(Factory::getComposerFile());

$manipulator = new JsonManipulator(file_get_contents($json->getPath()));
foreach ($scripts as $key => $command) {
$manipulator->removeSubNode('scripts', $key);
}

file_put_contents($json->getPath(), $manipulator->getContents());
}

public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
{
$json = new JsonFile(Factory::getComposerFile());
$jsonPath = ltrim(str_replace($recipeUpdate->getRootDir(), '', $json->getPath()), '/\\');

$recipeUpdate->setOriginalFile(
$jsonPath,
$this->configureScripts($originalConfig, $json)
);
$recipeUpdate->setNewFile(
$jsonPath,
$this->configureScripts($newConfig, $json)
);
}

private function configureScripts(array $scripts, JsonFile $json): string
{
$manipulator = new JsonManipulator(file_get_contents($json->getPath()));
foreach ($scripts as $cmdName => $script) {
$manipulator->addSubNode('scripts', $cmdName, $script);
}

return $manipulator->getContents();
}
}
289 changes: 289 additions & 0 deletions tests/Configurator/ComposerCommandConfiguratorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Flex\Tests\Configurator;

use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Util\Platform;
use PHPUnit\Framework\TestCase;
use Symfony\Flex\Configurator\ComposerCommandsConfigurator;
use Symfony\Flex\Lock;
use Symfony\Flex\Options;
use Symfony\Flex\Recipe;
use Symfony\Flex\Update\RecipeUpdate;

class ComposerCommandConfiguratorTest extends TestCase
{
protected function setUp(): void
{
@mkdir(FLEX_TEST_DIR);
if (method_exists(Platform::class, 'putEnv')) {
Platform::putEnv('COMPOSER', FLEX_TEST_DIR.'/composer.json');
} else {
putenv('COMPOSER='.FLEX_TEST_DIR.'/composer.json');
}
}

protected function tearDown(): void
{
@unlink(FLEX_TEST_DIR.'/composer.json');
@rmdir(FLEX_TEST_DIR);
if (method_exists(Platform::class, 'clearEnv')) {
Platform::clearEnv('COMPOSER');
} else {
putenv('COMPOSER');
}
}

public function providerForConfigureMethod(): iterable
{
yield 'without_scripts_block' => [
new \stdClass(),
<<<EOF
{
"scripts": {
"do:cool-stuff": "symfony-cmd"
}
}
EOF
];

yield 'with_existing_command' => [
[
'scripts' => [
'foo' => 'bar',
],
],
<<<EOF
{
"scripts": {
"foo": "bar",
"do:cool-stuff": "symfony-cmd"
}
}
EOF
];

yield 'with_existing_auto_scripts' => [
[
'scripts' => [
'auto-scripts' => [
'cache:clear' => 'symfony-cmd',
'assets:install %PUBLIC_DIR%' => 'symfony-cmd',
],
'post-install-cmd' => ['@auto-scripts'],
'post-update-cmd' => ['@auto-scripts'],
],
],
<<<EOF
{
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
],
"do:cool-stuff": "symfony-cmd"
}
}
EOF
];
}

/**
* @dataProvider providerForConfigureMethod
*/
public function testConfigure($composerSchema, string $expectedComposerJson): void
{
file_put_contents(FLEX_TEST_DIR.'/composer.json', json_encode($composerSchema, \JSON_PRETTY_PRINT));

$configurator = new ComposerCommandsConfigurator(
$this->createMock(Composer::class),
$this->createMock(IOInterface::class),
new Options(['root-dir' => FLEX_TEST_DIR])
);

$recipe = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock();
$lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock();

$configurator->configure($recipe, [
'do:cool-stuff' => 'symfony-cmd',
], $lock);
$this->assertEquals(
$expectedComposerJson,
file_get_contents(FLEX_TEST_DIR.'/composer.json')
);
}

public function providerForUnconfigureMethod(): iterable
{
yield 'unconfigure_one_command_with_auto_scripts' => [
[
'scripts' => [
'auto-scripts' => [
'cache:clear' => 'symfony-cmd',
'assets:install %PUBLIC_DIR%' => 'symfony-cmd',
],
'post-install-cmd' => ['@auto-scripts'],
'post-update-cmd' => ['@auto-scripts'],
'do:cool-stuff' => 'symfony-cmd',
'do:another-cool-stuff' => 'symfony-cmd-2',
],
],
<<<EOF
{
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
],
"do:another-cool-stuff": "symfony-cmd-2"
}
}
EOF
];

yield 'unconfigure_command' => [
[
'scripts' => [
'do:another-cool-stuff' => 'symfony-cmd-2',
'do:cool-stuff' => 'symfony-cmd',
],
],
<<<EOF
{
"scripts": {
"do:another-cool-stuff": "symfony-cmd-2"
}
}
EOF
];
}

/**
* @dataProvider providerForUnconfigureMethod
*/
public function testUnconfigure($composerSchema, string $expectedComposerJson): void
{
file_put_contents(FLEX_TEST_DIR.'/composer.json', json_encode($composerSchema, \JSON_PRETTY_PRINT));

$configurator = new ComposerCommandsConfigurator(
$this->createMock(Composer::class),
$this->createMock(IOInterface::class),
new Options(['root-dir' => FLEX_TEST_DIR])
);

$recipe = $this->createMock(Recipe::class);
$lock = $this->createMock(Lock::class);

$configurator->unconfigure($recipe, [
'do:cool-stuff' => 'symfony-cmd',
], $lock);
$this->assertEquals(
$expectedComposerJson,
file_get_contents(FLEX_TEST_DIR.'/composer.json')
);
}

public function testUpdate(): void
{
$configurator = new ComposerCommandsConfigurator(
$this->createMock(Composer::class),
$this->createMock(IOInterface::class),
new Options(['root-dir' => FLEX_TEST_DIR])
);

$recipeUpdate = new RecipeUpdate(
$this->createMock(Recipe::class),
$this->createMock(Recipe::class),
$this->createMock(Lock::class),
FLEX_TEST_DIR
);

file_put_contents(FLEX_TEST_DIR.'/composer.json', json_encode([
'scripts' => [
'auto-scripts' => [
'cache:clear' => 'symfony-cmd',
'assets:install %PUBLIC_DIR%' => 'symfony-cmd',
],
'post-install-cmd' => ['@auto-scripts'],
'post-update-cmd' => ['@auto-scripts'],
'foo' => 'bar',
],
], \JSON_PRETTY_PRINT));

$configurator->update(
$recipeUpdate,
['foo' => 'bar'],
['foo' => 'baz', 'do:cool-stuff' => 'symfony-cmd']
);

$expectedComposerJsonOriginal = <<<EOF
{
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
],
"foo": "bar"
}
}
EOF
;
$this->assertSame(['composer.json' => $expectedComposerJsonOriginal], $recipeUpdate->getOriginalFiles());

$expectedComposerJsonNew = <<<EOF
{
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
],
"foo": "baz",
"do:cool-stuff": "symfony-cmd"
}
}
EOF
;
$this->assertSame(['composer.json' => $expectedComposerJsonNew], $recipeUpdate->getNewFiles());
}
}

0 comments on commit ad92e20

Please sign in to comment.