-
Notifications
You must be signed in to change notification settings - Fork 12k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Request: 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.js For staging: cp src/environments/environment.stag.values.js dist/assets/env.js For development: cp src/environments/environment.values.js dist/assets/env.js I'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 |
@Gnyblast Thank you for sharing ... I used the window['env'] pattern that was used in the original article which caused the unit test to fail on build |
@arambazamba Do you really need to use real env data on testing? Since it's a test and you kind of mock everthing, you can also mock the environment file and pass that one in by configuring it for testing. So that way you don't have to worry about Let say:
you can also have
so that when you configure your envirnment to use |
From what I understand, nothing has changed since I wrote this two years ago, apart from other suggestions for Startup/Runtime Configuration, which is a different issue angular/angular#3855 (and the Angular CLI team has indicated that they do not want to provide/maintain a solution to that in the CLI). |
@Gnyblast Thank for you input 👍 I had a similar thought after I replied to you … Actually I need the injection only on a prod build from which I will create the tagged artifact. So I removed the injection from the default environment file and left it only in environment.prod.ts. Works perfect for my needs |
Hey any updates on this? |
@LemonyPie you can just load a assets file using
|
@ricardosaracino thanks for the idea, but in my team we used a single docker image for stage and prod. So I wanted to have a way to set the env variables during the image build to deploy the same image and be sure it's working correctly changing files seems not robust as thing may go wrong on prod after testing on stage |
See above: #4318 (comment)
@LemonyPie I'm not sure I understand your requirement. If you just need to set environment variables for runtime configuration, you could use angular-server-side-configuration, which is designed to support configuring Angular applications in containers (e.g. in a Kubernetes environment), as I implemented it for that specific case. |
Another way to do this is, as mentioned before, requesting the data from a file (maybe created by the HELM that deploy the node) or a property server. For this I created kuoki, with the Environment and the Environment Angular libraries. There are many options, but I use to run away from variable injection in code because of race condition problems in the properties load or the dependency from objects like Window, that's an open door to attacks. |
As a new user of Angular I was really surprised environment variables weren't supported. Every other major front-end framework I've so far used has supported them. After trying three or four of the recommended approaches here (spent half a day on them all), I personally settled on @chihab's Here's the result: https://www.dotenv.org/docs/frameworks/angular/vercel |
vite supports |
Another idea is opening esbuild-plugins to allow us create plugins like "define" ourselfs. Here an example using import { getVersion } from './utils.mjs'
/** @type {import('esbuild').Plugin} */
export default {
name: 'plugin-define',
setup: async (build) => {
const initialOptions = build.initialOptions
initialOptions.define ??= {}
initialOptions.define.APP_VERSION = JSON.stringify("x.x.x")
},
} |
Closed via #28362, which will be included in version 19, currently in prerelease. |
This is definately a step in the right direction but its still not using environment variables in the bundle so the initial request is not really fulfilled. |
I think this is a good step forward, but in the end, environment variables and files have been a de facto standard. If with this issue here you don't want to expose the underlying tooling, for example, Webpack in its time, and now Vite, why don't exposing them in your own way? One that doesn't need knowledge about the tooling. But the important part here is to be able to load a variable from the environment, and this is not loading from the environment. If you want to use a certain prefix to sanitize variables, that's ok, it's also de facto standard, and most tooling does that to prevent exposing variables not related to the app. And if you want to do it a standard future-proof way, using import.meta seems pretty standard and future-proof. Again, this is a great start, but I don't feel like this is really fixing the issue here. |
I completely agree with @michaeljota. I will not be able to replace my usage of dotenv with the feature that closed this issue. @alan-agius4 Please either reopen or advise us to open a new issue. |
Me neither, and no dev-server support |
I created a new issue that at least captures my unmet use case: Ability to set environment/configuration values at runtime instead of buildtime angular/angular#58433 |
Reopened this ticket as: #28661 |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
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.ts
file 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 --prod
could be:ng build --prod --envVar:commitId: CI_COMMIT_ID
which 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:
The text was updated successfully, but these errors were encountered: