Grunt环境variables不会被设置,直到所有的任务加载

我在我的项目中使用npm模块grunt env和load-grunt-config 。 grunt env为你处理环境variables,而load-grunt-config处理,为你加载gruntconfiguration。 你可以把你的任务放到其他文件中,然后load-grunt-config将它们捆绑在一起,并且有很多的负载并且为你消耗。 您也可以创build一个aliases.js文件,将您想要合并为一个任务的任务一个接一个地运行。 它与原始Gruntfile.js的grunt.registerTask任务类似。 我把所有的grunt任务放在一个单独的grunt/文件夹下,并且在主Gruntfile没有额外的子文件夹,正如Github上的load-grunt-config README.md所build议的那样。 这是我瘦身的Gruntfile

 module.exports = function(grunt) { 'use strict'; require('time-grunt')(grunt); // function & property declarations grunt.initConfig({ pkg: grunt.file.readJSON('package.json') }); require('load-grunt-config')(grunt, { init: true, loadGruntConfig: { scope: 'devDependencies', pattern: ['grunt-*', 'time-grunt'] } }); }; 

从理论上讲,将所有这些文件设置为load-grunt-config加载的正确方式应该与只有一个Gruntfile.js 。 不过,我似乎遇到了一点麻烦。 似乎在env任务下设置的环境variables不会为随后的grunt任务设置,而是由时间node处理其任务(在本例中为express服务器)设置。

grunt env任务:

 module.exports = { // environment variable values for developers // creating/maintaining site dev: { options: { add: { NODE_ENV: 'dev', MONGO_PORT: 27017, SERVER_PORT: 3000 } } } }; 

grunt-shell-spawn任务:

 // shell command tasks module.exports = { // starts up MongoDB server/daemon mongod: { command: 'mongod --bind_ip konneka.org --port ' + (process.env.MONGO_PORT || 27017) + ' --dbpath C:/MongoDB/data/db --ipv6', options: { async: true, // makes this command asynchronous stdout: false, // does not print to the console stderr: true, // prints errors to the console failOnError: true, // fails this task when it encounters errors execOptions: { cwd: '.' } } } }; 

咕噜快递任务:

 module.exports = { // default options options: { hostname: '127.0.0.1', // allow connections from localhost port: (process.env.SERVER_PORT || 3000), // default port }, prod: { options: { livereload: true, // automatically reload server when express pages change // serverreload: true, // run forever-running server (do not close when finished) server: path.resolve(__dirname, '../backend/page.js'), // express server file bases: 'dist/' // watch files in app folder for changes } } }; 

aliases.js文件( grunt-load-config将任务合并为一个接一个的方式):

 module.exports = { // starts forever-running server with "production" environment server: ['env:prod', 'shell:mongod', 'express:prod', 'express-keepalive'] }; 

backend/env/prod.js (特定于环境的Expressconfiguration,如果NODE_ENV设置为“prod”,则在MEAN.JS之后build模 ):

 'use strict'; module.exports = { port: process.env.SERVER_PORT || 3001, dbUrl: process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://konneka.org:' + (process.env.MONGO_PORT || 27018) + '/mean' }; 

backend/env/dev.jsdev环境的特定于环境的Expressconfiguration,如果NODE_ENVvariables未设置或设置为“dev”,则加载):

 module.exports = { port: process.env.SERVER_PORT || 3000, dbUrl: 'mongodb://konneka.org:' + (process.env.MONGO_PORT || 27017) + '/mean-dev' }; 

部分backend/page.js (我的快速configuration页面,也模仿MEAN.JS):

 'use strict'; var session = require('express-session'); var mongoStore = require('connect-mongo')(session); var express = require('express'); var server = express(); ... // create the database object var monServer = mongoose.connect(environ.dbUrl); // create a client-server session, using a MongoDB collection/table to store its info server.use(session({ resave: true, saveUninitialized: true, secret: environ.sessionSecret, store: new mongoStore({ db: monServer.connections[0].db, // specify the database these sessions will be saved into auto_reconnect: true }) })); ... // listen on port related to environment variable server.listen(process.env.SERVER_PORT || 3000); module.exports = server; 

当我运行grunt server ,我得到:

 $ cd /c/repos/konneka/ && grunt server Running "env:prod" (env) task Running "shell:mongod" (shell) task Running "express:prod" (express) task Running "express-server:prod" (express-server) task Web server started on port:3000, hostname: 127.0.0.1 [pid: 3996] Running "express-keepalive" task Fatal error: failed to connect to [konneka.org:27018] Execution Time (2014-08-15 18:05:31 UTC) loading tasks 38.3s █████████████████████████████████ 79% express-server:prod 8.7s ████████ 18% express-keepalive 1.2s ██ 2% Total 48.3s 

现在,我似乎无法把数据库连接起来,但现在却忽略了这一点。 请注意,服务器在端口3000上启动,这意味着在执行grunt express:prod任务期间, SERVER_PORT未设置,因此端口被设置为3000.还有很多其他类似的例子,其中没有设置环境variables我的应用程序使用默认值。 但是,请注意session尝试连接到端口27018上的数据库(并失败),因此MONGO_PORT最终会被设置。

如果我刚刚尝试了一下grunt server任务,我可以把它load-grunt-configload-grunt-config并行运行任务,而不是一个接一个地执行任务或者其他错误,但是即使我一个接一个地尝试任务,比如运行grunt env:prod shell:mongod express-server:prod express-keepalive ,我得到了类似(不正确)的结果,所以无论是grunt还是grunt env可以同时运行任务,或者其他的事情正在进行。

这里发生了什么? 为什么环境variables没有正确地设置为以后的grunt任务? 他们什么时候最终定下来,为什么那么而不是其他时间呢? 我怎样才能使他们自己而不是后来的grunt任务,假设甚至有一种方式?

解决这个问题的方法很明显,一旦你弄明白了,让我们从头开始:

问题

您正在使用load-grunt-config加载一组模块(定义任务的对象)并将它们组合成一个模块(对象)并将其传递给Grunt。 为了更好地理解load-grunt-config在做什么,花点时间仔细阅读源代码(这只是三个文件) 。 所以,而不是写作:

 // filename: Gruntfile.js grunt.initConfig({ foo: { a: { options: {}, } }, bar: { b: { options: {}, } } }); 

你可以写这个:

 // filename: grunt/foo.js module.exports = { a: { options: {}, } } // filename: grunt/bar.js module.exports = { b: { options: {}, } } // filename: Gruntfile.js require('load-grunt-config')(grunt); 

基本上,这样你就可以把一个Gruntconfiguration分成多个文件,让它更“维护”。 但是你需要意识到的是这两种方法在语义上是等价的。 也就是说,你可以期望他们的行为是一样的。

因此,当你写下面的*:

(*我已经减less了这个问题,试图让这个答案更普遍一些,并减less噪音,我已经排除了加载任务和无关的选项传递的东西,但错误应该仍然是一样的。 我已经更改了环境variables的值,因为默认值与设置的值相同。)

 // filename: grunt/env.js module.exports = { dev: { options: { add: { // These values are different for demo purposes NODE_ENV: 'dev', MONGO_PORT: 'dev_mongo_port', SERVER_PORT: 'dev_server_port' } } } }; // filename: grunt/shell.js module.exports = { mongod: { command: 'mongod --port ' + (process.env.MONGO_PORT || 27017) } }; // filename: grunt/aliases.js module.exports = { server: ['env:prod', 'shell:mongod'] }; // filename: Gruntfile.js module.exports = function (grunt) { require('load-grunt-config')(grunt); }; 

你可以考虑以上相同的如下:

 module.exports = function (grunt) { grunt.initConfig({ env: { dev: { options: { add: { NODE_ENV: 'dev', MONGO_PORT: 'dev_mongo_port', SERVER_PORT: 'dev_server_port' } } } }, shell: { mongod: { command: 'mongod --port ' + (process.env.MONGO_PORT || 27017) } } }); grunt.registerTask('server', ['env:dev', 'shell:mongod']); }; 

现在你看到这个问题了吗? 你期望shell:mongod什么命令shell:mongod运行? 正确的答案是:

 mongod --port 27017 

你想要执行的是:

 mongo --port dev_mongo_port 

问题是,当(process.env.MONGO_PORT || 27017)被评估时,环境variables还没有被设置(即在env:dev任务已经运行之前)。

一个办法

那么让我们看看一个正在工作的Gruntconfiguration,然后将其分解到多个文件中:

 module.exports = function (grunt) { grunt.initConfig({ env: { dev: { options: { add: { NODE_ENV: 'dev', MONGO_PORT: 'dev_mongo_port', SERVER_PORT: 'dev_server_port' } } } }, shell: { mongod: { command: 'mongod --port ${MONGO_PORT:-27017}' } } }); grunt.registerTask('server', ['env:dev', 'shell:mongod']); }; 

现在,当你运行shell:mongod ,命令将包含${MONGO_PORT:-27017}而Bash(或者只是sh)将查找你之前在任务中设置的环境variables(即env:dev )。

好吧,这对shell:mongod任务来说都很好,但其他任务又如何呢?

你需要远离环境variables(除非你想调用Grunt 之前设置它们,为什么?以这个Gruntconfiguration为例:

 module.exports = function (grunt) { grunt.initConfig({ env: { dev: { options: { add: { NODE_ENV: 'dev', MONGO_PORT: 'dev_mongo_port', SERVER_PORT: 'dev_server_port' } } } }, express: { options: { hostname: '127.0.0.1' port: (process.env.SERVER_PORT || 3000) }, prod: { options: { livereload: true server: path.resolve(__dirname, '../backend/page.js'), bases: 'dist/' } } } }); grunt.registerTask('server', ['env:dev', 'express:prod']); }; 

express:prod任务configuration包含哪个端口? 3000 。 你需要的是引用你在上面的任务中定义的值。 你如何做到这一点取决于你。 你可以:

  • 分开envconfiguration并引用它的值

     module.exports = function (grunt) { grunt.config('env', { dev: { options: { add: { NODE_ENV: 'dev', MONGO_PORT: 'dev_mongo_port', SERVER_PORT: 'dev_server_port' } } } }); grunt.config('express', { options: { hostname: '127.0.0.1' port: '<%= env.dev.options.add.SERVER_PORT %>' } }); grunt.registerTask('server', ['env:dev', 'express:prod']); }; 

    但是你会注意到env任务的语义不能在这里保留,因为它不再代表一个任务的configuration。 你可以使用你自己devise的一个对象:

     module.exports = function (grunt) { grunt.config('env', { dev: { NODE_ENV: 'dev', MONGO_PORT: 'dev_mongo_port', SERVER_PORT: 'dev_server_port' } }); grunt.config('express', { options: { hostname: '127.0.0.1' port: '<%= env.dev.SERVER_PORT %>' } }); grunt.registerTask('server', ['env:dev', 'express:prod']); }; 
  • 通过grunt一个参数来指定它应该使用的configuration

  • 有多个configuration文件(例如Gruntfile.js.devGruntfile.js.prod )并根据需要重命名
  • 读取一个开发configuration文件(例如grunt.file.readJSON('config.development.json') ),如果它存在,并且回退到一个生产configuration文件,如果它不存在
  • 这里没有列出一些更好的方法

但以上所有都应该达到同样的最终结果。

这似乎是你想要做的事情的本质,它对我有用。 最重要的部分是我在评论中提到的 – 在执行其他任务之前,链接环境任务。

Gruntfile.js

 module.exports = function(grunt) { // Do grunt-related things in here grunt.loadNpmTasks('grunt-env'); grunt.initConfig({ env: { dev: { PROD : 'http://production.server' } } }); grunt.registerTask('printEnv', 'prints a message with an env var', function() { console.log('Env var in subsequent grunt task: ' + process.env.PROD) } ); grunt.registerTask('prod', ['env:dev', 'printEnv']); }; 

grunt prod输出

 Running "env:dev" (env) task Running "printEnv" task Env var in subsequent grunt task: http://production.server Done, without errors.