在所有任务加载之前,Grunt环境变量不会被设置

时间:2014-08-15 18:56:56

标签: javascript node.js express gruntjs environment-variables

我在项目中使用npm模块grunt envload-grunt-configgrunt env为您处理环境变量,而load-grunt-config处理,为您加载grunt配置。您可以将您的任务放入其他文件中,然后load-grunt-config将它们捆绑起来,并grunt加载&为你消耗它们。您还可以创建一个aliases.js文件,其中包含要组合在一起的任务,一个接一个地运行。它与原始Gruntfile.js中的grunt.registerTask任务类似。我将所有grunt任务放在根文件夹下的主grunt/下的单独Gruntfile文件夹中,没有额外的子文件夹,如load-grunt-config README.md所建议的那样Github上。这是我精简的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任务下设置的环境变量没有为后续grunt任务设置,但是由node处理其任务的时间设置,在这种情况下是{{1服务器。

express任务:

grunt env

grunt-shell-spawn任务:

module.exports = {

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

grunt express任务:

// 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'] }; 的一部分(特定于环境的Express配置,如果backend/env/prod.js设置为" prod",则会在MEAN.JS之后建模时加载)

NODE_ENV

'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.js环境的特定于环境的Express配置,如果未设置`NODE_ENV变量或设置为" dev"),则加载:

dev

module.exports = { port: process.env.SERVER_PORT || 3000, dbUrl: 'mongodb://konneka.org:' + (process.env.MONGO_PORT || 27017) + '/mean-dev' }; 的一部分(我的快速配置页面,也是以MEAN.JS为模型):

backend/page.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

现在,我似乎无法首先连接数据库,但暂时忽略了这一点。请注意,服务器在端口3000上启动,这意味着在执行$ 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 任务期间,grunt express:prod未设置,因此端口设置为3000.还有许多其他示例,其中包含环境变量未设置,因此我的应用程序使用默认值。但是,请注意SERVER_PORT尝试在端口27018上连接到数据库(并且失败),因此最终会设置session

如果我刚刚尝试了MONGO_PORT任务,我可以将其归结为grunt server并行运行任务而不是一个接一个或其他错误,但即使我尝试任务一个接一个,例如运行load-grunt-config,我得到类似(不正确)的结果,因此grunt env:prod shell:mongod express-server:prod express-keepalivegrunt也可以并行运行任务,或者其他内容正在运行上。

这里发生了什么?为什么环境变量没有为以后的grunt env任务设置正确?他们什么时候最终确定,为什么然后而不是其他时间呢?我怎样才能使grunt任务本身而不是之后设置它们,假设有一种方法?

2 个答案:

答案 0 :(得分:7)

一旦你弄明白,解决方案就显而易见了,所以让我们从头开始:

问题

您正在使用load-grunt-config加载一组模块(定义任务的对象)并将它们组合到一个模块(对象)中并将其传递给Grunt。为了更好地了解load-grunt-config正在做什么,请花一点时间read through the source (it's just three files)。所以,而不是写:

// 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);

基本上,通过这种方式,您可以将Grunt配置拆分为多个文件,并使其更具“可维护性”。但是你需要意识到的是,这两种方法在语义上是等价的。也就是说,你可以期望它们的行为方式相同。

因此,当您编写以下*:

(*我已经减少了问题,试图使这个答案更加通用并减少噪音。我已经排除了诸如加载任务和无关选项传递之类的事情,但错误应该仍然是相同的。 另请注意,我已更改了环境变量的值,因为默认值与设置的值相同。)

// 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运行什么命令?正确答案是:

mongod --port 27017

你想要执行的是:

mongo --port dev_mongo_port

问题在于,当评估(process.env.MONGO_PORT || 27017)时,尚未设置环境变量(即在env:dev任务运行之前)。

解决方案

让我们先看看一个正在运行的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 ${MONGO_PORT:-27017}'
            }
        }
    });
    grunt.registerTask('server', ['env:dev', 'shell:mongod']);
};

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

好的,这对于env:dev任务来说一切都很好,但是其他任务怎么样呢,例如Express?

你需要摆脱环境变量(除非你想在调用Grunt之前设置它们。为什么?以此Grunt配置为例:

shell:mongod

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。您需要的是它引用您在上述任务中定义的值。你如何做到这一点取决于你。你可以:

  • 分隔3000配置并引用其值

    env

    但是你会注意到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
  • 传递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']); }; 参数以指定应使用的配置

  • 拥有多个配置文件(例如gruntGruntfile.js.dev)并根据需要重命名
  • 阅读开发配置文件(例如Gruntfile.js.prod)(如果存在)并回退到生产配置文件(如果不存在)
  • 此处未列出的更好方法

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

答案 1 :(得分:4)

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

<强> 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.