Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upRequest: Method to pass environment variables during build vs file. #4318
Comments
|
@hansl we were just discussing this, thought you might have something to add |
|
Why not make ENV variables available in process.env again? I'm really feeling comfortable with Heroku Config Vars and Codeship environment variables, I already set up them, moved all my sensitive data like secret keys to angular-cli environment.dev.ts, BUT I can't use them.( export const environment = {
production: false,
GOOGLE_RECAPTCHA_SITE_KEY: '6Le...Zq'
};Because there is no way to access variables from test env or production like: export const environment = {
production: false,
GOOGLE_RECAPTCHA_SITE_KEY: process.env.GOOGLE_RECAPTCHA_SITE_KEY
};in my environment.test.ts and environment.prod.ts Need to thinking on using third party packages like dotenv or env2 P.S: Is it hard to implement configuration like this: ...
"scripts": [],
"environments": {
"source": "environments/environment.ts",
"dev": "environments/environment.dev.ts",
"test": "environments/environment.test.ts",
"prod": "environments/environment.prod.ts"
},
"processEnv": [
"CUSTOM_ENV_VAR",
"GOOGLE_RECAPTCHA_SITE_KEY",
...
]So then there will be opportunity access them from process.env.CUSTOM_ENV_VAR, etc. |
|
I ran into the same issue a day ago. And for a temporary workaround, that works (just checked it with Codeship), I just added the "prebuild" script, that generates the appropriate environment file using current env variables. ejs is used as a template processor due to it's simplicity. scripts/prebuild.js - this is a script with the required logic #!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const ejs = require('ejs');
const environmentFilesDirectory = path.join(__dirname, '../src/environments');
const targetEnvironmentTemplateFileName = 'environment.prod.ts.template';
const targetEnvironmentFileName = 'environment.prod.ts';
// Define default values in case there are no defined ones,
// but you should define only non-crucial values here,
// because build should fail if you don't provide the correct values
// for your production environment
const defaultEnvValues = {
PREFIX_STORAGE_TYPE: 'localStorage',
PREFIX_USER_TOKEN_FIELD_NAME: 'userToken',
};
// Load template file
const environmentTemplate = fs.readFileSync(
path.join(environmentFilesDirectory, targetEnvironmentTemplateFileName),
{encoding: 'utf-8'}
);
// Generate output data
const output = ejs.render(environmentTemplate, Object.assign({}, defaultEnvValues, process.env));
// Write environment file
fs.writeFileSync(path.join(environmentFilesDirectory, targetEnvironmentFileName), output);
process.exit(0);src/environments/environment.prod.ts.template - this is a template file, that generates the required environment file. Notice, that export const environment = {
production: true,
backendUrl: '<%= PREFIX_BACKEND_URL %>',
storageType: '<%= PREFIX_STORAGE_TYPE %>',
userTokenFieldName: '<%= PREFIX_USER_TOKEN_FIELD_NAME %>'
};package.json - there are some changes here. In scripts/prebuild.js shebang (#!) style is used, so "prebuild" is just a pointer to the script file. And to make it work it must be marked as executable, that's why "postinstall" script is added either. {
...
"scripts": {
...
"build": "ng build --prod --aot",
"prebuild": "./scripts/prebuild.js",
...
"postinstall": "chmod +x ./scripts/*.js"
},
...
"devDependencies": {
...
"ejs": "^2.5.6",
...
}
}
|
|
@k10der this is great, this is similar to what we did. What we do is actually move the environment scripting out of angular totally. We have a single environment.ts file for all builds and use a custom script to generate the correct values via the env variables. I still think this should be something within the CLI but there are of course workarounds. |
|
@DennisSmolek, I also think, that handling of process.env variables should be built-in in cli. And your approach to use a single environment file, that is generated by external tool makes sense: my dev environment variables are currently stored in the project repository, which is OK unless I'll start using some 3rd party APIs with private keys. So I guess one-environment-file approach should be used in future angular-cli versions. |
|
Injecting environment variables during build seems essential. Any ETA for supporting this in the ng cli? |
|
Why would common cross platform method for OS configuration support of env variables be excluded? Unless there's a strong argument against 12factor that is being proposed for angular? |
|
I'd also really like to be able to use process.env variables. I'm deploying to docker swarms and the capability to be able to include variables in a compose file is important to my application. |
|
very needed for dockerizing the apps indeed |
|
@k10der Do you have any repository on github that is using the solution you proposed? I'd like to check it because I could not find any real solution on the internet. |
|
Hey, @carlosthe19916. I have a sample project, that I use just for practicing in Angular2+. It's not a production ready and you probably won't get any benefit from running it, but I use the proposed solution there. And it's exactly as I described it in this topic. |
|
+1 |
|
Hi friends... What I think I want is to access environment variables at run time (in addition to build time as under discussion here.) For continuous integration purposes, I want to build only once, then deploy that same dist folder to my staging, qa, and prod environments using environment variables set with my CI provider on my runtime box. At first noodling |
|
This is also my use case. I'm thinking to ignore the Angular CLI environments and use a plain old JavaScript include that sets my environment vars in a window.environment var. Then, I can just deploy a different JavaScript include file and have different variable values with the same build output. |
|
Workaround ahead. Might work fine in your setup, might not. I've used two angular cli environments ( Production and Staging are using the same src/environment/environment.ts export const environment = {
production: false,
backendUrl: (<any>window)._env.backendUrl,
};src/environment/environment.prod.ts export const environment = {
production: true,
backendUrl: (<any>window)._env.backendUrl,
};src/environment/environment.values.js (development variables) window._env = {
backendUrl: 'https://localhost:7000',
};src/environment/environment.prod.values.js (production variables) window._env = {
backendUrl: 'https://api.example.com/',
};src/environment/environment.stag.values.js (staging variables) window._env = {
backendUrl: 'https://api-staging.example.com/',
};Next, you need to make sure that you add this line to your ...
<head>
...
<script src="/assets/env.js"></script>
</head>
...The tests also need to have these values, so I've loaded them at the top of the src/test.ts // Load default environment values
import 'environments/environment.values.js';
...Finally, you need to change your deployment/development scripts so that you execute this command: For production: cp src/environments/environment.prod.values.js dist/assets/env.jsFor staging: cp src/environments/environment.stag.values.js dist/assets/env.jsFor development: cp src/environments/environment.values.js dist/assets/env.jsI'm using Gitlab CI for deployment, and I execute this command before copying the You might also want to add the |
|
How about the |
|
Another quick solution (any shell command in a template could break everything) package.json: "prebuild": "eval \"echo \\\"$(cat src/environments/environment.ts.template)\\\"\" > src/environments/environment.ts",environment.ts.template: export const environment = {
production: false,
clientId: \"${CLIENT_ID}\",
apiId: \"${API_ID}\",
authDiscoveryEndpoint: \"${AUTH_DISCOVERY_ENDPOINT}\"
};Also, something like this could work better perl -p -i -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : $&/eg' < src/environments/environment.ts.template | tee src/environments/environment.ts |
|
My current workaround is a pre and post build event which replaces a version-tag in my environment.prod.ts file install npm-replace: package.json Jenkins runs it with: ($Tag is my release-tag, for example "1.0.0") |
|
We wound up sing this Visual Studio Team Services extension in our release. Very happy with it. |
|
Adding my voice to this request--in the middle of doing DevOps work on three different Angular 4 applications and the lack of access to environment variables has made this much harder than it needs to be. I'm hopeful this can be rectified soon. I agree with @GaryB432 that access at both build and runtime would be great, but I could settle with build time if need be. |
|
To add to this, on Heroku I currently need to commit new code to change the env variables. Having access to process.env would let us change the environment variables through the browser and command line without needing to commit anything. |
|
If anyone is interested we made a little command line util to add to your CD step. It takes an EJS template and processes with |
|
Just adding to my comment above, if you guys are using Azure we created a build/release step that plugs environment variables into a small shim script. Very simple and meets our requirement, viz: build one dist folder and deploy it to multiple environments. I'm sure the underlying package on which it is based, the one I mentioned in the previous comment, is adaptable to other CI/CD ecosystems. |
|
This CLI approach generates a file that doesn't messes up with the version control and keeps them on environment.ts |
|
@ricardosaracino Agreed… @mrdulin You can use |
|
@peschee I care about how to inject env vars at runtime, not build process. There is a way like @titanium170 said, is to using custom webpack config and plugins so that I can inject env vars into |
|
if you are serving the site with apache, or IIS there is no process.env, i dont think this makes much sense in terms of being in angluar |
|
This is actually beyond the web server used. A browser does not have environment variables. |
|
@mrdulin Maybe something like this might fit your use case? You can add it by executing |
|
While I'd like to see environment variables get first class support in the CLI, the angular-builders project makes it really easy to add support yourself. Example webpack config (webpack.config.js) I've been using: const Dotenv = require('dotenv-webpack');
module.exports = {
plugins: [
new Dotenv()
]
};You'll then need to modify the angular.json file as mentioned in the angular builder docs. |
|
My original issue at #7507 (comment) was closed in favor of this one. From a 12 Factors perspective, dotenv is, strictly speaking, the anti-pattern of writing config to files. see: https://12factor.net/config The EnvrionmentPlugin for webpack is already included in production builds. The EnvironmentPlugin could be in all builds and .angular-cli.json could be extended to include defaults for your project. This approach doesn't add new plugins, only requires minimal changes to the current build process, and keeps all the config in .angular-cli.json. |
|
Some kind of workaround if you are using some sensitive data that you shouldn't commit, I don't know if it has been suggested before, I try to read the whole thread but it's from 2016, for code sake. Create a typescript file, exporting the data you need, and don't commit that file. I know it's not as simple as having access to I have my firebase configuration as:
Both environment and environment.prod reexport the firebase config file as its content won't change. |
I agree. We use nconf instead which allows us to use hierarchical configuration: e.g. we define default values in env-files and override what's needed via env-vars. |
|
+1 Any update from the Angular team on this one? |
|
@clydin to evaluate some possible designs and come up with a proposal. |
|
I have used this approach of loading a local json file ... it works well.
|
This works but its for runtime. This is supported very easily with webpack i dont see it being so hard to implement |
I know... just thought someone might stumble across this post and need a solution |
|
A humble summary of available ways to manage app state from command line. Whether angular app is running on server, it is possible to convert env variables to angular providers as noted here https://angular.io/guide/universal which is architecturally a cleaner way imo: server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});As for common builder way there is an option to provide different configuration (https://angular.io/guide/workspace-config#alternate-build-configurations) that in turn can affect environment file or any other build artifact, but in enumish way: one cannot provide some custom values but instead choose of any predefined ones. |
|
I use Azure build pipe lines to build artifacts and to deploy to on premise servers. Testing a build on QA and then rebuilding for Production is not good practice.. The QA tested build should be promoted. |
I dont like angular's Essentially if i need all configs to be the same except 1 i need to create a whole new configuration file? Add that to my angular.json? Recommit my code, get PR reviewed, Update my pipelines... its too much.
And if my devops guy needs to test a new environment with different configurations i dont need to be involved at all. |
|
To differentiate, because a few different issues seem to be conflated here. Build-Time Configuration
Desired (As stated in the feature request of this issue, if I understand it correctly):
Is @clydin going to (only) focus on this? Will #3855 be taken into account? Workarounds:
Startup/Runtime Configuration Is this out of scope for @clydin? Should a separate issue be created for this? Possible solutions:
|
|
How can I get env variable from heroku env and use them in my code? I was searching and did not get any solution :( |
|
I have been using venv, a pretty simple solution that works with angular or with any js app. |
|
Dear Angular Developers,
Passing environment variables should be a no brainer basic feature of any
build system. Without it, we are left to use third party tools (e.g.
Tokenizer) or if these tools are not available, create our own function to
find and replace key,value pairs within the environment.ts file; thereby
making temporary changes to build source code.
A better approach would be to have Angular accept environment variables or
even pass them in the command line. This would *enable build developers
to write polymorphic build scripts* that will set up and do the right thing
for the environment being built for. Examples where this is important are:
- building for a local Dev or QA environment on any OS, Container, etc.
- building for a shared Dev, Staged environment on any OS, Container,
Pod, etc.
- building for a Production environment on any OS, Container, Pod, etc.
Values that would be easier to pass into Angular via environment variables:
- IP Address and port of service
- IP Addresses and ports of services being dependent upon
- Location of TLS Certificates
What is the difficulty here? Do you need assistance with making this
happen?
Kind regards,
Ralph
Ralph A. Navarro Jr.
*Navarro Computing LLC <https://navarrocomputing.com>*
Mobile: *+1(508)**287-0190*
Skype: eaglet3d
…On Tue, Apr 28, 2020 at 8:49 AM Gary Bortosky ***@***.***> wrote:
I have been using venv <https://www.npmjs.com/package/venv>, a pretty
simple solution that works with angular or with any js app.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#4318 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AA2VJIV7ENQU34KSDWMBLITRO3GGDANCNFSM4C6P2MVA>
.
|
|
I used a trick to set the I'm using a custom webpack file to be able to use Tailwind CSS with Angular. process.env['NODE_ENV'] = process.argv.indexOf('--prod') !== -1
? 'production'
: '';Then, when the production build starts with Definitely not ideal but it work for the time being (with 9.1.1). |
Not defending the angular team here as this definitely would be a useful feature, however almost all projects I've worked on just hit /api. Seems to me all your issues could be addressed by just deploying your frontend along side your backend, and you avoid CORS issues and many other problems. Of course this only applies if you want a backend (or at least a basic nginx, which I'm guessing you'd need for 404/reload handling anyways)? When you need to embed, for example the commit hash, as the example provided by @DennisSmolek. I went around this as follows: git-version.js
Then, run it - it will generate a TS file. Import this from app component (or wherever) and:
Then on CI build: This will make sure the build has the latest commit hash. There are other variations of this but yes, would be nice to do this without hacks. |

OS?
Any, mostly a build/CI question
Versions.
1.0.0-beta.26
Reasoning
I really like the environment variables setup with the CLI and we have switched to using them. One problem though is we no longer have access to server set ENV variables which is how we pass information to certain things during deployment. Consider:
Codeship ENV variables
Being able to add the release stage, branch, and commit ID allows us to segment and track bugs/issues very accurately.
My problem is I know the build takes whatever stage I pass and replaces the
environment.tsfile so I can't just edit the file with a bash script (well, if I know EXACTLY the build before hand I could) but it would be nice to be able to pass env variables in the build line.Consider:
ng build --prodcould be:ng build --prod --envVar:commitId: CI_COMMIT_IDwhich would append the variable after the file gets merged
something like this would ensure it gets added to the right file and at the right time/place..
Then we could do: