Skip to content

Commit

Permalink
Merge pull request #48 from peterjaap/add-env-var-support
Browse files Browse the repository at this point in the history
Added support and readme for environment variables
  • Loading branch information
DavidLambauer authored Sep 29, 2022
2 parents 887dee3 + 520ed9a commit 3c9bd4c
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 1 deletion.
24 changes: 23 additions & 1 deletion Model/Processor/ImportProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Semaio\ConfigImportExport\Model\Converter\ScopeConverterInterface;
use Semaio\ConfigImportExport\Model\File\FinderInterface;
use Semaio\ConfigImportExport\Model\File\Reader\ReaderInterface;
use Semaio\ConfigImportExport\Model\Resolver\EnvironmentVariableResolver;
use Semaio\ConfigImportExport\Model\Validator\ScopeValidatorInterface;

class ImportProcessor extends AbstractProcessor implements ImportProcessorInterface
Expand Down Expand Up @@ -39,19 +40,27 @@ class ImportProcessor extends AbstractProcessor implements ImportProcessorInterf
*/
private $scopeConverter;

/**
* @var EnvironmentVariableResolver
*/
private $environmentVariableResolver;

/**
* @param WriterInterface $configWriter
* @param ScopeValidatorInterface $scopeValidator
* @param ScopeConverterInterface $scopeConverter
* @param EnvironmentVariableResolver $environmentVariableResolver
*/
public function __construct(
WriterInterface $configWriter,
ScopeValidatorInterface $scopeValidator,
ScopeConverterInterface $scopeConverter
ScopeConverterInterface $scopeConverter,
EnvironmentVariableResolver $environmentVariableResolver
) {
$this->configWriter = $configWriter;
$this->scopeValidator = $scopeValidator;
$this->scopeConverter = $scopeConverter;
$this->environmentVariableResolver = $environmentVariableResolver;
}

/**
Expand Down Expand Up @@ -145,6 +154,19 @@ public function transformConfigToScopeConfig($path, array $config)
continue;
}

try {
$value = $this->environmentVariableResolver->resolveValue($value);
} catch (\UnexpectedValueException $e) {
$errorMsg = sprintf(
'<error>%s (%s => %s)</error>',
$e->getMessage(),
$path,
$value
);
$this->getOutput()->writeln($errorMsg);
continue;
}

$return[] = [
'value' => $value,
'scope' => $scope,
Expand Down
42 changes: 42 additions & 0 deletions Model/Resolver/EnvironmentVariableResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

/**
* Copyright © semaio GmbH. All rights reserved.
* See LICENSE.md bundled with this module for license details.
*/

namespace Semaio\ConfigImportExport\Model\Resolver;

class EnvironmentVariableResolver
{
/**
* @var string
*/
private $value;

/**
* Resolve the config value if it's an environment
* variable reference.
*
* @throws \UnexpectedValueException
*
* @param string $value
* @return string|false the original string, or the resolved
* string if it's a reference or false on error
*/
public function resolveValue($value)
{
$this->value = $value;
return preg_replace_callback(
'/\%env\(([^:\%\%]+?)\)\%/',
function ($matches) {
$resolvedValue = getenv($matches[1]);
if ($resolvedValue === false) {
throw new \UnexpectedValueException(sprintf('Environment variable %s does not exist', $matches[1]));
}
return $resolvedValue;
},
$value
);
}
}
52 changes: 52 additions & 0 deletions Test/Unit/Model/Resolver/EnvironmentVariableResolverTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php
/**
* Copyright © semaio GmbH. All rights reserved.
* See LICENSE.md bundled with this module for license details.
*/

namespace Semaio\ConfigImportExport\Test\Unit\Model\Validator;

use Magento\Store\Api\Data\WebsiteInterface;
use Magento\Store\Model\StoreManagerInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Semaio\ConfigImportExport\Model\Resolver\EnvironmentVariableResolver;

class EnvironmentVariableResolverTest extends TestCase
{
/**
* @var EnvironmentVariableResolver
*/
private $environmentVariableResolver;

/**
* Set up test class
*/
protected function setUp(): void
{
parent::setUp();

putenv('HOSTNAME=testvalue1');
putenv('SUBDOMAIN=testvalue2');
putenv('CONCAT_THIS=testvalue3');
putenv('WITH_THIS=testvalue4');

$this->environmentVariableResolver = new EnvironmentVariableResolver();
}

/**
* @test
*/
public function validate(): void
{
$this->assertEquals($this->environmentVariableResolver->resolveValue('test_without_env_var'), 'test_without_env_var');

$this->assertEquals($this->environmentVariableResolver->resolveValue('%env(HOSTNAME)%'), 'testvalue1');
$this->assertEquals($this->environmentVariableResolver->resolveValue('https://%env(SUBDOMAIN)%.example.com'), 'https://testvalue2.example.com');
$this->assertEquals($this->environmentVariableResolver->resolveValue('%env(CONCAT_THIS)%%env(WITH_THIS)%'), 'testvalue3testvalue4');

$this->expectException(\UnexpectedValueException::class);
$this->environmentVariableResolver->resolveValue('%env(DOESNOTEXIST)%');
}

}
14 changes: 14 additions & 0 deletions docs/config-import.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ php bin/magento config:data:import config/store dev/therouv
The files in the `base` folder will always be imported (if they exist), regardless of which environment parameter has been passed. If the base and environment configurations have the same configuration field set, then the environment value for that configuration will overwrite the base configuration.
### Environment Variables substitution
If you do not want to store your secrets in version control, you can use placeholders for environment variables in the configuration files. This is done with the notation `%env(ENV_VAR_NAME)%`.
For example, this might be the content of your config file:
```
vendorx/general/api_key:
default:
0: %env(VENDORX_API_KEY)%
```
You can then set the environment variable `VENDORX_API_KEY` in your CI/CD configuration to the secret API key.
### Recursive folder setup
If you choose to store your configuration files in subdirectories, e.g. per vendor, the recommended folder setup should look like this:
Expand Down

0 comments on commit 3c9bd4c

Please sign in to comment.