Skip to content

Commit

Permalink
Merge pull request #155 from boboldehampsink/environment_override_imp…
Browse files Browse the repository at this point in the history
…rovements

Schematic can now parse environment variables in the schema file dire…
  • Loading branch information
bvangennep authored Aug 31, 2018
2 parents c7213f8 + bf5b9fb commit 7de6928
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 25 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 4.0.15 - 2018-08-21
### Added
- Schematic can now parse environment variables in the schema file directly, without need for an override file
- Used environment variables don't have to be prefixed with SCHEMATIC_ anymore
- Environment variables without SCHEMATIC_ prefix are now case-sensitive

## 4.0.14 - 2018-07-26
### Fixed
- Fixed a bug where element indexes with custom elements failed to import
Expand Down
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ Multiple exclusions can also be specified:
./craft schematic/import --exclude=volumes,categoryGroups
```

Keys in the schema file can be overridden by passing an override file to schematic using the `--override-file` flag, for instance: `./craft schematic/import --override-file=craft/config/override.yml`.

See [Supported DataTypes](#Supported DataTypes)

### Supported DataTypes
Expand All @@ -116,20 +114,24 @@ Here is a list of all of the data types and their corresponding exclude paramete
| User Groups | userGroups |
| Volumes | volumes |

### Overrides
### Overrides and environment variables

Specific keys can be overriden by adding a key in `config/override.yml` and setting the corresponding environment variable. The key name in the `override.yml` needs to be the same as the key you want to override from `schema.yml`, including any parent key names.

Specific keys can be overriden by adding a key in `craft/config/override.yml` and setting the corresponding environment variable. The key name in the `override.yml` needs to be the same as the key you want to override from `schema.yml`, including any parent key names. The value has to start and end with a `%` (percentage sign). The correspending environment value will be `SCHEMATIC_{value_without_percentage_signs}`.
The override file is also applied back when exporting, so your variables are not overriden by actual values. Schematic also supports passing an override file using the `--override-file` flag, for instance: `./craft schematic/import --override-file=path/to/your/config/override.yml`.

#### Example

If the following `override.yml` is defined:

```yml
parent:
key_name: %key_value%
key_name: %KEY_VALUE%
```
Then the environment variable `SCHEMATIC_KEY_VALUE` needs to be set. The value of this environment variable will override the key `key_name`. If the environment variable is not set Schematic will throw an error.
Then the environment variable `KEY_VALUE` needs to be set. The value of this environment variable will override the key `key_name`. If the environment variable is not set Schematic will throw an error.

Environment variables can also directly be used in the `schema.yml` file. Beware that if you do that, they will be overriden on export by their environment variable's values.

### Hooks

Expand Down
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "4.0.14",
"version": "4.0.15",
"name": "nerds-and-company/schematic",
"description": "Craft setup and sync tool",
"type": "craft-plugin",
Expand All @@ -13,8 +13,8 @@
},
{
"name": "Bob Olde Hampsink",
"email": "[email protected]",
"homepage": "https://nerds.company",
"email": "[email protected]",
"homepage": "https://robuust.digital",
"role": "Developer"
}
],
Expand Down
18 changes: 13 additions & 5 deletions src/Models/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ class Data extends Model
*/
public static function fromYaml($yaml, $overrideYaml): array
{
$yaml = static::replaceEnvVariables($yaml);
$data = Yaml::parse($yaml);

if (!empty($overrideYaml)) {
$overrideYaml = static::replaceEnvVariables($overrideYaml);
$overrideData = Yaml::parse($overrideYaml);
if (null != $overrideData) {

if ($overrideData != null) {
$data = array_replace_recursive($data, $overrideData);
}
}
Expand Down Expand Up @@ -61,11 +64,15 @@ public static function replaceEnvVariables($yaml): string
$originalValues = $matches[0];
$replaceValues = [];
foreach ($originalValues as $match) {
$envVariable = strtoupper(substr($match, 1, -1));
$envVariable = 'SCHEMATIC_'.$envVariable;
$envVariable = substr($match, 1, -1);
$envValue = getenv($envVariable);
if (!$envValue) {
throw new Exception("Schematic environment variable not set: {$envVariable}");
$envVariable = strtoupper($envVariable);
$envVariable = 'SCHEMATIC_'.$envVariable;
$envValue = getenv($envVariable);
if (!$envValue) {
throw new Exception("Schematic environment variable not set: {$envVariable}");
}
}
$replaceValues[] = $envValue;
}
Expand All @@ -85,7 +92,8 @@ public static function toYaml(array $data, $overrideYaml = ''): string
{
if (!empty($overrideYaml)) {
$overrideData = Yaml::parse($overrideYaml);
if (null != $overrideData) {

if ($overrideData != null) {
$data = array_replace_recursive($data, $overrideData);
}
}
Expand Down
3 changes: 2 additions & 1 deletion tests/_data/test_override.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ volumes:
uploads:
attributes:
keyId: override_key
bucket: '%s3_bucket%'
bucket: '%S3_BUCKET%'
secret: '%s3_secret_access_key%'
3 changes: 2 additions & 1 deletion tests/_data/test_schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ volumes:
uploads:
attributes:
keyId: original_key_id
bucket: original_bucket_name
bucket: '%S3_BUCKET%'
secret: '%s3_secret_access_key%'
users:
settings:
requireEmailVerification: true
Expand Down
2 changes: 1 addition & 1 deletion tests/_support/Helper/Unit.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function _before(TestCase $test)
{
$mockApp = $this->getMockApp($test);
$mockApp->controller = $this->getMock($test, Controller::class);
$mockApp->controller->module = $this->getmockModule($test);
$mockApp->controller->module = $this->getMockModule($test);

Craft::$app = $mockApp;
Schematic::$force = false;
Expand Down
50 changes: 42 additions & 8 deletions tests/unit/Models/DataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,62 @@ private function getOverrideTestFile()

/**
* @return Data
*
* @param bool $useOverride Whether to use the override file or not
*/
private function generateDataModel()
private function generateDataModel($useOverride = false)
{
putenv('SCHEMATIC_S3_BUCKET=override_bucket_name');
putenv('SCHEMATIC_S3_SECRET_ACCESS_KEY=secret');

$schema = $this->getSchemaTestFile();
$override = $this->getOverrideTestFile();
$override = $useOverride ? $this->getOverrideTestFile() : [];

return Data::fromYaml($schema, $override);
}

public function testRegularOverride()
public function testEnvironment()
{
putenv('S3_BUCKET=bucket_name');
$result = $this->generateDataModel();
$this->assertEquals('bucket_name', $result['volumes']['uploads']['attributes']['bucket']);
}

public function testEnvironmentFallback()
{
putenv('S3_BUCKET'); // unset
putenv('SCHEMATIC_S3_BUCKET=bucket_name');
$result = $this->generateDataModel();
$this->assertEquals('bucket_name', $result['volumes']['uploads']['attributes']['bucket']);
$this->assertEquals('secret', $result['volumes']['uploads']['attributes']['secret']);
}

public function testRegularOverride()
{
putenv('S3_BUCKET=bucket_name');
$result = $this->generateDataModel(true);
$this->assertEquals('override_key', $result['volumes']['uploads']['attributes']['keyId']);
}

public function testEnvironmentOverride()
{
$result = $this->generateDataModel();
putenv('S3_BUCKET=override_bucket_name');
$result = $this->generateDataModel(true);
$this->assertEquals('override_bucket_name', $result['volumes']['uploads']['attributes']['bucket']);
}

public function testEnvironmentOverrideFallback()
{
putenv('S3_BUCKET'); // unset
putenv('SCHEMATIC_S3_BUCKET=override_bucket_name');
$result = $this->generateDataModel(true);
$this->assertEquals('override_bucket_name', $result['volumes']['uploads']['attributes']['bucket']);
$this->assertEquals('secret', $result['volumes']['uploads']['attributes']['secret']);
}

public function testErrorWhenEnvironmentVariableNotSet()
{
// unset environment variable
// unset environment variables
putenv('S3_BUCKET');
putenv('SCHEMATIC_S3_BUCKET');
$this->expectException('Exception');
$schema = $this->getSchemaTestFile();
Expand All @@ -68,23 +99,26 @@ public function testErrorWhenEnvironmentVariableNotSet()

public function testToYamlIsValidYaml()
{
putenv('S3_BUCKET=bucket_name');
$dataModel = $this->generateDataModel();
$yaml = Data::toYaml($dataModel);
$this->assertInternalType('array', Yaml::parse($yaml));
}

public function testToYamlContainsCorrectText()
{
putenv('S3_BUCKET=bucket_name');
$dataModel = $this->generateDataModel();
$yaml = Data::toYaml($dataModel);
$this->assertContains('override_bucket_name', $yaml);
$this->assertContains('bucket_name', $yaml);
}

public function testToYamlOverride()
{
putenv('S3_BUCKET=bucket_name');
$dataModel = $this->generateDataModel();
$override = $this->getOverrideTestFile();
$yaml = Data::toYaml($dataModel, $override);
$this->assertContains("'%s3_bucket%'", $yaml);
$this->assertContains("'%S3_BUCKET%'", $yaml);
}
}

0 comments on commit 7de6928

Please sign in to comment.