Skip to content

Commit 320a423

Browse files
[command] Add README metadata during composer sync (#57) (#82)
* Add README metadata during composer sync * Update wiki submodule pointer for PR #82 --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
1 parent e5e39b5 commit 320a423

4 files changed

Lines changed: 114 additions & 2 deletions

File tree

.github/wiki

Submodule wiki updated from 5cea16d to 936be00

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"ecs",
1313
"fast-forward"
1414
],
15+
"readme": "README.md",
1516
"authors": [
1617
{
1718
"name": "Felipe Sayão Lobato Abreu",

src/Console/Command/UpdateComposerJsonCommand.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Composer\Command\BaseCommand;
2323
use Composer\Factory;
2424
use Composer\Json\JsonManipulator;
25+
use FastForward\DevTools\Composer\Json\ComposerJsonInterface;
2526
use FastForward\DevTools\Filesystem\FilesystemInterface;
2627
use Symfony\Component\Config\FileLocatorInterface;
2728
use Symfony\Component\Console\Attribute\AsCommand;
@@ -45,10 +46,12 @@ final class UpdateComposerJsonCommand extends BaseCommand
4546
/**
4647
* Creates a new UpdateComposerJsonCommand instance.
4748
*
49+
* @param ComposerJsonInterface $composer the composer.json metadata accessor
4850
* @param FilesystemInterface $filesystem the filesystem used to read and write composer.json
4951
* @param FileLocatorInterface $fileLocator the locator used to resolve packaged configuration files
5052
*/
5153
public function __construct(
54+
private readonly ComposerJsonInterface $composer,
5255
private readonly FilesystemInterface $filesystem,
5356
private readonly FileLocatorInterface $fileLocator,
5457
) {
@@ -95,6 +98,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
9598
$manipulator->addSubNode('scripts', $name, $command);
9699
}
97100

101+
if ('' === $this->composer->getReadme() && $this->filesystem->exists('README.md', \dirname($file))) {
102+
$manipulator->addProperty('readme', 'README.md');
103+
}
104+
98105
$manipulator->addSubNode('extra', 'grumphp', [
99106
'config-default-path' => Path::makeRelative($grumphpConfig, getcwd()),
100107
], true);

tests/Console/Command/UpdateComposerJsonCommandTest.php

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
namespace FastForward\DevTools\Tests\Console\Command;
2121

22+
use FastForward\DevTools\Composer\Json\ComposerJsonInterface;
2223
use FastForward\DevTools\Console\Command\UpdateComposerJsonCommand;
2324
use FastForward\DevTools\Filesystem\FilesystemInterface;
2425
use PHPUnit\Framework\Attributes\CoversClass;
@@ -32,13 +33,17 @@
3233
use Symfony\Component\Console\Input\InputInterface;
3334
use Symfony\Component\Console\Output\OutputInterface;
3435

36+
use function Safe\json_decode;
37+
3538
#[CoversClass(UpdateComposerJsonCommand::class)]
3639
final class UpdateComposerJsonCommandTest extends TestCase
3740
{
3841
use ProphecyTrait;
3942

4043
private ObjectProphecy $filesystem;
4144

45+
private ObjectProphecy $composer;
46+
4247
private ObjectProphecy $fileLocator;
4348

4449
private ObjectProphecy $input;
@@ -52,12 +57,17 @@ final class UpdateComposerJsonCommandTest extends TestCase
5257
*/
5358
protected function setUp(): void
5459
{
60+
$this->composer = $this->prophesize(ComposerJsonInterface::class);
5561
$this->filesystem = $this->prophesize(FilesystemInterface::class);
5662
$this->fileLocator = $this->prophesize(FileLocatorInterface::class);
5763
$this->input = $this->prophesize(InputInterface::class);
5864
$this->output = $this->prophesize(OutputInterface::class);
5965

60-
$this->command = new UpdateComposerJsonCommand($this->filesystem->reveal(), $this->fileLocator->reveal());
66+
$this->command = new UpdateComposerJsonCommand(
67+
$this->composer->reveal(),
68+
$this->filesystem->reveal(),
69+
$this->fileLocator->reveal(),
70+
);
6171
}
6272

6373
/**
@@ -89,6 +99,10 @@ public function executeWillUpdateComposerJsonScriptsAndExtraConfiguration(): voi
8999
->willReturn(true);
90100
$this->filesystem->readFile('/app/composer.json')
91101
->willReturn('{"name":"example/package"}');
102+
$this->composer->getReadme()
103+
->willReturn('');
104+
$this->filesystem->exists('README.md', '/app')
105+
->willReturn(false);
92106
$this->fileLocator->locate('grumphp.yml', Argument::type('string'))
93107
->willReturn('/app/vendor/fast-forward/dev-tools/grumphp.yml');
94108
$this->filesystem->dumpFile(
@@ -100,6 +114,96 @@ public function executeWillUpdateComposerJsonScriptsAndExtraConfiguration(): voi
100114
self::assertSame(UpdateComposerJsonCommand::SUCCESS, $this->executeCommand());
101115
}
102116

117+
/**
118+
* @return void
119+
*/
120+
#[Test]
121+
public function executeWillAddReadmeMetadataWhenReadmeExistsAndComposerJsonDoesNotDeclareReadme(): void
122+
{
123+
$this->input->getOption('file')
124+
->willReturn('/app/composer.json');
125+
$this->filesystem->exists('/app/composer.json')
126+
->willReturn(true);
127+
$this->filesystem->readFile('/app/composer.json')
128+
->willReturn('{"name":"example/package"}');
129+
$this->composer->getReadme()
130+
->willReturn('');
131+
$this->filesystem->exists('README.md', '/app')
132+
->willReturn(true);
133+
$this->fileLocator->locate('grumphp.yml', Argument::type('string'))
134+
->willReturn('/app/vendor/fast-forward/dev-tools/grumphp.yml');
135+
$this->filesystem->dumpFile(
136+
'/app/composer.json',
137+
Argument::that(static function (string $contents): bool {
138+
$composerJson = json_decode($contents, true);
139+
140+
return 'README.md' === $composerJson['readme'];
141+
}),
142+
)->shouldBeCalledOnce();
143+
144+
self::assertSame(UpdateComposerJsonCommand::SUCCESS, $this->executeCommand());
145+
}
146+
147+
/**
148+
* @return void
149+
*/
150+
#[Test]
151+
public function executeWillPreserveExistingReadmeMetadata(): void
152+
{
153+
$this->input->getOption('file')
154+
->willReturn('/app/composer.json');
155+
$this->filesystem->exists('/app/composer.json')
156+
->willReturn(true);
157+
$this->filesystem->readFile('/app/composer.json')
158+
->willReturn('{"name":"example/package","readme":"docs/readme.md"}');
159+
$this->composer->getReadme()
160+
->willReturn('docs/readme.md');
161+
$this->filesystem->exists('README.md', '/app')
162+
->shouldNotBeCalled();
163+
$this->fileLocator->locate('grumphp.yml', Argument::type('string'))
164+
->willReturn('/app/vendor/fast-forward/dev-tools/grumphp.yml');
165+
$this->filesystem->dumpFile(
166+
'/app/composer.json',
167+
Argument::that(static function (string $contents): bool {
168+
$composerJson = json_decode($contents, true);
169+
170+
return 'docs/readme.md' === $composerJson['readme'];
171+
}),
172+
)->shouldBeCalledOnce();
173+
174+
self::assertSame(UpdateComposerJsonCommand::SUCCESS, $this->executeCommand());
175+
}
176+
177+
/**
178+
* @return void
179+
*/
180+
#[Test]
181+
public function executeWillSkipReadmeMetadataWhenReadmeDoesNotExist(): void
182+
{
183+
$this->input->getOption('file')
184+
->willReturn('/app/composer.json');
185+
$this->filesystem->exists('/app/composer.json')
186+
->willReturn(true);
187+
$this->filesystem->readFile('/app/composer.json')
188+
->willReturn('{"name":"example/package"}');
189+
$this->composer->getReadme()
190+
->willReturn('');
191+
$this->filesystem->exists('README.md', '/app')
192+
->willReturn(false);
193+
$this->fileLocator->locate('grumphp.yml', Argument::type('string'))
194+
->willReturn('/app/vendor/fast-forward/dev-tools/grumphp.yml');
195+
$this->filesystem->dumpFile(
196+
'/app/composer.json',
197+
Argument::that(static function (string $contents): bool {
198+
$composerJson = json_decode($contents, true);
199+
200+
return ! \array_key_exists('readme', $composerJson);
201+
}),
202+
)->shouldBeCalledOnce();
203+
204+
self::assertSame(UpdateComposerJsonCommand::SUCCESS, $this->executeCommand());
205+
}
206+
103207
/**
104208
* @return int
105209
*/

0 commit comments

Comments
 (0)