1414namespace Ymir \Cli \Command \Laravel ;
1515
1616use Illuminate \Support \Collection ;
17- use Symfony \Component \Filesystem \Filesystem ;
18- use Symfony \Component \Yaml \Yaml ;
1917use Ymir \Cli \ApiClient ;
2018use Ymir \Cli \Command \AbstractCommand ;
2119use Ymir \Cli \Command \LocalProjectCommandInterface ;
22- use Ymir \Cli \Dockerfile ;
23- use Ymir \Cli \Exception \InvalidInputException ;
2420use Ymir \Cli \Exception \Project \UnsupportedProjectException ;
21+ use Ymir \Cli \Exception \RuntimeException ;
2522use Ymir \Cli \ExecutionContextFactory ;
23+ use Ymir \Cli \Laravel \VaporDockerfileMigrator ;
2624use Ymir \Cli \Project \Configuration \Laravel \VaporConfigurationChange ;
2725use Ymir \Cli \Project \EnvironmentConfiguration ;
2826use Ymir \Cli \Project \Type \LaravelProjectType ;
2927use Ymir \Cli \Support \Arr ;
28+ use Ymir \Cli \YamlParser ;
3029
3130class MigrateVaporCommand extends AbstractCommand implements LocalProjectCommandInterface
3231{
@@ -38,28 +37,28 @@ class MigrateVaporCommand extends AbstractCommand implements LocalProjectCommand
3837 public const NAME = 'laravel:vapor:migrate ' ;
3938
4039 /**
41- * The project Dockerfile service.
40+ * The Dockerfile migrator service.
4241 *
43- * @var Dockerfile
42+ * @var VaporDockerfileMigrator
4443 */
45- private $ dockerfile ;
44+ private $ vaporDockerfileMigrator ;
4645
4746 /**
48- * The file system .
47+ * The vapor configuration parser .
4948 *
50- * @var Filesystem
49+ * @var YamlParser
5150 */
52- private $ filesystem ;
51+ private $ yamlParser ;
5352
5453 /**
5554 * Constructor.
5655 */
57- public function __construct (ApiClient $ apiClient , ExecutionContextFactory $ contextFactory , Dockerfile $ dockerfile , Filesystem $ filesystem )
56+ public function __construct (ApiClient $ apiClient , ExecutionContextFactory $ contextFactory , VaporDockerfileMigrator $ vaporDockerfileMigrator , YamlParser $ yamlParser )
5857 {
5958 parent ::__construct ($ apiClient , $ contextFactory );
6059
61- $ this ->dockerfile = $ dockerfile ;
62- $ this ->filesystem = $ filesystem ;
60+ $ this ->vaporDockerfileMigrator = $ vaporDockerfileMigrator ;
61+ $ this ->yamlParser = $ yamlParser ;
6362 }
6463
6564 /**
@@ -85,7 +84,7 @@ protected function perform()
8584 $ vaporEnvironments = Arr::get ($ vaporConfiguration , 'environments ' );
8685
8786 if (!is_array ($ vaporEnvironments )) {
88- throw new InvalidInputException ('No valid "environments" key found in vapor.yml file ' );
87+ throw new RuntimeException ('No valid "environments" key found in vapor.yml file ' );
8988 }
9089
9190 $ matchedEnvironments = $ this ->getProjectConfiguration ()->getEnvironments ()->keys ()->intersect (collect ($ vaporEnvironments )->keys ())->values ();
@@ -100,75 +99,24 @@ protected function perform()
10099
101100 $ imageDeploymentEnvironmentConfigurations = $ this ->getImageDeploymentEnvironmentConfigurations ($ matchedEnvironments );
102101
103- if (!$ imageDeploymentEnvironmentConfigurations ->isEmpty ()) {
104- $ this ->migrateDockerfiles ($ imageDeploymentEnvironmentConfigurations );
102+ if ($ imageDeploymentEnvironmentConfigurations ->isNotEmpty ()) {
103+ $ migrationResult = $ this ->migrateDockerfiles ($ imageDeploymentEnvironmentConfigurations );
104+
105+ collect ($ migrationResult ['created_dockerfiles ' ])->each (function (array $ createdDockerfile ): void {
106+ $ this ->output ->info ($ this ->generateDockerfileCreatedMessage ($ createdDockerfile ));
107+ });
105108 }
106109
107110 $ this ->output ->info ('Vapor configuration migrated into <comment>ymir.yml</comment> file for the following environment(s): ' );
108111 $ this ->output ->list ($ matchedEnvironments );
109112 }
110113
111- /**
112- * Back up all relevant Dockerfiles before migration.
113- */
114- private function backupDockerfiles (Collection $ environmentConfigurations , bool $ backupGlobalDockerfile ): void
115- {
116- $ dockerfilePaths = $ environmentConfigurations
117- ->map (function (EnvironmentConfiguration $ environmentConfiguration ): string {
118- return $ this ->generateDockerfilePath ($ environmentConfiguration ->getName ());
119- })
120- ->unique ();
121-
122- if ($ backupGlobalDockerfile ) {
123- $ dockerfilePaths ->push ($ this ->generateDockerfilePath ());
124- }
125-
126- $ dockerfilePaths ->filter (function (string $ dockerfilePath ): bool {
127- return $ this ->filesystem ->exists ($ dockerfilePath );
128- })->each (function (string $ dockerfilePath ): void {
129- $ this ->filesystem ->rename ($ dockerfilePath , $ dockerfilePath .'.bak ' , true );
130- });
131- }
132-
133- /**
134- * Create a Dockerfile.
135- */
136- private function createDockerfile (EnvironmentConfiguration $ environmentConfiguration , string $ phpVersion , bool $ globalDockerfile ): void
137- {
138- $ architecture = $ environmentConfiguration ->getArchitecture () ?: 'x86_64 ' ;
139- $ environment = $ globalDockerfile ? '' : $ environmentConfiguration ->getName ();
140-
141- $ this ->dockerfile ->create ($ architecture , $ phpVersion , $ environment );
142-
143- $ this ->output ->info ($ this ->generateDockerfileCreatedMessage ($ architecture , $ environment , $ phpVersion ));
144-
145- if ($ globalDockerfile ) {
146- $ this ->output ->comment (sprintf ('Using <comment>%s</comment> environment configuration ' , $ environmentConfiguration ->getName ()));
147- }
148- }
149-
150114 /**
151115 * Generate the success message after creating the Dockerfile.
152116 */
153- private function generateDockerfileCreatedMessage (string $ architecture , string $ environment , string $ phpVersion ): string
154- {
155- return sprintf ('Created <comment>%s</comment> for PHP <comment>%s</comment> and <comment>%s</comment> architecture ' , Dockerfile::getFileName ($ environment ), $ phpVersion , $ architecture );
156- }
157-
158- /**
159- * Generate the full Dockerfile path.
160- */
161- private function generateDockerfilePath (string $ environment = '' ): string
162- {
163- return sprintf ('%s/%s ' , $ this ->getProjectDirectory (), Dockerfile::getFileName ($ environment ));
164- }
165-
166- /**
167- * Get the fallback PHP version from environment or project type.
168- */
169- private function getFallbackPhpVersion (EnvironmentConfiguration $ environmentConfiguration ): string
117+ private function generateDockerfileCreatedMessage (array $ createdDockerfile ): string
170118 {
171- return empty ( $ environmentConfiguration -> getPhpVersion ()) ? $ this -> getProjectConfiguration ()-> getProjectType ()-> getDefaultPhpVersion () : $ environmentConfiguration -> getPhpVersion ( );
119+ return sprintf ( ' Created <comment>%s</comment> for PHP <comment>%s</comment> and <comment>%s</comment> architecture ' , $ createdDockerfile [ ' name ' ], $ createdDockerfile [ ' php_version ' ], $ createdDockerfile [ ' architecture ' ] );
172120 }
173121
174122 /**
@@ -191,77 +139,22 @@ private function getImageDeploymentEnvironmentConfigurations(Collection $matched
191139 */
192140 private function getVaporConfiguration (): array
193141 {
194- $ vaporConfigurationFilePath = $ this ->getProjectDirectory ().'/vapor.yml ' ;
195-
196- if (!$ this ->filesystem ->exists ($ vaporConfigurationFilePath )) {
197- throw new InvalidInputException (sprintf ('No vapor configuration file found at "%s" ' , $ vaporConfigurationFilePath ));
198- }
199-
200- try {
201- $ vaporConfiguration = Yaml::parse ((string ) file_get_contents ($ vaporConfigurationFilePath ));
202- } catch (\Throwable $ exception ) {
203- throw new InvalidInputException (sprintf ('Error parsing Vapor configuration file: %s ' , $ exception ->getMessage ()));
204- }
142+ $ vaporConfiguration = $ this ->yamlParser ->parse ($ this ->getProjectDirectory ().'/vapor.yml ' );
205143
206- if (! is_array ( $ vaporConfiguration) ) {
207- throw new InvalidInputException ( ' Error parsing Vapor configuration file ' );
144+ if (null === $ vaporConfiguration ) {
145+ throw new RuntimeException ( ' Unable to migrate Vapor configuration because "vapor.yml" is missing from the project directory ' );
208146 }
209147
210148 return $ vaporConfiguration ;
211149 }
212150
213151 /**
214- * Migrate Dockerfiles for image deployment environments.
215- */
216- private function migrateDockerfiles (Collection $ environmentConfigurations ): void
217- {
218- $ phpVersions = $ environmentConfigurations ->mapWithKeys (function (EnvironmentConfiguration $ environmentConfiguration ): array {
219- return [$ environmentConfiguration ->getName () => $ this ->resolvePhpVersion ($ environmentConfiguration , $ this ->generateDockerfilePath ($ environmentConfiguration ->getName ()))];
220- });
221- $ createGlobalDockerfile = $ this ->output ->confirm ('Do you want to create one global <comment>Dockerfile</comment> for all image deployment environments? ' , false );
222- $ sourceEnvironmentConfiguration = $ this ->selectDockerfileSourceEnvironmentConfiguration ($ environmentConfigurations );
223- $ dockerfileConfigurations = $ createGlobalDockerfile ? collect ([$ sourceEnvironmentConfiguration ]) : $ environmentConfigurations ;
224-
225- $ this ->backupDockerfiles ($ environmentConfigurations , $ createGlobalDockerfile );
226-
227- $ dockerfileConfigurations ->each (function (EnvironmentConfiguration $ environmentConfiguration ) use ($ createGlobalDockerfile , $ phpVersions ): void {
228- $ this ->createDockerfile ($ environmentConfiguration , (string ) $ phpVersions ->get ($ environmentConfiguration ->getName ()), $ createGlobalDockerfile );
229- });
230- }
231-
232- /**
233- * Resolve the PHP version from the existing Dockerfile content.
152+ * Migrate the Dockerfiles for image deployment environments.
234153 */
235- private function resolvePhpVersion ( EnvironmentConfiguration $ environmentConfiguration , string $ dockerfilePath ): string
154+ private function migrateDockerfiles ( Collection $ imageDeploymentEnvironmentConfigurations ): array
236155 {
237- if (!$ this ->filesystem ->exists ($ dockerfilePath )) {
238- return $ this ->getFallbackPhpVersion ($ environmentConfiguration );
239- }
240-
241- $ dockerfileContent = (string ) file_get_contents ($ dockerfilePath );
242-
243- if (1 !== preg_match ('/:php-?(?:(\d+)\.(\d+)|(\d{2,3}))/i ' , $ dockerfileContent , $ matches )) {
244- return $ this ->getFallbackPhpVersion ($ environmentConfiguration );
245- } elseif (!empty ($ matches [1 ]) && !empty ($ matches [2 ])) {
246- return sprintf ('%s.%s ' , $ matches [1 ], $ matches [2 ]);
247- } elseif (empty ($ matches [3 ])) {
248- return $ this ->getFallbackPhpVersion ($ environmentConfiguration );
249- }
250-
251- $ version = $ matches [3 ];
252-
253- return 2 === strlen ($ version ) ? sprintf ('%s.%s ' , $ version [0 ], $ version [1 ]) : sprintf ('%s.%s ' , $ version [0 ], substr ($ version , 1 ));
254- }
255-
256- /**
257- * Select the source environment for Dockerfile generation.
258- */
259- private function selectDockerfileSourceEnvironmentConfiguration (Collection $ environmentConfigurations ): EnvironmentConfiguration
260- {
261- $ productionEnvironmentConfiguration = $ environmentConfigurations ->first (function (EnvironmentConfiguration $ environmentConfiguration ): bool {
262- return 'production ' === $ environmentConfiguration ->getName ();
263- });
264-
265- return $ productionEnvironmentConfiguration instanceof EnvironmentConfiguration ? $ productionEnvironmentConfiguration : $ environmentConfigurations ->first ();
156+ return $ this ->output ->confirm ('Do you want to create one global <comment>Dockerfile</comment> for all image deployment environments? ' , false )
157+ ? $ this ->vaporDockerfileMigrator ->migrateGlobalDockerfile ($ imageDeploymentEnvironmentConfigurations , $ this ->getProjectDirectory (), $ this ->getProjectConfiguration ()->getProjectType ())
158+ : $ this ->vaporDockerfileMigrator ->migrateEnvironmentDockerfiles ($ imageDeploymentEnvironmentConfigurations , $ this ->getProjectDirectory (), $ this ->getProjectConfiguration ()->getProjectType ());
266159 }
267160}
0 commit comments