-
Notifications
You must be signed in to change notification settings - Fork 4.4k
[WIP] Improve support for .env files and different build environments (fix #1163 #903) #1178
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
Conversation
> Starts a Node.js local development server. See [API Proxying During Development](proxy.md) for more details. | ||
Starts a Node.js local development server. See [API Proxying During Development](proxy.md) for more details. | ||
|
||
> Sets process.env.NODE_ENV to `'development'` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added these notes to that people get the connection between the commands and the NODE_ENV the set. These define the "mode" the app is running in.
// see https://github.com/ampedandwired/html-webpack-plugin | ||
new HtmlWebpackPlugin({ | ||
filename: {{#if_or unit e2e}}process.env.NODE_ENV === 'testing' | ||
filename: {{#if_or unit e2e}}process.env.VUE_TEST = 'e2e' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See /test/e2e/runner.js
:
Here we set NODE_ENV
to test
, but for our e2e tests we run the webpack dev server with webpack.prod.conf.js
, because the want to run e2e tests against a devserver that serves files like in production.
Therefore, we have to use a second env variable as a workaround to make the production config behave like NODE_ENV === 'production'
for e2e tests.
* It should not contain any sensitive information. See README. | ||
*/ | ||
module.exports = { | ||
NODE_ENV: 'development' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we now aggregate variables and stingify their values in /build/utils
, we don't have to stringify them here anymore - so no more '" "'
double quotes!
{{/if_eq}} | ||
{{#if_eq runner "karma"}} | ||
"unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", | ||
"unit": "karma start test/unit/karma.conf.js --single-run", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we set the NODE_ENV to test
in the karma config, so BABEL_ENV will fall back to this.
"friendly-errors-webpack-plugin": "^1.6.1", | ||
"html-webpack-plugin": "^2.30.1", | ||
"webpack-bundle-analyzer": "^2.9.0", | ||
"minimist": "^1.2.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can actually be removed, leftover from an experiment.
const utils = require('./utils') | ||
const config = require('../config') | ||
const isProduction = process.env.NODE_ENV === 'production' | ||
const isProduction = process.env.NODE_ENV === 'production' || process.env.VUE_E2E === 'test' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See /test/e2e/runner.js
:
Here we set NODE_ENV
to test
, but for our e2e tests we run the webpack dev server with webpack.prod.conf.js
, because the want to run e2e tests against a devserver that serves files like in production.
Therefore, we have to use a second env variable as a workaround to make the production config behave like NODE_ENV === 'production'
for e2e tests.
Sidenote: The tests are failing because of memory issues on CircleCI. Maybe needs a workaround, but not relevant to this PR. |
Holy fuck. I know my comment doesn’t add anything useful but damn 👏👏. I’ll check this as soon as I can. Thanks for your work on this!!! |
Looking forward to it :) |
Wow again, I really appreciate you all are looking into this and advancing this template for our needs 👍 👏 The modes are the different ways your app can run:
I would recommend to rename "production mode" to "build mode" or "shipment mode". I think sentences like these causes confusion:
Other than that I agree with the split in modes and environments. A bigger topic
Maybe I misunderstood but it looks like this setup again limits me to three different configs via If the application is built in any other environment than my local computer, the env vars should come from the server/environment and it's the developers/teams job to make them available. There is no Which makes me question the usecase of Mode and environment should not be coupled IMO. If a developer choses to build the application always locally and then deploys it to the different stages, THEN the configs need to be available locally - but again, the mode should not matter. For example:
Now let's say I want to deploy the app to QA and go through the two options: build locally vs build on the server. Build locallyI run Build RemotelyI deploy my app and run This solution eliminates any assumption about how many environments there are and there is no need to namespace anything. Hope this helps in any way - let me know if I missed something and misunderstood the solution. |
@codeofsumit FYI that's exactly how the env loading in vue-cli-next is handled. You can have as many modes as you want, and each mode specifies its own env variables. So you do something like |
@codeofsumit thanks for the feedback, I need to find the time for a longer reply, hopefully before the weekend. |
} | ||
``` | ||
|
||
You will also find a file called `externals.js` in this folder. Use this file if to map any environment variables that you want to pass to your Vue app independent of the mode. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reasoning behind the name "externals"?
Since this file is loaded regardless of "mode" it seems like the place to store the default values that will get overwritten with mode/env specific values - hence maybe "defaults" would make more sense than "externals"?
Just running into this in my app now. On a related note - I'm not sure why yet, but the define-plugin does work within vue components while the environment-plugin does not. |
}, | ||
plugins: [ | ||
new webpack.DefinePlugin({ | ||
'process.env': utils.stringifiedEnvVars |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a little nitpicky, but this fn name makes me think that ALL env vars will be stringified and exposed.
You can define these values in the files with the name matching the mode. | ||
|
||
```javascript | ||
// config/variables/development.js |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The naming of the variables
dir here is also a little confusing. Aren't all your env vars and webpack config settings "variables"? Maybe config/exposed
or config/public
would be more clear that these are the variables that will be accessible to the build/browser.
Similarly, when I started using this template, I found the fact that config/index.js
was mostly build-related configuration a bit confusing. Maybe rename to config/build.js
or config/webpack
? The more explicit import would likely make it more clear. It could also be worth a quick review of what config lives in there vs what lives in the build directory in the various webpack config files.
Perhaps config/index
would be the file that loads various files and exposes the formats needed in different places throughout.
## Passing Environment Variables to your application | ||
|
||
Sometimes it is practical to have different config values according to the environment that the application is running in. | ||
You application can run in different modes, as documented in the chapter about [Commands](commands.md) - `development`, `test`, `production`. Each of these modes might require different default values for things like: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo "Your"
|
||
We agree to that as well. But `development`, `test` and `production` are not so much build environments as they are different *modes* that the application can run in. | ||
|
||
Since there are many situations where you want to run more than one of those modes in the same environment, but with different values for a given variable (i.e. running `dev` and `test` locally, as further up), these different values have to be passed in with different environment variables. But these different variables have to be re-mapped to the same name if they ought to be accessible within the application: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This flexibility is nice, but don't many people use multiple .env files (for different modes) to solve this problem?
Maybe worth adding a note about using separate .env files as the "switch" rather than remapping in the config/variables
files.
'use strict' | ||
/** | ||
* This file should define variables that webpack should make available to | ||
* your application when running in development mode (NODE_ENV === 'production') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo : "when running in production mode"
'use strict' | ||
/** | ||
* This file should define variables that webpack should make available to | ||
* your application when running in development mode (NODE_ENV === 'test') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo : "when running in test mode"
I won't finish this. |
Will something else be taking its place instead? |
I won't put much energy into feature development for this template, since vue-cli 3 is already in RC now, and it doesn't make sense to start new projects with this template in my personal opinion. I would assume that this template won't be useful anymore compared to vue-cli 3 within weeks after vue-cli 3 has officially become stable. And since there is no clean update path for this template from an old version to a new one, new features have very limited use for existing projects as well - you can switch those over to vue-cli 3 at least as easy, probably. I'd be happy to merge changes to this template proviced they are tested solidly (which is not easy!) though, so go ahead if you want to improve this. |
Ah - didn't realize vue-cli 3 will be replacing. |
Preface
This PR is really big, with a lot of small and bigger changes all over the place. It was quite challenging to reach some kind of consistency - and it surely has errors, mistakes and quirky code here and there, so I'm looking for extensive constructive feedback.
Something important to understand: I tried to establish two different terms:
mode
andbuild environment
.The
modes
are the different ways your app can run:npm run dev
( =development
mode, NODE_ENV='development')npm run build
( =production
mode, NODE_ENV='production')npm run test
( =test
mode, NODE_ENV='test')The
build environment
means the physical environment where you run any of those modes:Running in
production
mode in astaging
environment probably requires different environment variable values than in theproduction
environment, etc. These changes attempt to provide a way to do that in a way that is maintainable and safe.Keep that in mind when checking out these changes and especially when reading the re-written docs, and please tell me if it succeeds in getting that across.
What this PR does
The important conceptual changes:
test
- we had instances of bothtest
andtesting
.dotenv
package and call it where necessary to load environment variables from a.env
file:/config/webpack.dev.conf.js
/config/webpack.prod.conf.js
/test/unit/karma.conf.js
/test/unit/setup.js
*.env.js
files:/config/*.env.js
to a subdirectory called/variables
and renamed them./config/variables/externals.js
to map general environment variables<hr>
./variables
to load to/build/utils
, which allowed me to move theDefinePlugin
from thedev
andprod
configs to thebase
config.Reviewing
I'll mention people possibly interested i this with the reuest of review in term of both code quality and usefullness for their use cases:
Please also review the following Documentation, both stylistically and content-wise. I'm no expert on the whole 12Factors philposphy so maybe what I wrote and argued is a whole lot of crap. If that's the case, please say so ;)
Documentation for this:
Passing Environment Variables to your application
You application can run in different modes, as documented in the chapter about Commands -
development
,test
,production
. Each of these modes might require different default values for things like:You can define these values in the files with the name matching the mode.
You can also map to environment variables easily.
You will also find a file called
externals.js
in this folder. Use this file if to map any environment variables that you want to pass to your Vue app independent of the mode.How are these files processed?
When you run one of the available commands,
process.env.NODE_ENV
will be set to one of the three modes mentioned above, and the appropriate module from/config/variables
will be required. The object exported by that module will be merged with the one fromexternals.js
. In case of duplicate entries, the former overwrites the latter.The resulting object will be passed to webpack's
DefinePlugin
to make the values available in your Vue application.How do I use these values in my application?
You can access each of these values as properties of
process.env
inside your app.Example:
.env
file supportWhenever you run one of the available commands (except
lint
), We use dontenv to load any environment variables that you have provided with an.env
file in the root directory of your project.That means that you can pass values from your
.env
file to your Vue app through the variable files we introduced above.Example:
Strategies for different build environments.
Usually you have more build environments than the three app modes (
development
,test
,production
): You might have a CI environment where you run tests, a staging environment where you run a built version of your app for your team to test, but with a staging API backend, a QA environment that holds a "copy" of your prod for debugging purposes and so on. In some of those environments you only runnpm run test
, in others onlynpm run build
and in some you run both, or evennpm run dev
. The bottom line is, each app's setup is different, each team's requirements are different.How can you configure those different environments and still keep configuration flexible and secure?
The answer for this template is environment variables configured in an
.env
file, namespaced by "mode".Here's how this could look for a "staging" environment:
Now when you run
npm run test
, inside of your Vue app,process.env.API_KEY
will match the value ofAPI_KEY_TEST
in your.env
file, when you runnpm run build
, it will matchAPI_KEY_PROD
.In this example both have the same value, because you want to run the
build
command with the same API_KEY as thetest
command, but they can be different in other environments, like when you develop locally:In your app, you can access
process.env.API_KEY
and will get the correct value in each mode.A word on 'seperation of config from code' according to "The 12 Factor App"
The 12 Factor App has a section about configuration values that recommends to extract all such values into environment variables, and never save them inside code - like we allow you do in the files mentioned above.
The main concerns are:
production
credentials)test
orproduction
, orstaging
and so on.We think this is mostly good advice, but in the context of this template, some things are to be considered to get some perspective:
Sensitive Information
Every value that you pass into your app and use it can be accessed by your users one way or another. For that reason, frontend apps (the kind this template is meant for) should generally do not contain sensitive information like API secrets or database keys. If however you do need those values in your build setup, for example, it is absolutely good practice to pass those in via environment variables - just don't don't pass them to your Vue app through the
/variables
files we discussed above.Config files per environment are inflexible
We agree to that as well. But
development
,test
andproduction
are not so much build environments as they are different modes that the application can run in.Since there are many situations where you want to run more than one of those modes in the same environment, but with different values for a given variable (i.e. running
dev
andtest
locally, as shown in an example further up), these different values have to be passed in with different environment variables. But these different variables have to be re-mapped to the same name if they ought to be accessible within the application:While you might have these environment variables:
Your app only knows about
process.env.API_URL
, so we have to map the value of these environment variables toAPI_URL
depending on the mode we are running in. That's what the files in/config/variables
are for.