From 31b12b11682dfed3e8a367a825824a4d2636efd0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 12 Dec 2018 14:35:38 +0100 Subject: [PATCH 1/2] Add "dump-env" command to compile .env files to .env.local.php --- src/Command/DumpEnvCommand.php | 92 ++++++++++++++++++++++++++++++++++ src/Flex.php | 1 + 2 files changed, 93 insertions(+) create mode 100644 src/Command/DumpEnvCommand.php diff --git a/src/Command/DumpEnvCommand.php b/src/Command/DumpEnvCommand.php new file mode 100644 index 000000000..da291225d --- /dev/null +++ b/src/Command/DumpEnvCommand.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Flex\Command; + +use Composer\Command\BaseCommand; +use Composer\Config; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Dotenv\Dotenv; +use Symfony\Flex\Options; + +class DumpEnvCommand extends BaseCommand +{ + private $config; + private $options; + + public function __construct(Config $config, Options $options) + { + $this->config = $config; + $this->options = $options; + + parent::__construct(); + } + + protected function configure() + { + $this->setName('symfony:dump-env') + ->setAliases(['dump-env']) + ->setDescription('Compiles .env files to .env.local.php.') + ->setDefinition([ + new InputArgument('env', InputArgument::REQUIRED, 'The application environment to dump .env files for - e.g. "prod".'), + ]) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + require $this->config->get('vendor-dir').'/autoload.php'; + + if (!class_exists(Dotenv::class)) { + throw new \RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); + } + + $_SERVER['APP_ENV'] = $env = $input->getArgument('env'); + $path = $this->options->get('root-dir').'/.env'; + $dotenv = new Dotenv(); + + if (method_exists($dotenv, 'loadEnv')) { + $dotenv->loadEnv($path); + } else { + // fallback code in case your Dotenv component is not 4.2 or higher (when loadEnv() was added) + $dotenv->load(file_exists($path) || !file_exists($p = "$path.dist") ? $path : $p); + + if ('test' !== $env && file_exists($p = "$path.local")) { + $dotenv->load($p); + } + + if (file_exists($p = "$path.$env")) { + $dotenv->load($p); + } + + if (file_exists($p = "$path.$env.local")) { + $dotenv->load($p); + } + } + + $vars = array_flip(explode(',', ($_SERVER['SYMFONY_DOTENV_VARS'] ?? '').',APP_ENV')); + $vars = array_intersect_key($_SERVER, $vars); + $vars = var_export($vars, true); + $vars = <<getIO()->writeError('Successfully dumped .env files in .env.local.php'); + } +} diff --git a/src/Flex.php b/src/Flex.php index d343399d3..e0b6aeb2d 100644 --- a/src/Flex.php +++ b/src/Flex.php @@ -233,6 +233,7 @@ public function activate(Composer $composer, IOInterface $io) $app->add(new Command\UnpackCommand($resolver)); $app->add(new Command\SyncRecipesCommand($this)); $app->add(new Command\GenerateIdCommand($this)); + $app->add(new Command\DumpEnvCommand($this->config, $this->options)); break; } From 7e561d54a3a29a422bf35981f3ff89384c6d625d Mon Sep 17 00:00:00 2001 From: renanbr Date: Fri, 18 Jan 2019 17:39:35 +0100 Subject: [PATCH 2/2] add --empty option --- composer.json | 1 + phpunit.xml.dist | 2 +- src/Command/DumpEnvCommand.php | 44 ++++++++----- tests/Command/DumpEnvCommandTest.php | 98 ++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 18 deletions(-) create mode 100644 tests/Command/DumpEnvCommandTest.php diff --git a/composer.json b/composer.json index a02deb241..a42c7fb38 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ }, "require-dev": { "composer/composer": "^1.0.2", + "symfony/dotenv": "^3.4|^4.0", "symfony/phpunit-bridge": "^3.4.19|^4.1.8", "symfony/process": "^2.7|^3.0|^4.0" }, diff --git a/phpunit.xml.dist b/phpunit.xml.dist index be4197f1e..a2f9dfa26 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,6 +1,6 @@ -setDefinition([ new InputArgument('env', InputArgument::REQUIRED, 'The application environment to dump .env files for - e.g. "prod".'), ]) + ->addOption('empty', null, InputOption::VALUE_NONE, 'Ignore the content of .env files') ; } protected function execute(InputInterface $input, OutputInterface $output) + { + $_SERVER['APP_ENV'] = $env = $input->getArgument('env'); + $path = $this->options->get('root-dir').'/.env'; + + if (!$input->getOption('empty')) { + $this->loadEnv($path, $env); + } + + $vars = array_flip(explode(',', ($_SERVER['SYMFONY_DOTENV_VARS'] ?? '').',APP_ENV')); + $vars = array_intersect_key($_SERVER, $vars); + $vars = var_export($vars, true); + $vars = <<getIO()->writeError('Successfully dumped .env files in .env.local.php'); + } + + private function loadEnv(string $path, string $env) { require $this->config->get('vendor-dir').'/autoload.php'; @@ -51,8 +78,6 @@ protected function execute(InputInterface $input, OutputInterface $output) throw new \RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); } - $_SERVER['APP_ENV'] = $env = $input->getArgument('env'); - $path = $this->options->get('root-dir').'/.env'; $dotenv = new Dotenv(); if (method_exists($dotenv, 'loadEnv')) { @@ -73,20 +98,5 @@ protected function execute(InputInterface $input, OutputInterface $output) $dotenv->load($p); } } - - $vars = array_flip(explode(',', ($_SERVER['SYMFONY_DOTENV_VARS'] ?? '').',APP_ENV')); - $vars = array_intersect_key($_SERVER, $vars); - $vars = var_export($vars, true); - $vars = <<getIO()->writeError('Successfully dumped .env files in .env.local.php'); } } diff --git a/tests/Command/DumpEnvCommandTest.php b/tests/Command/DumpEnvCommandTest.php new file mode 100644 index 000000000..22e3379dc --- /dev/null +++ b/tests/Command/DumpEnvCommandTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Flex\Tests\Command; + +use Composer\Config; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Flex\Command\DumpEnvCommand; +use Symfony\Flex\Options; + +class DumpEnvCommandTest extends TestCase +{ + public function testFileIsCreated() + { + @mkdir(FLEX_TEST_DIR); + $env = FLEX_TEST_DIR.'/.env'; + $envLocal = FLEX_TEST_DIR.'/.env.local.php'; + @unlink($env); + @unlink($envLocal); + + $envContent = <<createCommandDumpEnv(); + $command->execute([ + 'env' => 'prod', + ]); + + $this->assertFileExists($envLocal); + + $vars = require $envLocal; + $this->assertSame([ + 'APP_ENV' => 'prod', + 'APP_SECRET' => 'abcdefgh123456789', + ], $vars); + + unlink($env); + unlink($envLocal); + } + + public function testEmptyOptionMustIgnoreContent() + { + @mkdir(FLEX_TEST_DIR); + $env = FLEX_TEST_DIR.'/.env'; + $envLocal = FLEX_TEST_DIR.'/.env.local.php'; + @unlink($env); + @unlink($envLocal); + + $envContent = <<createCommandDumpEnv(); + $command->execute([ + 'env' => 'prod', + '--empty' => true, + ]); + + $this->assertFileExists($envLocal); + + $vars = require $envLocal; + $this->assertSame([ + 'APP_ENV' => 'prod', + ], $vars); + + unlink($env); + unlink($envLocal); + } + + private function createCommandDumpEnv() + { + $command = new DumpEnvCommand( + new Config(false, __DIR__.'/../..'), + new Options(['root-dir' => FLEX_TEST_DIR]) + ); + + $application = new Application(); + $application->add($command); + $command = $application->find('dump-env'); + + return new CommandTester($command); + } +}