Skip to content

Commit 7784bd1

Browse files
authored
Merge pull request #620 from CPS-IT/feature/optional-commands
2 parents 8b98f8e + d130049 commit 7784bd1

File tree

5 files changed

+62
-28
lines changed

5 files changed

+62
-28
lines changed

docs/development/configuration.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ steps:
212212
command: 'git init --initial-branch=main'
213213
skipConfirmation: true
214214
allowFailure: true
215+
- type: runCommand
216+
options:
217+
command: 'composer update'
218+
allowFailure: true
219+
required: false
215220
- type: showNextSteps
216221
options:
217222
templateFile: templates/next-steps.html.twig

resources/config.schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,12 @@
315315
"title": "Allow command execution failure",
316316
"description": "Ignore errors occurred during command execution and continue as normal",
317317
"default": false
318+
},
319+
"required": {
320+
"type": "boolean",
321+
"title": "Enforce command execution",
322+
"description": "If set to true, the command must be executed and cannot be skipped, otherwise project generation fails",
323+
"default": true
318324
}
319325
},
320326
"required": [

src/Builder/Config/ValueObject/StepOptions.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public function __construct(
4141
private readonly ?string $command = null,
4242
private readonly bool $skipConfirmation = false,
4343
private readonly bool $allowFailure = false,
44+
private readonly bool $required = true,
4445
) {}
4546

4647
/**
@@ -75,4 +76,9 @@ public function shouldAllowFailure(): bool
7576
{
7677
return $this->allowFailure;
7778
}
79+
80+
public function isRequired(): bool
81+
{
82+
return $this->required;
83+
}
7884
}

src/Builder/Generator/Step/RunCommandStep.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public function run(BuildResult $buildResult): bool
5757
if (!$this->config->getOptions()->shouldSkipConfirmation() && !$this->messenger->confirmRunCommand($command)) {
5858
$this->stopped = true;
5959

60-
return false;
60+
return !$this->config->getOptions()->isRequired();
6161
}
6262

6363
$this->messenger->newLine(ComposerIO\IOInterface::VERBOSE);

tests/src/Builder/Generator/Step/RunCommandStepTest.php

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
use CPSIT\ProjectBuilder as Src;
2727
use CPSIT\ProjectBuilder\Tests;
28+
use Generator;
29+
use LogicException;
2830
use PHPUnit\Framework;
2931
use Symfony\Component\Filesystem;
3032

@@ -38,6 +40,7 @@ final class RunCommandStepTest extends Tests\ContainerAwareTestCase
3840
{
3941
private Src\Builder\Generator\Step\RunCommandStep $subject;
4042
private Src\Builder\BuildResult $result;
43+
private Filesystem\Filesystem $filesystem;
4144

4245
protected function setUp(): void
4346
{
@@ -50,6 +53,11 @@ protected function setUp(): void
5053
'foo',
5154
),
5255
);
56+
$this->filesystem = new Filesystem\Filesystem();
57+
58+
if (!$this->filesystem->exists($this->result->getWrittenDirectory())) {
59+
$this->filesystem->mkdir($this->result->getWrittenDirectory());
60+
}
5361
}
5462

5563
#[Framework\Attributes\Test]
@@ -64,7 +72,7 @@ public function runThrowsExceptionIfNoCommandIsGiven(): void
6472
#[Framework\Attributes\Test]
6573
public function runThrowsExceptionIfRevertingIsAttempted(): never
6674
{
67-
$this->expectException('\LogicException');
75+
$this->expectException(LogicException::class);
6876
$this->expectExceptionCode(1687518806);
6977
$this->expectExceptionMessage('An already run command cannot be reverted.');
7078
$this->subject->revert($this->result);
@@ -79,7 +87,7 @@ public function runCommandIsSupported(): void
7987
}
8088

8189
#[Framework\Attributes\Test]
82-
public function runExecutesCommandWithoutConfirmationIfSKipConfirmationIsConfigured(): void
90+
public function runExecutesCommandWithoutConfirmationIfSkipConfirmationIsConfigured(): void
8391
{
8492
$this->subject->setConfig(
8593
new Src\Builder\Config\ValueObject\Step(
@@ -91,18 +99,30 @@ public function runExecutesCommandWithoutConfirmationIfSKipConfirmationIsConfigu
9199
),
92100
);
93101

94-
$workingDirectory = $this->result->getWrittenDirectory();
95-
96-
$fileSystem = new Filesystem\Filesystem();
97-
if (!$fileSystem->exists($workingDirectory)) {
98-
$fileSystem->mkdir($workingDirectory);
99-
}
100-
101102
self::assertTrue($this->subject->run($this->result));
102103
self::assertFalse($this->subject->isStopped());
103104
self::assertStringNotContainsString('Do you wish to run this command?', $this->io->getOutput());
104105
}
105106

107+
#[Framework\Attributes\Test]
108+
#[Framework\Attributes\DataProvider('runDoesNotExecuteCommandAndRespectsExecutionRequirementDataProvider')]
109+
public function runDoesNotExecuteCommandAndRespectsExecutionRequirement(bool $required, bool $expected): void
110+
{
111+
$this->subject->setConfig(
112+
new Src\Builder\Config\ValueObject\Step(
113+
Src\Builder\Generator\Step\RunCommandStep::getType(),
114+
new Src\Builder\Config\ValueObject\StepOptions(
115+
command: 'echo \'foo\'',
116+
required: $required,
117+
),
118+
),
119+
);
120+
121+
$this->io->setUserInputs(['no']);
122+
self::assertSame($expected, $this->subject->run($this->result));
123+
self::assertTrue($this->subject->isStopped());
124+
}
125+
106126
#[Framework\Attributes\Test]
107127
public function runExecutesCommandAndAllowsExecutionFailures(): void
108128
{
@@ -116,13 +136,6 @@ public function runExecutesCommandAndAllowsExecutionFailures(): void
116136
),
117137
);
118138

119-
$workingDirectory = $this->result->getWrittenDirectory();
120-
121-
$fileSystem = new Filesystem\Filesystem();
122-
if (!$fileSystem->exists($workingDirectory)) {
123-
$fileSystem->mkdir($workingDirectory);
124-
}
125-
126139
self::assertTrue($this->subject->run($this->result));
127140
self::assertFalse($this->subject->isStopped());
128141
self::assertStringContainsString('not found', $this->io->getOutput());
@@ -157,19 +170,23 @@ public function invalidCommandPrintProcessErrorMessage(): void
157170
),
158171
);
159172

160-
$workingDirectory = $this->result->getWrittenDirectory();
161-
162-
$fileSystem = new Filesystem\Filesystem();
163-
if (!$fileSystem->exists($workingDirectory)) {
164-
$fileSystem->mkdir($workingDirectory);
165-
}
166-
167173
$this->io->setUserInputs(['yes']);
168-
$actual = $this->subject->run($this->result);
169-
$this->io->getOutput();
174+
self::assertFalse($this->subject->run($this->result));
175+
}
170176

171-
self::assertFalse($actual);
177+
/**
178+
* @return Generator<string, array{bool, bool}>
179+
*/
180+
public static function runDoesNotExecuteCommandAndRespectsExecutionRequirementDataProvider(): Generator
181+
{
182+
yield 'required' => [true, false];
183+
yield 'optional' => [false, true];
184+
}
172185

173-
$fileSystem->remove($workingDirectory);
186+
protected function tearDown(): void
187+
{
188+
if ($this->filesystem->exists($this->result->getWrittenDirectory())) {
189+
$this->filesystem->remove($this->result->getWrittenDirectory());
190+
}
174191
}
175192
}

0 commit comments

Comments
 (0)