Skip to content

Commit a4d8497

Browse files
committed
feat: add default php version to project types
1 parent 6093d0d commit a4d8497

15 files changed

+135
-14
lines changed

src/Command/Docker/CreateDockerfileCommand.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,6 @@ class CreateDockerfileCommand extends AbstractCommand implements LocalProjectCom
3737
*/
3838
private const DEFAULT_ARCHITECTURE = 'arm64';
3939

40-
/**
41-
* Default PHP runtime tag used when none is configured.
42-
*/
43-
private const DEFAULT_PHP_TAG = 'php-74';
44-
4540
/**
4641
* The project Dockerfile.
4742
*
@@ -145,8 +140,10 @@ private function resolvePhpVersion(string $environment): string
145140

146141
if (empty($phpVersion) && !empty($environment)) {
147142
$phpVersion = $this->getProjectConfiguration()->getEnvironmentConfiguration($environment)->getPhpVersion();
143+
} elseif (empty($phpVersion)) {
144+
$phpVersion = $this->getProjectConfiguration()->getProjectType()->getDefaultPhpVersion();
148145
}
149146

150-
return empty($phpVersion) ? self::DEFAULT_PHP_TAG : $phpVersion;
147+
return $phpVersion;
151148
}
152149
}

src/Project/Initialization/DockerInitializationStep.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Ymir\Cli\ExecutionContext;
1919
use Ymir\Cli\Project\Configuration\ConfigurationChangeInterface;
2020
use Ymir\Cli\Project\Configuration\ImageDeploymentConfigurationChange;
21+
use Ymir\Cli\Project\Type\ProjectTypeInterface;
2122

2223
class DockerInitializationStep implements InitializationStepInterface
2324
{
@@ -49,14 +50,18 @@ public function __construct(DockerExecutable $dockerExecutable, Dockerfile $dock
4950
*/
5051
public function perform(ExecutionContext $context, array $projectRequirements): ?ConfigurationChangeInterface
5152
{
53+
if (empty($projectRequirements['type']) || !$projectRequirements['type'] instanceof ProjectTypeInterface) {
54+
return null;
55+
}
56+
5257
$output = $context->getOutput();
5358

5459
if (!$output->confirm('Do you want to deploy this project using a container image?')) {
5560
return null;
5661
}
5762

5863
if (!$this->dockerfile->exists() || $output->confirm('A <comment>Dockerfile</comment> already exists in the project directory. Do you want to overwrite it?', false)) {
59-
$this->dockerfile->create('arm64', 'php-74');
64+
$this->dockerfile->create('arm64', $projectRequirements['type']->getDefaultPhpVersion());
6065
}
6166

6267
if (!$this->dockerExecutable->isInstalled()) {

src/Project/Type/AbstractProjectType.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ protected function generateEnvironmentConfigurationArray(string $environment, ar
125125
$configuration = array_merge([
126126
'architecture' => 'arm64',
127127
'gateway' => false,
128+
'php' => $this->getDefaultPhpVersion(),
128129
], $baseConfiguration);
129130

130131
if ('staging' === $environment) {

src/Project/Type/AbstractWordPressProjectType.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ public function getAssetFiles(string $directory): Finder
4040
->notName(['*.mo', '*.po']);
4141
}
4242

43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public function getDefaultPhpVersion(): string
47+
{
48+
return '7.4';
49+
}
50+
4351
/**
4452
* {@inheritDoc}
4553
*/

src/Project/Type/LaravelProjectType.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ public function getBuildSteps(): array
8888
];
8989
}
9090

91+
/**
92+
* {@inheritdoc}
93+
*/
94+
public function getDefaultPhpVersion(): string
95+
{
96+
return '8.3';
97+
}
98+
9199
/**
92100
* {@inheritDoc}
93101
*/

src/Project/Type/ProjectTypeInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ public function getAssetFiles(string $directory): Finder;
3838
*/
3939
public function getBuildSteps(): array;
4040

41+
/**
42+
* Get the default PHP version for the project type.
43+
*/
44+
public function getDefaultPhpVersion(): string;
45+
4146
/**
4247
* Get the Finder object for finding all the files that we want to exclude from the final build.
4348
*/

tests/Integration/Command/Docker/CreateDockerfileCommandTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ protected function setUp(): void
3838
$this->dockerfile = new Dockerfile($this->filesystem, $this->tempDir, realpath(__DIR__.'/../../../../stubs'));
3939
}
4040

41+
public static function provideProjectTypeDefaultPhpVersions(): array
42+
{
43+
return [
44+
['laravel', 'php-83'],
45+
['wordpress', 'php-74'],
46+
['bedrock', 'php-74'],
47+
['radicle', 'php-74'],
48+
];
49+
}
50+
4151
public function testCreateDockerfile(): void
4252
{
4353
$this->setupActiveTeam();
@@ -87,6 +97,22 @@ public function testCreateDockerfileForEnvironment(): void
8797
$this->assertStringContainsString('FROM --platform=linux/amd64 ymirapp/php-runtime:php-81', (string) file_get_contents($this->tempDir.'/staging.Dockerfile'));
8898
}
8999

100+
/**
101+
* @dataProvider provideProjectTypeDefaultPhpVersions
102+
*/
103+
public function testCreateDockerfileUsesProjectTypeDefaultPhpVersion(string $projectType, string $phpTag): void
104+
{
105+
$this->setupActiveTeam();
106+
$this->setupValidProject(1, 'project', ['production' => []], $projectType);
107+
108+
$this->bootApplication([new CreateDockerfileCommand($this->apiClient, $this->createExecutionContextFactory(), $this->dockerfile)]);
109+
110+
$this->executeCommand(CreateDockerfileCommand::NAME, [], ['no']);
111+
112+
$this->assertFileExists($this->tempDir.'/Dockerfile');
113+
$this->assertStringContainsString(sprintf('FROM --platform=linux/arm64 ymirapp/arm-php-runtime:%s', $phpTag), (string) file_get_contents($this->tempDir.'/Dockerfile'));
114+
}
115+
90116
public function testCreateDockerfileWithArchitectureOption(): void
91117
{
92118
$this->setupActiveTeam();

tests/Integration/Command/TestCase.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ protected function setupValidProject(int $projectId = 1, string $projectName = '
184184

185185
$this->projectTypeMock = \Mockery::mock($projectTypeClass);
186186
$this->projectTypeMock->shouldReceive('getSlug')->andReturn($projectTypeSlug);
187+
$this->projectTypeMock->shouldReceive('getDefaultPhpVersion')->andReturn($this->resolveDefaultPhpVersion($projectTypeSlug));
187188

188189
$this->projectConfiguration = new ProjectConfiguration($this->filesystem, [$this->projectTypeMock], $this->tempDir.'/ymir.yml');
189190
$this->projectConfiguration->createNew($project, collect(), $this->projectTypeMock);
@@ -194,4 +195,21 @@ protected function setupValidProject(int $projectId = 1, string $projectName = '
194195

195196
return $project;
196197
}
198+
199+
/**
200+
* Resolve the default PHP version for a project type slug.
201+
*/
202+
private function resolveDefaultPhpVersion(string $projectTypeSlug): string
203+
{
204+
switch ($projectTypeSlug) {
205+
case 'laravel':
206+
return '8.3';
207+
case 'wordpress':
208+
case 'bedrock':
209+
case 'radicle':
210+
return '7.4';
211+
default:
212+
throw new \InvalidArgumentException(sprintf('Unsupported project type slug "%s" for resolving default PHP version', $projectTypeSlug));
213+
}
214+
}
197215
}

tests/Unit/Project/Initialization/DockerInitializationStepTest.php

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Ymir\Cli\ExecutionContext;
2020
use Ymir\Cli\Project\Configuration\ImageDeploymentConfigurationChange;
2121
use Ymir\Cli\Project\Initialization\DockerInitializationStep;
22+
use Ymir\Cli\Project\Type\ProjectTypeInterface;
2223
use Ymir\Cli\Tests\TestCase;
2324

2425
class DockerInitializationStepTest extends TestCase
@@ -59,35 +60,43 @@ protected function setUp(): void
5960

6061
public function testPerformAsksToOverwriteExistingDockerfile(): void
6162
{
63+
$projectType = \Mockery::mock(ProjectTypeInterface::class);
64+
$projectType->shouldReceive('getDefaultPhpVersion')->once()->andReturn('php-version');
65+
6266
$this->output->shouldReceive('confirm')->with('Do you want to deploy this project using a container image?')->once()->andReturn(true);
6367
$this->dockerfile->shouldReceive('exists')->once()->andReturn(true);
6468
$this->output->shouldReceive('confirm')->with('A <comment>Dockerfile</comment> already exists in the project directory. Do you want to overwrite it?', false)->once()->andReturn(true);
65-
$this->dockerfile->shouldReceive('create')->once()->with('arm64', 'php-74');
69+
$this->dockerfile->shouldReceive('create')->once()->with('arm64', 'php-version');
6670
$this->dockerExecutable->shouldReceive('isInstalled')->once()->andReturn(true);
6771

6872
$step = new DockerInitializationStep($this->dockerExecutable, $this->dockerfile);
6973

70-
$result = $step->perform($this->context, []);
74+
$result = $step->perform($this->context, ['type' => $projectType]);
7175

7276
$this->assertInstanceOf(ImageDeploymentConfigurationChange::class, $result);
7377
}
7478

7579
public function testPerformCreatesDockerfileAndReturnsImageDeploymentConfigurationChange(): void
7680
{
81+
$projectType = \Mockery::mock(ProjectTypeInterface::class);
82+
$projectType->shouldReceive('getDefaultPhpVersion')->once()->andReturn('php-version');
83+
7784
$this->output->shouldReceive('confirm')->with('Do you want to deploy this project using a container image?')->once()->andReturn(true);
7885
$this->dockerfile->shouldReceive('exists')->once()->andReturn(false);
79-
$this->dockerfile->shouldReceive('create')->once()->with('arm64', 'php-74');
86+
$this->dockerfile->shouldReceive('create')->once()->with('arm64', 'php-version');
8087
$this->dockerExecutable->shouldReceive('isInstalled')->once()->andReturn(true);
8188

8289
$step = new DockerInitializationStep($this->dockerExecutable, $this->dockerfile);
8390

84-
$result = $step->perform($this->context, []);
91+
$result = $step->perform($this->context, ['type' => $projectType]);
8592

8693
$this->assertInstanceOf(ImageDeploymentConfigurationChange::class, $result);
8794
}
8895

8996
public function testPerformDoesNotOverwriteExistingDockerfileIfUserDeclines(): void
9097
{
98+
$projectType = \Mockery::mock(ProjectTypeInterface::class);
99+
91100
$this->output->shouldReceive('confirm')->with('Do you want to deploy this project using a container image?')->once()->andReturn(true);
92101
$this->dockerfile->shouldReceive('exists')->once()->andReturn(true);
93102
$this->output->shouldReceive('confirm')->with('A <comment>Dockerfile</comment> already exists in the project directory. Do you want to overwrite it?', false)->once()->andReturn(false);
@@ -96,31 +105,62 @@ public function testPerformDoesNotOverwriteExistingDockerfileIfUserDeclines(): v
96105

97106
$step = new DockerInitializationStep($this->dockerExecutable, $this->dockerfile);
98107

99-
$result = $step->perform($this->context, []);
108+
$result = $step->perform($this->context, ['type' => $projectType]);
100109

101110
$this->assertInstanceOf(ImageDeploymentConfigurationChange::class, $result);
102111
}
103112

104113
public function testPerformReturnsNullIfUserDeclinesImageDeployment(): void
105114
{
115+
$projectType = \Mockery::mock(ProjectTypeInterface::class);
116+
106117
$this->output->shouldReceive('confirm')->with('Do you want to deploy this project using a container image?')->once()->andReturn(false);
107118

108119
$step = new DockerInitializationStep($this->dockerExecutable, $this->dockerfile);
109120

121+
$this->assertNull($step->perform($this->context, ['type' => $projectType]));
122+
}
123+
124+
public function testPerformReturnsNullWithoutProjectType(): void
125+
{
126+
$this->output->shouldNotReceive('confirm');
127+
128+
$step = new DockerInitializationStep($this->dockerExecutable, $this->dockerfile);
129+
110130
$this->assertNull($step->perform($this->context, []));
111131
}
112132

113133
public function testPerformShowsWarningIfDockerIsNotInstalled(): void
114134
{
135+
$projectType = \Mockery::mock(ProjectTypeInterface::class);
136+
$projectType->shouldReceive('getDefaultPhpVersion')->once()->andReturn('php-version');
137+
115138
$this->output->shouldReceive('confirm')->with('Do you want to deploy this project using a container image?')->once()->andReturn(true);
116139
$this->dockerfile->shouldReceive('exists')->once()->andReturn(false);
117-
$this->dockerfile->shouldReceive('create')->once()->with('arm64', 'php-74');
140+
$this->dockerfile->shouldReceive('create')->once()->with('arm64', 'php-version');
118141
$this->dockerExecutable->shouldReceive('isInstalled')->once()->andReturn(false);
119142
$this->output->shouldReceive('warning')->with("<comment>Docker</comment> wasn't detected and is required to deploy the project locally")->once();
120143

121144
$step = new DockerInitializationStep($this->dockerExecutable, $this->dockerfile);
122145

123-
$result = $step->perform($this->context, []);
146+
$result = $step->perform($this->context, ['type' => $projectType]);
147+
148+
$this->assertInstanceOf(ImageDeploymentConfigurationChange::class, $result);
149+
}
150+
151+
public function testPerformUsesProjectTypeDefaultPhpVersion(): void
152+
{
153+
$projectType = \Mockery::mock(ProjectTypeInterface::class);
154+
$projectType->shouldReceive('getDefaultPhpVersion')->once()->andReturn('php-version');
155+
156+
$this->output->shouldReceive('confirm')->with('Do you want to deploy this project using a container image?')->once()->andReturn(true);
157+
$this->dockerfile->shouldReceive('exists')->once()->andReturn(false);
158+
$this->dockerfile->shouldReceive('create')->once()->with('arm64', 'php-version');
159+
$this->dockerExecutable->shouldReceive('isInstalled')->once()->andReturn(true);
160+
161+
$step = new DockerInitializationStep($this->dockerExecutable, $this->dockerfile);
162+
163+
$result = $step->perform($this->context, ['type' => $projectType]);
124164

125165
$this->assertInstanceOf(ImageDeploymentConfigurationChange::class, $result);
126166
}

tests/Unit/Project/Type/AbstractProjectTypeTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,25 @@ protected function tearDown(): void
3838
public function testGenerateEnvironmentConfigurationForProduction(): void
3939
{
4040
$projectType = \Mockery::mock(AbstractProjectType::class, [\Mockery::mock(Filesystem::class)])->makePartial();
41+
$projectType->shouldReceive('getDefaultPhpVersion')->once()->andReturn('7.4');
4142

4243
$this->assertSame([
4344
'architecture' => 'arm64',
4445
'gateway' => false,
46+
'php' => '7.4',
4547
'foo' => 'bar',
4648
], $projectType->generateEnvironmentConfiguration('production', ['foo' => 'bar'])->toArray());
4749
}
4850

4951
public function testGenerateEnvironmentConfigurationForStaging(): void
5052
{
5153
$projectType = \Mockery::mock(AbstractProjectType::class, [\Mockery::mock(Filesystem::class)])->makePartial();
54+
$projectType->shouldReceive('getDefaultPhpVersion')->once()->andReturn('7.4');
5255

5356
$this->assertSame([
5457
'architecture' => 'arm64',
5558
'gateway' => false,
59+
'php' => '7.4',
5660
'foo' => 'bar',
5761
'cron' => false,
5862
'warmup' => false,

0 commit comments

Comments
 (0)