@@ -28,10 +28,13 @@ final class MakerTestEnvironment
2828 private string $ flexPath ;
2929 private string $ path ;
3030 private MakerTestProcess $ runnedMakerProcess ;
31+ private bool $ isWindows ;
3132
3233 private function __construct (
3334 private MakerTestDetails $ testDetails ,
3435 ) {
36+ $ this ->isWindows = str_contains (strtolower (\PHP_OS ), 'win ' );
37+
3538 $ this ->fs = new Filesystem ();
3639 $ this ->rootPath = realpath (__DIR__ .'/../../ ' );
3740 $ cachePath = $ this ->rootPath .'/tests/tmp/cache ' ;
@@ -124,13 +127,18 @@ private function changeRootNamespaceIfNeeded(): void
124127
125128 public function prepareDirectory (): void
126129 {
130+ // Copy MakerBundle to a "repo" directory for tests
131+ if (!file_exists ($ makerRepoPath = sprintf ('%s/maker-repo ' , $ this ->cachePath ))) {
132+ MakerTestProcess::create (sprintf ('git clone %s %s ' , $ this ->rootPath , $ makerRepoPath ), $ this ->cachePath )->run ();
133+ }
134+
127135 if (!$ this ->fs ->exists ($ this ->flexPath )) {
128136 $ this ->buildFlexSkeleton ();
129137 }
130138
131139 if (!$ this ->fs ->exists ($ this ->path )) {
132140 try {
133- // lets do some magic here git is faster than copy
141+ // let's do some magic here git is faster than copy
134142 MakerTestProcess::create (
135143 '\\' === \DIRECTORY_SEPARATOR ? 'git clone %FLEX_PATH% %APP_PATH% ' : 'git clone "$FLEX_PATH" "$APP_PATH" ' ,
136144 \dirname ($ this ->flexPath ),
@@ -141,6 +149,11 @@ public function prepareDirectory(): void
141149 )
142150 ->run ();
143151
152+ // In Window's we have to require MakerBundle in each project - git clone doesn't symlink well
153+ if ($ this ->isWindows ) {
154+ $ this ->composerRequireMakerBundle ($ this ->path );
155+ }
156+
144157 // install any missing dependencies
145158 $ dependencies = $ this ->determineMissingDependencies ();
146159 if ($ dependencies ) {
@@ -231,33 +244,21 @@ private function buildFlexSkeleton(): void
231244 $ targetVersion = $ this ->getTargetSkeletonVersion ();
232245 $ versionString = $ targetVersion ? sprintf (':%s ' , $ targetVersion ) : '' ;
233246
247+ $ flexProjectDir = sprintf ('flex_project%s ' , $ targetVersion );
248+
234249 MakerTestProcess::create (
235- sprintf ('composer create-project symfony/skeleton%s flex_project %s --prefer-dist --no-progress ' , $ versionString , $ targetVersion ),
250+ sprintf ('composer create-project symfony/skeleton%s %s --prefer-dist --no-progress ' , $ versionString , $ flexProjectDir ),
236251 $ this ->cachePath
237252 )->run ();
238253
239254 $ rootPath = str_replace ('\\' , '\\\\' , realpath (__DIR__ .'/../.. ' ));
240255
241- // processes any changes needed to the Flex project
242- $ replacements = [
243- [
244- 'filename ' => 'config/bundles.php ' ,
245- 'find ' => "Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], " ,
246- 'replace ' => "Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], \n Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], " ,
247- ],
248- [
249- // ugly way to autoload Maker & any other vendor libs needed in the command
250- 'filename ' => 'composer.json ' ,
251- 'find ' => '"App \\\Tests \\\": "tests/" ' ,
252- 'replace ' => sprintf (
253- '"App \\\Tests \\\": "tests/", ' ."\n" .' "Symfony \\\Bundle \\\MakerBundle \\\": "%s/src/", ' ."\n" .' "PhpParser \\\": "%s/vendor/nikic/php-parser/lib/PhpParser/" ' ,
254- // escape \ for Windows
255- $ rootPath ,
256- $ rootPath
257- ),
258- ],
259- ];
260- $ this ->processReplacements ($ replacements , $ this ->flexPath );
256+ $ this ->addMakerBundleRepoToComposer (sprintf ('%s/%s/composer.json ' , $ this ->cachePath , $ flexProjectDir ));
257+
258+ // In Linux, git plays well with symlinks - we can add maker to the flex skeleton.
259+ if (!$ this ->isWindows ) {
260+ $ this ->composerRequireMakerBundle (sprintf ('%s/%s ' , $ this ->cachePath , $ flexProjectDir ));
261+ }
261262
262263 if ($ _SERVER ['MAKER_ALLOW_DEV_DEPS_IN_APP ' ] ?? false ) {
263264 MakerTestProcess::create ('composer config minimum-stability dev ' , $ this ->flexPath )->run ();
@@ -411,4 +412,43 @@ private function getTargetSkeletonVersion(): ?string
411412 {
412413 return $ _SERVER ['SYMFONY_VERSION ' ] ?? '' ;
413414 }
415+
416+ private function composerRequireMakerBundle (string $ projectDirectory ): void
417+ {
418+ MakerTestProcess::create ('composer require --dev symfony/maker-bundle ' , $ projectDirectory )
419+ ->run ()
420+ ;
421+
422+ $ makerRepoSrcPath = sprintf ('%s/maker-repo/src ' , $ this ->cachePath );
423+
424+ // DX - So we can test local changes without having to commit them.
425+ if (!is_link ($ makerRepoSrcPath )) {
426+ $ this ->fs ->remove ($ makerRepoSrcPath );
427+ $ this ->fs ->symlink (sprintf ('%s/src ' , $ this ->rootPath ), $ makerRepoSrcPath );
428+ }
429+ }
430+
431+ /**
432+ * Adds Symfony/MakerBundle as a "path" repository to composer.json.
433+ */
434+ private function addMakerBundleRepoToComposer (string $ composerJsonPath ): void
435+ {
436+ $ composerJson = json_decode (
437+ file_get_contents ($ composerJsonPath ), true , 512 , \JSON_THROW_ON_ERROR );
438+
439+ // Require-dev is empty and composer complains about this being an array when we encode it again.
440+ unset($ composerJson ['require-dev ' ]);
441+
442+ $ composerJson ['repositories ' ]['symfony/maker-bundle ' ] = [
443+ 'type ' => 'path ' ,
444+ 'url ' => sprintf ('%s%smaker-repo ' , $ this ->cachePath , \DIRECTORY_SEPARATOR ),
445+ 'options ' => [
446+ 'versions ' => [
447+ 'symfony/maker-bundle ' => '9999.99 ' , // Arbitrary version to avoid stability conflicts
448+ ],
449+ ],
450+ ];
451+
452+ file_put_contents ($ composerJsonPath , json_encode ($ composerJson , \JSON_THROW_ON_ERROR | \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES ));
453+ }
414454}
0 commit comments