From 026a4aa7249692f7081ab5043c48483a81707da1 Mon Sep 17 00:00:00 2001 From: Artyom Osepyan Date: Tue, 30 Sep 2025 21:10:16 +0300 Subject: [PATCH 1/3] feat: generate .env.development file if missing Refs: https://github.com/RonasIT/laravel-project-initializator/issues/60 --- resources/.env.example | 65 +++++++++++++++++++ src/Commands/InitCommand.php | 11 ++++ tests/InitCommandTest.php | 40 ++++++++++++ tests/Support/Traits/InitCommandMockTrait.php | 14 ++++ 4 files changed, 130 insertions(+) create mode 100644 resources/.env.example diff --git a/resources/.env.example b/resources/.env.example new file mode 100644 index 0000000..35db1dd --- /dev/null +++ b/resources/.env.example @@ -0,0 +1,65 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=sqlite +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_DATABASE=laravel +# DB_USERNAME=root +# DB_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=log +MAIL_SCHEME=null +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" diff --git a/src/Commands/InitCommand.php b/src/Commands/InitCommand.php index f169937..14c326d 100644 --- a/src/Commands/InitCommand.php +++ b/src/Commands/InitCommand.php @@ -17,6 +17,8 @@ class InitCommand extends Command implements Isolatable { + public const string RESOURCES_PATH = 'vendor/ronasit/laravel-project-initializator/resources'; + public const string TEMPLATES_PATH = 'vendor/ronasit/laravel-project-initializator/resources/md/readme'; public const array RESOURCES_ITEMS = [ @@ -89,6 +91,15 @@ public function handle(): void $this->appUrl = $this->ask('Please enter an application URL', "https://api.dev.{$kebabName}.com"); + if (!file_exists(filename: '.env.example')) { + $envExamplePath = base_path(DIRECTORY_SEPARATOR . self::RESOURCES_PATH . DIRECTORY_SEPARATOR . '.env.example'); + copy($envExamplePath, '.env.example'); + } + + if (!file_exists('.env.development')) { + copy('.env.example', '.env.development'); + } + $envFile = (file_exists('.env')) ? '.env' : '.env.example'; $this->createOrUpdateConfigFile($envFile, '=', [ diff --git a/tests/InitCommandTest.php b/tests/InitCommandTest.php index d934c8e..d62dd11 100644 --- a/tests/InitCommandTest.php +++ b/tests/InitCommandTest.php @@ -13,6 +13,11 @@ public function testRunWithoutAdminAndReadmeCreationConvertAppNameToPascalCaseTe { $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); + $this->mockCopy( + [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], + ['.env.example', '.env.development'], + ); + $this->mockFileGetContent( [ 'arguments' => ['.env.example'], @@ -78,6 +83,11 @@ public function testRunWithoutAdminAndReadmeCreation() { $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); + $this->mockCopy( + [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], + ['.env.example', '.env.development'], + ); + $this->mockFileGetContent( [ 'arguments' => ['.env.example'], @@ -145,6 +155,11 @@ public function testRunWithAdminAndWithoutReadmeCreation() { $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); + $this->mockCopy( + [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], + ['.env.example', '.env.development'], + ); + $this->mockFileGetContent( [ 'arguments' => ['.env.example'], @@ -216,6 +231,11 @@ public function testRunWithAdminAndDefaultReadmeCreation() { $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); + $this->mockCopy( + [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], + ['.env.example', '.env.development'], + ); + $this->mockFileGetContent( [ 'arguments' => ['.env.example'], @@ -416,6 +436,11 @@ public function testRunWithAdminAndPartialReadmeCreation() { $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); + $this->mockCopy( + [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], + ['.env.example', '.env.development'], + ); + $this->mockFileGetContent( [ 'arguments' => ['.env.example'], @@ -551,6 +576,11 @@ public function testRunWithAdminAndFullReadmeCreationAndRemovingInitializatorIns { $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); + $this->mockCopy( + [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], + ['.env.example', '.env.development'], + ); + $this->mockFileGetContent( [ 'arguments' => ['.env.example'], @@ -720,6 +750,11 @@ public function testRunWithoutAdminAndUsingTelescope() { $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); + $this->mockCopy( + [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], + ['.env.example', '.env.development'], + ); + $this->mockFileGetContent( [ 'arguments' => ['.env.example'], @@ -859,6 +894,11 @@ public function testRunWithClerkMobileAppWithPintInstalled(): void { $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); + $this->mockCopy( + [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], + ['.env.example', '.env.development'], + ); + $this->mockFileGetContent( [ 'arguments' => ['.env.example'], diff --git a/tests/Support/Traits/InitCommandMockTrait.php b/tests/Support/Traits/InitCommandMockTrait.php index c26da68..7ae545b 100644 --- a/tests/Support/Traits/InitCommandMockTrait.php +++ b/tests/Support/Traits/InitCommandMockTrait.php @@ -54,6 +54,20 @@ public function mockFileGetContent(array ...$rawCallChain): void ); } + public function mockCopy(array ...$rawCallChain): void + { + $callChain = array_map(fn ($call) => $this->functionCall( + name: 'copy', + arguments: $call, + result: true, + ), $rawCallChain); + + $this->mockNativeFunction( + namespace: 'RonasIT\ProjectInitializator\Commands', + callChain: $callChain, + ); + } + protected function mockClassExists(array ...$rawCallChain): void { $callChain = array_map(fn ($call) => $this->functionCall( From b19187a1a3f51a137d83867b223aa2a6976b81c2 Mon Sep 17 00:00:00 2001 From: Artyom Osepyan Date: Thu, 2 Oct 2025 09:13:16 +0300 Subject: [PATCH 2/3] fix: remarks from reviewer --- resources/.env.example | 65 ------------------------------------ src/Commands/InitCommand.php | 15 +++------ tests/InitCommandTest.php | 8 ----- 3 files changed, 4 insertions(+), 84 deletions(-) delete mode 100644 resources/.env.example diff --git a/resources/.env.example b/resources/.env.example deleted file mode 100644 index 35db1dd..0000000 --- a/resources/.env.example +++ /dev/null @@ -1,65 +0,0 @@ -APP_NAME=Laravel -APP_ENV=local -APP_KEY= -APP_DEBUG=true -APP_URL=http://localhost - -APP_LOCALE=en -APP_FALLBACK_LOCALE=en -APP_FAKER_LOCALE=en_US - -APP_MAINTENANCE_DRIVER=file -# APP_MAINTENANCE_STORE=database - -PHP_CLI_SERVER_WORKERS=4 - -BCRYPT_ROUNDS=12 - -LOG_CHANNEL=stack -LOG_STACK=single -LOG_DEPRECATIONS_CHANNEL=null -LOG_LEVEL=debug - -DB_CONNECTION=sqlite -# DB_HOST=127.0.0.1 -# DB_PORT=3306 -# DB_DATABASE=laravel -# DB_USERNAME=root -# DB_PASSWORD= - -SESSION_DRIVER=database -SESSION_LIFETIME=120 -SESSION_ENCRYPT=false -SESSION_PATH=/ -SESSION_DOMAIN=null - -BROADCAST_CONNECTION=log -FILESYSTEM_DISK=local -QUEUE_CONNECTION=database - -CACHE_STORE=database -# CACHE_PREFIX= - -MEMCACHED_HOST=127.0.0.1 - -REDIS_CLIENT=phpredis -REDIS_HOST=127.0.0.1 -REDIS_PASSWORD=null -REDIS_PORT=6379 - -MAIL_MAILER=log -MAIL_SCHEME=null -MAIL_HOST=127.0.0.1 -MAIL_PORT=2525 -MAIL_USERNAME=null -MAIL_PASSWORD=null -MAIL_FROM_ADDRESS="hello@example.com" -MAIL_FROM_NAME="${APP_NAME}" - -AWS_ACCESS_KEY_ID= -AWS_SECRET_ACCESS_KEY= -AWS_DEFAULT_REGION=us-east-1 -AWS_BUCKET= -AWS_USE_PATH_STYLE_ENDPOINT=false - -VITE_APP_NAME="${APP_NAME}" diff --git a/src/Commands/InitCommand.php b/src/Commands/InitCommand.php index 7f430ee..fc481d8 100644 --- a/src/Commands/InitCommand.php +++ b/src/Commands/InitCommand.php @@ -17,8 +17,6 @@ class InitCommand extends Command implements Isolatable { - public const string RESOURCES_PATH = 'vendor/ronasit/laravel-project-initializator/resources'; - public const string TEMPLATES_PATH = 'vendor/ronasit/laravel-project-initializator/resources/md/readme'; public const array RESOURCES_ITEMS = [ @@ -92,21 +90,16 @@ public function handle(): void $this->appUrl = $this->ask('Please enter an application URL', "https://api.dev.{$kebabName}.com"); - if (!file_exists(filename: '.env.example')) { - $envExamplePath = base_path(DIRECTORY_SEPARATOR . self::RESOURCES_PATH . DIRECTORY_SEPARATOR . '.env.example'); - copy($envExamplePath, '.env.example'); - } - - if (!file_exists('.env.development')) { - copy('.env.example', '.env.development'); - } - $envFile = (file_exists('.env')) ? '.env' : '.env.example'; $this->createOrUpdateConfigFile($envFile, '=', [ 'APP_NAME' => $this->appName, ]); + if (!file_exists('.env.development')) { + copy('.env.example', '.env.development'); + } + $this->createOrUpdateConfigFile('.env.development', '=', [ 'APP_NAME' => $this->appName, 'APP_URL' => $this->appUrl, diff --git a/tests/InitCommandTest.php b/tests/InitCommandTest.php index e2f0b48..a92334d 100644 --- a/tests/InitCommandTest.php +++ b/tests/InitCommandTest.php @@ -14,7 +14,6 @@ public function testRunWithoutAdminAndReadmeCreationConvertAppNameToPascalCaseTe $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); $this->mockCopy( - [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], ['.env.example', '.env.development'], ); @@ -85,7 +84,6 @@ public function testRunWithoutAdminAndReadmeCreation() $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); $this->mockCopy( - [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], ['.env.example', '.env.development'], ); @@ -158,7 +156,6 @@ public function testRunWithAdminAndWithoutReadmeCreation() $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); $this->mockCopy( - [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], ['.env.example', '.env.development'], ); @@ -235,7 +232,6 @@ public function testRunWithAdminAndDefaultReadmeCreation() $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); $this->mockCopy( - [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], ['.env.example', '.env.development'], ); @@ -441,7 +437,6 @@ public function testRunWithAdminAndPartialReadmeCreation() $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); $this->mockCopy( - [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], ['.env.example', '.env.development'], ); @@ -582,7 +577,6 @@ public function testRunWithAdminAndFullReadmeCreationAndRemovingInitializatorIns $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); $this->mockCopy( - [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], ['.env.example', '.env.development'], ); @@ -757,7 +751,6 @@ public function testRunWithoutAdminAndUsingTelescope() $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); $this->mockCopy( - [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], ['.env.example', '.env.development'], ); @@ -902,7 +895,6 @@ public function testRunWithClerkMobileAppWithPintInstalled(): void $this->mockChangeConfig('config/auto-doc.php', 'auto_doc.php', 'auto_doc_after_changes.php'); $this->mockCopy( - [base_path('/vendor/ronasit/laravel-project-initializator/resources/.env.example'), '.env.example'], ['.env.example', '.env.development'], ); From 959ce4a825f9a09aafe61a444d3cd786d2cdf65a Mon Sep 17 00:00:00 2001 From: Artyom Osepyan Date: Thu, 2 Oct 2025 11:12:50 +0300 Subject: [PATCH 3/3] fix: remarks from reviewer --- tests/InitCommandTest.php | 26 +++++++++++++------ tests/Support/Traits/InitCommandMockTrait.php | 7 ++++- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/tests/InitCommandTest.php b/tests/InitCommandTest.php index 32b3b34..866fa33 100644 --- a/tests/InitCommandTest.php +++ b/tests/InitCommandTest.php @@ -20,6 +20,9 @@ public function testRunWithoutAdminAndReadmeCreationConvertAppNameToPascalCaseTe $this->mockNativeFunction( 'RonasIT\ProjectInitializator\Commands', + $this->callFileExists('.env', false), + $this->callFileExists('.env.development', false), + $this->callCopy('.env.example', '.env.development'), $this->callClassExists('Laravel\Telescope\TelescopeServiceProvider'), @@ -61,14 +64,15 @@ public function testRunWithoutAdminAndReadmeCreation() { $this->mockNativeFunction( '\Winter\LaravelConfigWriter', - $this->changeEnvFileCall('.env.example', 'env.example.yml', 'env.example_app_name_pascal_case.yml'), + $this->changeEnvFileCall('.env', 'env.example.yml', 'env.example_app_name_pascal_case.yml'), $this->changeEnvFileCall('.env.development', 'env.development.yml', 'env.development_app_name_pascal_case.yml'), $this->changeConfigFileCall(base_path('config/auto-doc.php'), 'auto_doc.php', 'auto_doc_after_changes.php'), ); $this->mockNativeFunction( 'RonasIT\ProjectInitializator\Commands', - $this->callCopy('.env.example', '.env.development'), + $this->callFileExists('.env'), + $this->callFileExists('.env.development'), $this->callFileGetContent(base_path('composer.json'), $this->getFixture('composer_with_pint_settings.json')), @@ -117,7 +121,8 @@ public function testRunWithAdminAndWithoutReadmeCreation() $this->mockNativeFunction( 'RonasIT\ProjectInitializator\Commands', - $this->callCopy('.env.example', '.env.development'), + $this->callFileExists('.env', false), + $this->callFileExists('.env.development'), $this->callFileGetContent(base_path('composer.json'), $this->getFixture('composer_with_pint_settings.json')), @@ -172,7 +177,8 @@ public function testRunWithAdminAndDefaultReadmeCreation() $this->mockNativeFunction( 'RonasIT\ProjectInitializator\Commands', - $this->callCopy('.env.example', '.env.development'), + $this->callFileExists('.env', false), + $this->callFileExists('.env.development'), $this->callFileGetContent($this->generateResourcePath('md/readme/README.md'), $this->getReadmeTemplateContent('README.md')), $this->callFileGetContent($this->generateResourcePath('md/readme/RESOURCES_AND_CONTACTS.md'), $this->getReadmeTemplateContent('RESOURCES_AND_CONTACTS.md')), @@ -293,7 +299,8 @@ public function testRunWithAdminAndPartialReadmeCreation() $this->mockNativeFunction( 'RonasIT\ProjectInitializator\Commands', - $this->callCopy('.env.example', '.env.development'), + $this->callFileExists('.env', false), + $this->callFileExists('.env.development'), $this->callFileGetContent($this->generateResourcePath('md/readme/README.md'), $this->getReadmeTemplateContent('README.md')), $this->callFileGetContent($this->generateResourcePath('md/readme/RESOURCES_AND_CONTACTS.md'), $this->getReadmeTemplateContent('RESOURCES_AND_CONTACTS.md')), @@ -392,7 +399,8 @@ public function testRunWithAdminAndFullReadmeCreationAndRemovingInitializatorIns $this->mockNativeFunction( 'RonasIT\ProjectInitializator\Commands', - $this->callCopy('.env.example', '.env.development'), + $this->callFileExists('.env', false), + $this->callFileExists('.env.development'), $this->callFileGetContent($this->generateResourcePath('md/readme/README.md'), $this->getReadmeTemplateContent('README.md')), $this->callFileGetContent($this->generateResourcePath('md/readme/RESOURCES_AND_CONTACTS.md'), $this->getReadmeTemplateContent('RESOURCES_AND_CONTACTS.md')), @@ -507,7 +515,8 @@ public function testRunWithoutAdminAndUsingTelescope() $this->mockNativeFunction( 'RonasIT\ProjectInitializator\Commands', - $this->callCopy('.env.example', '.env.development'), + $this->callFileExists('.env', false), + $this->callFileExists('.env.development'), $this->callFileGetContent($this->generateResourcePath('md/readme/README.md'), $this->getReadmeTemplateContent('README.md')), $this->callFileGetContent($this->generateResourcePath('md/readme/RESOURCES_AND_CONTACTS.md'), $this->getReadmeTemplateContent('RESOURCES_AND_CONTACTS.md')), @@ -612,7 +621,8 @@ public function testRunWithClerkMobileAppWithPintInstalled(): void $this->mockNativeFunction( 'RonasIT\ProjectInitializator\Commands', - $this->callCopy('.env.example', '.env.development'), + $this->callFileExists('.env', false), + $this->callFileExists('.env.development'), $this->callFileGetContent($this->generateResourcePath('md/readme/README.md'), $this->getReadmeTemplateContent('README.md')), $this->callFileGetContent($this->generateResourcePath('md/readme/RESOURCES_AND_CONTACTS.md'), $this->getReadmeTemplateContent('RESOURCES_AND_CONTACTS.md')), diff --git a/tests/Support/Traits/InitCommandMockTrait.php b/tests/Support/Traits/InitCommandMockTrait.php index 23fa523..6ecd679 100644 --- a/tests/Support/Traits/InitCommandMockTrait.php +++ b/tests/Support/Traits/InitCommandMockTrait.php @@ -18,6 +18,11 @@ protected function callCopy(string $source, string $result): array return $this->functionCall('copy', [$source, $result], true); } + protected function callFileExists(string $fileName, bool $result = true): array + { + return $this->functionCall('file_exists', [$fileName], $result); + } + protected function callFileGetContent(string $fileName, string $sourceFixture): array { return $this->functionCall('file_get_contents', [$fileName], $sourceFixture); @@ -45,7 +50,7 @@ protected function changeEnvFileCall(string $fileName, string $sourceFixture, st protected function changeConfigFileCall(string $fileName, string $sourceFixture, string $resultFixture): array { return [ - $this->functionCall('file_exists', [$fileName]), + $this->callFileExists($fileName), $this->callFileGetContent($fileName, $this->getFixture($sourceFixture)), $this->callFilePutContent($fileName, $this->getFixture($resultFixture)), ];