Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Application/Command/AddCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
/**
* @author Serhii Andriichuk <andriichuk29@gmail.com>
*/
class AddCommand extends Command
final class AddCommand extends Command
{
protected static $defaultName = 'add';

Expand Down
11 changes: 7 additions & 4 deletions src/Environment/Reader/EnvReaderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ class EnvReaderFactory
public function make(string $type): EnvReaderInterface
{
switch ($type) {
case 'auto':
case EnvReaderType::AUTO:
return $this->baseOnAvailability();

case 'vlucas/phpdotenv':
case EnvReaderType::SYSTEM:
return new SystemEnvReader();

case EnvReaderType::VLUCAS:
return new VlucasPhpDotEnvFileReader();

case 'symfony/dotenv':
case EnvReaderType::SYMFONY:
return new SymfonyDotEnvFileReader();

case 'josegonzalez/dotenv':
case EnvReaderType::JOSEGONZALEZ:
return new JoseGonzalezDotEnvFileReader();

default:
Expand Down
14 changes: 14 additions & 0 deletions src/Environment/Reader/EnvReaderType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Andriichuk\KeepEnv\Environment\Reader;

final class EnvReaderType
{
public const AUTO = 'auto';
public const SYSTEM = 'system';
public const VLUCAS = 'vlucas/phpdotenv';
public const SYMFONY = 'symfony/dotenv';
public const JOSEGONZALEZ = 'josegonzalez/dotenv';
}
16 changes: 16 additions & 0 deletions src/Environment/Reader/SystemEnvReader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Andriichuk\KeepEnv\Environment\Reader;

/**
* @author Serhii Andriichuk <andriichuk29@gmail.com>
*/
class SystemEnvReader implements EnvReaderInterface
{
public function read(string ...$paths): array
{
return $_ENV;
}
}
10 changes: 3 additions & 7 deletions src/Generator/SpecGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,9 @@ public function generate(
continue;
}

$required = trim((string) $value) !== '';
$rules = [];

if ($required) {
$rules['required'] = true;
}

$rules = [
'required' => trim((string) $value) !== ''
];
$rules = array_merge($rules, $this->guessType($key, $value));

$envSpec->add(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,14 @@ public function testReaderCanProvideVariablesFromFile(): void
'APP_NAME' => 'KeepEnv',
'APP_ENV' => 'production',
'APP_DEBUG' => true,
'PAYMENT_FEATURE_ENABLED' => 'false',
'APP_KEY' => null,
'REDIS_PORT' => 6379,
'REDIS_PASSWORD' => null,
'PROFILING_LIMIT' => 2.5,
'MAIL_FROM_ADDRESS' => 'test@test.com',
'DEBUG_IP' => '127.0.0.1',
'MAINTENANCE' => 'on',
],
$variables,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ public function testReaderCanProvideVariablesFromFile(): void
'APP_KEY' => '',
'REDIS_PORT' => '6379',
'MAIL_FROM_ADDRESS' => 'test@test.com',
'PAYMENT_FEATURE_ENABLED' => 'false',
'REDIS_PASSWORD' => 'null',
'PROFILING_LIMIT' => '2.50',
'DEBUG_IP' => '127.0.0.1',
'MAINTENANCE' => 'on',
],
$variables,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ protected function setUp(): void

$this->reader = new VlucasPhpDotEnvFileReader();

// Check that environment variables cannot affect the file reading
$_ENV['APP_ENV'] = 'dev';
$_ENV['APP_RANDOM_KEY'] = 'test_123';
}
Expand All @@ -53,6 +54,11 @@ public function testReaderCanProvideVariablesFromFile(): void
'APP_KEY' => '',
'REDIS_PORT' => '6379',
'MAIL_FROM_ADDRESS' => 'test@test.com',
'PAYMENT_FEATURE_ENABLED' => 'false',
'REDIS_PASSWORD' => 'null',
'PROFILING_LIMIT' => '2.50',
'DEBUG_IP' => '127.0.0.1',
'MAINTENANCE' => 'on',
],
$variables,
);
Expand Down
73 changes: 54 additions & 19 deletions tests/Functional/Generator/SpecGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Andriichuk\KeepEnv\Tests\Functional\Generator;

use Andriichuk\KeepEnv\Environment\Reader\EnvReaderFactory;
use Andriichuk\KeepEnv\Environment\Reader\EnvReaderType;
use Andriichuk\KeepEnv\Generator\Exceptions\SpecGeneratorException;
use Andriichuk\KeepEnv\Generator\Presets\PresetFactory;
use Andriichuk\KeepEnv\Generator\SpecGenerator;
Expand All @@ -26,7 +27,12 @@ protected function setUp(): void
$this->rootFolder = vfsStream::setup('src');
}

public function testEnvironmentSpecificationGeneration(): void
protected function tearDown(): void
{
$_ENV = [];
}

public function testGeneratorCanAutomaticallyDetectReaderBasedOnAvailablePackage(): void
{
$this->rootFolder->addChild(
(new vfsStreamFile('.env'))
Expand All @@ -38,7 +44,7 @@ public function testEnvironmentSpecificationGeneration(): void
);

$specGenerator = new SpecGenerator(
(new EnvReaderFactory())->make('auto'),
(new EnvReaderFactory())->make(EnvReaderType::AUTO),
(new SpecWriterFactory())->basedOnResource('vfs://src/keepenv.yaml'),
new PresetFactory(),
);
Expand All @@ -51,70 +57,99 @@ public function testEnvironmentSpecificationGeneration(): void
);

$this->assertFileEquals(
dirname(__DIR__, 2) . '/fixtures/case_1/keepenv.yaml',
dirname(__DIR__, 2) . '/fixtures/common/keepenv.yaml',
$this->rootFolder->getChild('keepenv.yaml')->url(),
);
}

public function testEnvironmentSpecificationWithLaravelPresetGeneration(): void
public function testGeneratorCanCreateSpecUsingLaravelPreset(): void
{
$this->rootFolder->addChild(
(new vfsStreamFile('.env'))
->setContent(
file_get_contents(
dirname(__DIR__, 2) . '/fixtures/case_2/.env',
dirname(__DIR__, 2) . '/fixtures/generator/laravel_preset/.env',
),
),
);

$specGenerator = new SpecGenerator(
(new EnvReaderFactory())->make('auto'),
(new EnvReaderFactory())->make(EnvReaderType::AUTO),
(new SpecWriterFactory())->basedOnResource('vfs://src/keepenv_laravel.yaml'),
new PresetFactory(),
);

$specGenerator->generate(
'common',
[dirname($this->rootFolder->getChild('.env')->url())],
'vfs://src/keepenv_laravel.yaml',
'vfs://src/keepenv.yaml',
static fn () => true,
'laravel'
);

$this->assertFileEquals(
dirname(__DIR__, 2) . '/fixtures/case_2/keepenv_laravel.yaml',
$this->rootFolder->getChild('keepenv_laravel.yaml')->url(),
dirname(__DIR__, 2) . '/fixtures/generator/laravel_preset/keepenv.yaml',
$this->rootFolder->getChild('keepenv.yaml')->url(),
);
}

public function testEnvironmentSpecificationWithSymfonyPresetGeneration(): void
public function testGeneratorCanCreateSpecUsingSymfonyPreset(): void
{
$this->rootFolder->addChild(
(new vfsStreamFile('.env.symfony'))
(new vfsStreamFile('.env'))
->setContent(
file_get_contents(
dirname(__DIR__, 2) . '/fixtures/case_2/.env.symfony',
dirname(__DIR__, 2) . '/fixtures/generator/symfony_preset/.env',
),
),
);

$specGenerator = new SpecGenerator(
(new EnvReaderFactory())->make('symfony/dotenv'),
(new SpecWriterFactory())->basedOnResource('vfs://src/keepenv_symfony.yaml'),
(new EnvReaderFactory())->make(EnvReaderType::SYMFONY),
(new SpecWriterFactory())->basedOnResource('vfs://src/keepenv.yaml'),
new PresetFactory(),
);

$specGenerator->generate(
'common',
[$this->rootFolder->getChild('.env.symfony')->url()],
'vfs://src/keepenv_symfony.yaml',
[$this->rootFolder->getChild('.env')->url()],
'vfs://src/keepenv.yaml',
static fn () => true,
'symfony'
);

$this->assertFileEquals(
dirname(__DIR__, 2) . '/fixtures/case_2/keepenv_symfony.yaml',
$this->rootFolder->getChild('keepenv_symfony.yaml')->url(),
dirname(__DIR__, 2) . '/fixtures/generator/symfony_preset/keepenv.yaml',
$this->rootFolder->getChild('keepenv.yaml')->url(),
);
}

public function testGeneratorCanCreateSpecUsingSystemVariables(): void
{
$_ENV['APP_NAME'] = 'KeepEnv';
$_ENV['APP_ENV'] = 'production';
$_ENV['APP_DEBUG'] = true;
$_ENV['APP_KEY'] = '';
$_ENV['REDIS_PORT'] = 6379;
$_ENV['REDIS_PASSWORD'] = null;
$_ENV['MAIL_FROM_ADDRESS'] = 'test@test.com';

$specGenerator = new SpecGenerator(
(new EnvReaderFactory())->make(EnvReaderType::SYSTEM),
(new SpecWriterFactory())->basedOnResource('vfs://src/keepenv.yaml'),
new PresetFactory(),
);

$specGenerator->generate(
'common',
[],
'vfs://src/keepenv.yaml',
static fn (): bool => true,
);

$this->assertFileEquals(
dirname(__DIR__, 2) . '/fixtures/common/keepenv.yaml',
$this->rootFolder->getChild('keepenv.yaml')->url(),
);
}

Expand All @@ -126,7 +161,7 @@ public function testGeneratorThrowsExceptionOnAlreadyExistingSpecFile(): void
$this->rootFolder->addChild((new vfsStreamFile('keepenv.yaml'))->setContent(''));

$specGenerator = new SpecGenerator(
(new EnvReaderFactory())->make('auto'),
(new EnvReaderFactory())->make(EnvReaderType::AUTO),
(new SpecWriterFactory())->basedOnResource('vfs://src/keepenv.yaml'),
new PresetFactory(),
);
Expand Down
25 changes: 25 additions & 0 deletions tests/fixtures/common/.env
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
# String value
APP_NAME="KeepEnv"

# String value without quotes
APP_ENV=production

# Boolean
APP_DEBUG=true

# Boolean with quotes
PAYMENT_FEATURE_ENABLED="false"

# Empty value
APP_KEY=

# Numberic integer
REDIS_PORT=6379

# Nullable
REDIS_PASSWORD=null

# Numberic decimal
PROFILING_LIMIT=2.50

# Email address
MAIL_FROM_ADDRESS=test@test.com

# IP address
DEBUG_IP=127.0.0.1

# Boolean with quotes
export MAINTENANCE="on"
26 changes: 26 additions & 0 deletions tests/fixtures/common/keepenv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,40 @@ environments:
rules:
required: true
boolean: true
PAYMENT_FEATURE_ENABLED:
description: 'Payment feature enabled.'
rules:
required: true
boolean: true
APP_KEY:
description: 'Application key.'
rules:
required: false
REDIS_PORT:
description: 'Redis port.'
rules:
required: true
numeric: true
REDIS_PASSWORD:
description: 'Redis password.'
rules:
required: false
PROFILING_LIMIT:
description: 'Profiling limit.'
rules:
required: true
numeric: true
MAIL_FROM_ADDRESS:
description: 'Mail from address.'
rules:
required: true
DEBUG_IP:
description: 'Debug ip.'
rules:
required: true
ip: true
MAINTENANCE:
description: Maintenance.
rules:
required: true
boolean: true