A DevOps engineer needed to pass environment variables to the container with the frontend to apply without rebuilding the container again. Angular does not provide any out-of-the-box solution for this purpose.
Our first approach was simple: create a JSON template app.config.template.json
in the project assets folder, which looked like this:
{
"var1": "$VAR",
"varObj": {
"varA": "$VARA",
"varBool": "$VAR_B",
}
}
Then generate a JSON file from this template by replacing the environment variables with the envsubst
command-line utility. This utility is included in the nginx:alpine
image.
"envsubst < ./assets/app.config.template.json > ./assets/app.config.json
CMD ["/bin/sh", "-c", "envsubst < /usr/share/nginx/html/assets/app.config.template.json > /usr/share/nginx/html/assets/app.config.json && exec nginx -g 'daemon off;'"]
There were two problems with this approach:
- Boolean values needed to be converted from strings to values every time.
- It was necessary to check for empty strings if an environment variable was not specified.
All this had to be done manually for each variable in the app.config.json
loading service. This is why it was not desirable to leave the first approach for a long time and it was decided to solve these problems in it. Plus, it was necessary to support the naming of nested variables, just like .NetCore does.
Therefore, a Node.js script was written that would create a JSON file from the JSON template, skipping empty fields, and converting numbers or boolean values where necessary. The node-config package in Node.js supports a similar format for environment variables, where you can specify not only the variable name but also the variable format. We take this idea, too.
{
"var1": "VAR",
"varObj": {
"varA": "VARA",
"varBool": {
"__name": "VAR_B",
"__format": "boolean",
}
}
}
The script was written. We support this format of config file.
{
"var1": "VAR",
"varObj": {
"varA": "",
"varBool": {
"__name": "VAR_B",
"__format": "boolean",
}
},
}
Based on this configuration, the script will try to find variables with the names VAR
, varObj__varA
, varObj__VAR_B
in the environment. And for varObj__VAR_B
, it will convert the string to a boolean value. If these values are not found, it will skip them.
For example, if the environment variables are set like this:
varObj__varA=VAR-A
varObj__VAR_B=true
then the resulting JSON will be like this:
{
"varObj": {
"varA": "VAR-A",
"varBool": true
},
}
CMD ["/bin/sh", "-c", "env-config /usr/share/nginx/html /usr/share/nginx/html/assets && exec nginx -g 'daemon off;'"]
This allows us to restart the container with new variables without rebuilding it, as before.
In our project, we use environment.ts
a lot for application configuration, so the final configuration is a merge of the variables specified in JSON and the variables specified in environment.ts
.