Skip to content

Commit

Permalink
Merge 4ee2049 into f699d03
Browse files Browse the repository at this point in the history
  • Loading branch information
popovicsandras committed Nov 17, 2022
2 parents f699d03 + 4ee2049 commit 16df4f8
Show file tree
Hide file tree
Showing 31 changed files with 850 additions and 58 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/.idea/
.history
/coverage/
node_modules/
*.log
Expand Down
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
language: node_js
node_js:
- node
- 16
- 14
- 12
- 10
- 8
Expand Down
1 change: 1 addition & 0 deletions Dockerfiles/build/files/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ <h3>
</h3>
<h3>
Yup, this container runs on Node.js version ${ NODE_VERSION }
And this is a splendid variable with default value "${ NODE_VERSION:-duck }"
</h3>
</body>
</html>
1 change: 1 addition & 0 deletions Dockerfiles/run/files/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ <h3>
</h3>
<h3>
Yup, this container runs on Node.js version ${ NODE_VERSION }
And this is a splendid variable with default value "${ NODE_VERSION:-duck }"
</h3>
</body>
</html>
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,45 @@ export MYVAR4=hello # same as --env MYVAR4=hello
>
> However, because the --system flag is set, the system environment variable value 'bar' is preferred.
<br><br>

#### **envsub variable placeholder default values**

**For the `dollar-curly` syntax only**, defining default values is possible.
The syntax is the following:
```yaml
# Before substitution
Variable with default value: ${VARIABLE_NAME:-default}.
# After substitution
Variable with default value: default.

# Before substitution
Variable with default value + whitespaces: ${VARIABLE_NAME:- default value }.
# After substitution
Variable with default value + whitespaces: default value .
```

##### Without modifier flags

**By default**, every `variable placeholder with default value` is substituted even if not set by the environment or from the cli.

![envsub templateFile outputFile](./images/envsub-default-normal.png "envsub templateFile outputFile")

---
##### With --env or --env-file
When using the **--env** and **--env-file** flags, only the restricted `variable placeholders with default value` are substituted even if not set by the environment or from the cli.

![envsub --env MYVAR1=llama --all templateFile outputFile](./images/envsub-default-env.png "envsub --env MYVAR1=llama --all templateFile outputFile")

---

##### With --env or --env-file and --all

- When using the **--env** and **--env-file** flags with the **--all** flag, every `variable placeholder with default value` is substituted even if not set by the environment or from the cli.

![envsub --env MYVAR1=llama --env MYVAR3 templateFile outputFile](./images/envsub-default-env-all.png "envsub --env MYVAR1=llama --env MYVAR3 templateFile outputFile")


<br><br>

#### **envsub overwrite** .. ```envsub templateFile```
Expand Down Expand Up @@ -216,7 +255,7 @@ envsub --help
-h, --help output usage information
-V, --version output the version number
-a, --all substitute all system environment variables - avoids substitution restrictions when using the --env or --env-file flags
-a, --all substitute all system environment variables and all variable placeholders with default value (only for dollar-curly syntax, like ${VAR_NAME:-default value}) - avoids substitution restrictions when using the --env or --env-file flags
-d, --diff show diff between template file and output file
-e, --env <name>[=value] environment variable to substitute .. if none specified then substitute all (but see --env-file) .. this flag can be repeated
-f, --env-file <envFile> load environment variables from an .env file .. this flag can be repeated
Expand Down
2 changes: 1 addition & 1 deletion bin/envsub.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ let addEnvironmentVariableFile = (envVarFile, envVarFileList) => {
program
.version(version)
.usage('[options] <templateFile> [outputFile]')
.option('-a, --all', 'substitute all system environment variables - avoids substitution restrictions when using the --env or --env-file flags')
.option('-a, --all', 'substitute all system environment variables and all variable placeholders with default value (only for dollar-curly syntax, like ${VAR_NAME:-default value}) - avoids substitution restrictions when using the --env or --env-file flags')
.option('-d, --diff', 'show diff between template file and output file')
.option('-e, --env <name>[=value]', 'environment variable to substitute .. if none specified then substitute all (but see --env-file) .. this flag can be repeated', addEnvironmentVariable, [])
.option('-f, --env-file <envFile>', 'load environment variables from an .env file .. this flag can be repeated', addEnvironmentVariableFile, [])
Expand Down
Binary file added images/envsub-default-env-all.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/envsub-default-env.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/envsub-default-normal.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 55 additions & 22 deletions js/envsub-parser.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,55 @@
const config = require('../main.config');
const matchAll = require('string.prototype.matchall');
const DotEnvParser = require('./DotEnvParser');
const DEFAULT_SYNTAX = 'dollar-curly';

matchAll.shim(); // will be a no-op if not needed

const SYNTAX = {
DOLLAR_BASIC: 'dollar-basic',
DOLLAR_CURLY: 'dollar-curly',
DOLLAR_BOTH: 'dollar-both',
HANDLEBARS: 'handlebars'
};

let dynamicRegexes = (opts) => {

let regexObj = (lhs, cleanLhs, rhs, sep) => {
return {lhs, cleanLhs, rhs, sep};
let regexObj = (lhs, cleanLhs, rhs, sep, type) => {
return {lhs, cleanLhs, rhs, sep, type};
};

let dynamicRegexes = [];

if (opts.syntax === 'default') {
opts.syntax = DEFAULT_SYNTAX;
opts.syntax = SYNTAX.DOLLAR_CURLY;
}

if (opts.syntax === 'dollar-basic' || opts.syntax === 'dollar-both') {
dynamicRegexes.push(regexObj('\\$', '$', '', ''));
if (opts.syntax === SYNTAX.DOLLAR_BASIC || opts.syntax === SYNTAX.DOLLAR_BOTH) {
dynamicRegexes.push(regexObj('\\$', '$', '', '', opts.syntax));
}

if (opts.syntax === 'dollar-curly' || opts.syntax === 'dollar-both') {
dynamicRegexes.push(regexObj('\\${', '${', '}', ' *'));
if (opts.syntax === SYNTAX.DOLLAR_CURLY || opts.syntax === SYNTAX.DOLLAR_BOTH) {
dynamicRegexes.push(regexObj('\\${', '${', '}', ' *', opts.syntax));
}

if (opts.syntax === 'handlebars') {
dynamicRegexes.push(regexObj('{{', '{{', '}}', ' *'));
if (opts.syntax === SYNTAX.HANDLEBARS) {
dynamicRegexes.push(regexObj('{{', '{{', '}}', ' *', opts.syntax));
}

return dynamicRegexes;
};

let substitute = (matches, contents, opts, dRegex) => {
let substitute = (matches, contents, opts) => {

matches && matches.forEach((match) => {
let envVarName = match.replace(dRegex.cleanLhs, '').replace(dRegex.rhs, '').trim();
matches && matches.forEach(([match, envVarName, defaultValue]) => {
let envVarValue = process.env[envVarName];
if (envVarValue || !opts.protect) {
contents = contents.replace(match, envVarValue || '');
if (envVarValue !== undefined) {
contents = contents.replace(match, envVarValue);
} else if (defaultValue !== undefined) {
contents = contents.replace(match, defaultValue);
} else {
contents = contents.replace(match, '');
}
}
});
return contents;
Expand All @@ -53,30 +67,49 @@ let envsubParser = (contents, args) => {

if (!opts.envs.length) {

// Find all env var matches
let regexp = `${dRegex.lhs}${dRegex.sep}${config.regex}${dRegex.sep}${dRegex.rhs}`;
let matches = contents.match(new RegExp(regexp, 'g'));
// Find all env var matches
const regexp = dRegex.type === SYNTAX.DOLLAR_CURLY ?
// default value support is only available with dollar-curly syntax
[ dRegex.lhs, dRegex.sep, config.curlyRegex(false), dRegex.sep, dRegex.rhs ].join('') :
// Fallback to everything else
[ dRegex.lhs, dRegex.sep, `(${config.regex})`, dRegex.sep, dRegex.rhs].join('');

let matches = contents.matchAll(new RegExp(regexp, 'g'));
matches = [...matches].map(([match, envVarName, defaultValue]) => [match, envVarName, defaultValue]);

// Substitute
contents = substitute(matches, contents, opts, dRegex);

} else {

// Iterate over selected env variables
opts.envs.forEach((env) => {

if (opts.system && process.env[env.name] == null || !opts.system) {
if (env.value != null) {
process.env[env.name] = env.value;
}
}

let regexp = `${dRegex.lhs}${dRegex.sep}${env.name}${dRegex.sep}${dRegex.rhs}`;
let matches = contents.match(new RegExp(regexp, 'g'));

let regexp = dRegex.type === SYNTAX.HANDLEBARS ?
[ dRegex.lhs, dRegex.sep, `(${env.name})`, dRegex.sep, dRegex.rhs ].join('') :
[ dRegex.lhs, dRegex.sep, config.curlyRegex(false, env.name), dRegex.sep, dRegex.rhs ].join('');
let matches = contents.matchAll(new RegExp(regexp, 'g'));
matches = [...matches].map(([match, envVarName, defaultValue]) => [match, envVarName, defaultValue]);

// Substitute
contents = substitute(matches, contents, opts, dRegex);

});

// In case of all flag we want to replace the ones with default value set
if (opts.all && dRegex.type === SYNTAX.DOLLAR_CURLY) {
let regexp = [ dRegex.lhs, dRegex.sep, config.curlyRegex(true), dRegex.sep, dRegex.rhs ].join('');
let matches = contents.matchAll(new RegExp(regexp, 'g'));
matches = [...matches].map(([match, envVarName, defaultValue]) => [match, envVarName, defaultValue]);

// Substitute
contents = substitute(matches, contents, opts, dRegex);
}
}
});

Expand Down
10 changes: 9 additions & 1 deletion main.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const VARIABLE_NAME_REGEX = '[a-zA-Z_]+[a-zA-Z0-9_]*';
const config = {
envsub: {
DEFAULT_OPTIONS: {
Expand All @@ -12,7 +13,14 @@ const config = {
diff: false
}
},
regex: '[a-zA-Z_]+[a-zA-Z0-9_]*'
regex: VARIABLE_NAME_REGEX,
curlyRegex: (mustHaveDefaultValue, variableNameRegex = VARIABLE_NAME_REGEX) => {
const separatorRegex = ':-';
const variableValueRegex = '[^}]*';
const optionalDefaultValueModifier = mustHaveDefaultValue ? '' : '?';

return `(${variableNameRegex})(?:${separatorRegex}(?<=${separatorRegex})(${variableValueRegex}))${optionalDefaultValueModifier}`;
}
};

module.exports = config;

0 comments on commit 16df4f8

Please sign in to comment.