Skip to content

Commit

Permalink
Add environment spark command
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbalandan committed May 25, 2021
1 parent 6bacf2c commit 76fe1c7
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 0 deletions.
160 changes: 160 additions & 0 deletions system/Commands/Utilities/Environment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<?php

namespace CodeIgniter\Commands\Utilities;

use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use CodeIgniter\Config\DotEnv;

/**
* Command to display the current environment,
* or set a new one in the `.env` file.
*/
final class Environment extends BaseCommand
{
/**
* The group the command is lumped under
* when listing commands.
*
* @var string
*/
protected $group = 'CodeIgniter';

/**
* The Command's name
*
* @var string
*/
protected $name = 'env';

/**
* The Command's short description
*
* @var string
*/
protected $description = 'Retrieves the current environment, or set a new one.';

/**
* The Command's usage
*
* @var string
*/
protected $usage = 'env [<environment>]';

/**
* The Command's arguments
*
* @var array<string, string>
*/
protected $arguments = [
'environment' => '[Optional] The new environment to set. If none is provided, this will print the current environment.',
];

/**
* The Command's options
*
* @var array
*/
protected $options = [];

/**
* Allowed values for environment. `testing` is excluded
* since spark won't work on it.
*
* @var array<int, string>
*/
private static $knownTypes = [
'production',
'development',
];

/**
* @inheritDoc
*
* @param array<string, mixed> $params
*
* @return void
*/
public function run(array $params)
{
if ($params === [])
{
CLI::write(sprintf('Your environment is currently set as %s.', CLI::color(ENVIRONMENT, 'green')));
CLI::newLine();

return;
}

$env = strtolower(array_shift($params));

if ($env === 'testing')
{
CLI::error('The "testing" environment is reserved for PHPUnit testing.', 'light_gray', 'red');
CLI::error('You will not be able to run spark under a "testing" environment.', 'light_gray', 'red');
CLI::newLine();

return;
}

if (! in_array($env, self::$knownTypes, true))
{
CLI::error(sprintf('Invalid environment type "%s". Expected one of "%s".', $env, implode('" and "', self::$knownTypes)), 'light_gray', 'red');
CLI::newLine();

return;
}

if (! $this->writeNewEnvironmentToEnvFile($env))
{
CLI::error('Error in writing new environment to .env file.', 'light_gray', 'red');
CLI::newLine();

return;
}

// force DotEnv to reload the new environment
// however we cannot redefine the ENVIRONMENT constant
putenv('CI_ENVIRONMENT');
unset($_ENV['CI_ENVIRONMENT'], $_SERVER['CI_ENVIRONMENT']);
(new DotEnv(ROOTPATH))->load();

CLI::write(sprintf('Environment is successfully changed to "%s".', $env), 'green');
CLI::write('The ENVIRONMENT constant will be changed on the next script execution.');
CLI::newLine();
}

/**
* @see https://regex101.com/r/4sSORp/1 for the regex in action
*
* @param string $newEnv
*
* @return boolean
*/
private function writeNewEnvironmentToEnvFile(string $newEnv): bool
{
$baseEnv = ROOTPATH . 'env';
$envFile = ROOTPATH . '.env';

if (! is_file($envFile))
{
if (! is_file($baseEnv))
{
CLI::write('Both default shipped `env` file and custom `.env` are missing.', 'yellow');
CLI::write('It is impossible to write the new environment type.', 'yellow');
CLI::newLine();

return false;
}

copy($baseEnv, $envFile);
}

$pattern = preg_quote($_SERVER['CI_ENVIRONMENT'] ?? ENVIRONMENT, '/');
$pattern = sprintf('/^[#\s]*CI_ENVIRONMENT[=\s]+%s$/m', $pattern);

return file_put_contents(
$envFile,
preg_replace($pattern, "\nCI_ENVIRONMENT = {$newEnv}", file_get_contents($envFile), -1, $count)
) !== false && $count > 0;
}
}
86 changes: 86 additions & 0 deletions tests/system/Commands/EnvironmentCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

namespace CodeIgniter\Commands;

use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Filters\CITestStreamFilter;

final class EnvironmentCommandTest extends CIUnitTestCase
{
private $streamFilter;

private $envPath = ROOTPATH . '.env';

private $backupEnvPath = ROOTPATH . '.env.backup';

protected function setUp(): void
{
parent::setUp();
CITestStreamFilter::$buffer = '';

$this->streamFilter = stream_filter_append(STDOUT, 'CITestStreamFilter');
$this->streamFilter = stream_filter_append(STDERR, 'CITestStreamFilter');

if (is_file($this->envPath))
{
rename($this->envPath, $this->backupEnvPath);
}
}

protected function tearDown(): void
{
parent::tearDown();
stream_filter_remove($this->streamFilter);

if (is_file($this->envPath))
{
unlink($this->envPath);
}

if (is_file($this->backupEnvPath))
{
rename($this->backupEnvPath, $this->envPath);
}

$_SERVER['CI_ENVIRONMENT'] = $_ENV['CI_ENVIRONMENT'] = ENVIRONMENT;
}

public function testUsingCommandWithNoArgumentsGivesCurrentEnvironment(): void
{
command('env');
$this->assertStringContainsString('testing', CITestStreamFilter::$buffer);
$this->assertStringContainsString(ENVIRONMENT, CITestStreamFilter::$buffer);
}

public function testProvidingTestingAsEnvGivesErrorMessage(): void
{
command('env testing');
$this->assertStringContainsString('The "testing" environment is reserved for PHPUnit testing.', CITestStreamFilter::$buffer);
}

public function testProvidingUnknownEnvGivesErrorMessage(): void
{
command('env foobar');
$this->assertStringContainsString('Invalid environment type "foobar".', CITestStreamFilter::$buffer);
}

public function testDefaultShippedEnvIsMissing()
{
rename(ROOTPATH . 'env', ROOTPATH . 'lostenv');
command('env development');
rename(ROOTPATH . 'lostenv', ROOTPATH . 'env');

$this->assertStringContainsString('Both default shipped', CITestStreamFilter::$buffer);
$this->assertStringContainsString('It is impossible to write the new environment type.', CITestStreamFilter::$buffer);
}

public function testSettingNewEnvIsSuccess(): void
{
// default env file has `production` env in it
$_SERVER['CI_ENVIRONMENT'] = 'production';
command('env development');

$this->assertStringContainsString('Environment is successfully changed to', CITestStreamFilter::$buffer);
$this->assertStringContainsString('CI_ENVIRONMENT = development', file_get_contents($this->envPath));
}
}

0 comments on commit 76fe1c7

Please sign in to comment.