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

try*_*sis 5 javascript environment-variables node.js express gruntjs

我在我的项目中使用npm模块grunt envload-grunt-config.grunt env为您处理环境变量,而load-grunt-config句柄grunt则为您加载配置.您可以将您的任务放入其他文件中,然后load-grunt-config将它们捆绑起来并grunt加载并为您使用它们.您还可以创建一个aliases.js文件,将要组合在一起的任务组合成一个任务,一个接一个地运行.它类似于原始中的grunt.registerTask任务Gruntfile.js.我将所有grunt任务放在grunt/根文件夹下的一个单独的文件夹中Gruntfile,没有额外的子文件夹,如load-grunt-configGithub上的README.md所示.这是我的瘦身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']
        }
    });

};
Run Code Online (Sandbox Code Playgroud)

理论上,将所有这些文件设置为正确的load-grunt-config加载方式应该与只有一个文件完全相同Gruntfile.js.但是,我似乎遇到了一些障碍.似乎在env任务下设置的环境变量没有为后续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
            }
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

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: '.'
            }
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

grunt表达任务:

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
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

aliases.jsfile(grunt-load-config组合任务的方式让它们一个接一个地运行):

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

的一部分backend/env/prod.js(环境特定的快车配置中,如果加载的NODE_ENV被设置为"PROD",仿照MEAN.JS):

'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'
};
Run Code Online (Sandbox Code Playgroud)

backend/env/dev.js(环境特定的环境Express配置的一部分dev,如果未设置`NODE_ENV变量或设置为"dev",则加载):

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

部分backend/page.js(我的快速配置页面,也以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;
Run Code Online (Sandbox Code Playgroud)

当我跑步时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
Run Code Online (Sandbox Code Playgroud)

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

如果我刚试过这个grunt server任务,我可以把它用来load-grunt-config并行运行任务而不是一个接一个地或者其他一些错误,但即使我一个接一个地尝试任务,比如跑步grunt env:prod shell:mongod express-server:prod express-keepalive,我也会变得类似(不正确)的结果,所以无论是gruntgrunt env并行运行的任务,以及,还是其他什么东西是怎么回事.

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

Why*_*rrh 7

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

问题

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

// filename: Gruntfile.js
grunt.initConfig({
    foo: {
        a: {
            options: {},
        }
    },
    bar: {
        b: {
            options: {},
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

你可以这样写:

// filename: grunt/foo.js
module.exports = {
    a: {
        options: {},
    }
}

// filename: grunt/bar.js
module.exports = {
    b: {
        options: {},
    }
}

// filename: Gruntfile.js
require('load-grunt-config')(grunt);
Run Code Online (Sandbox Code Playgroud)

基本上,通过这种方式,您可以将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);
};
Run Code Online (Sandbox Code Playgroud)

您可以考虑以上相同的内容:

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']);
};
Run Code Online (Sandbox Code Playgroud)

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

mongod --port 27017
Run Code Online (Sandbox Code Playgroud)

你想要执行的是:

mongo --port dev_mongo_port
Run Code Online (Sandbox Code Playgroud)

问题是,在(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']);
};
Run Code Online (Sandbox Code Playgroud)

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

好的,这对于shell:mongod任务来说一切都很好,但是其他任务呢,例如Express?

您需要远离环境变量(除非您想调用Grunt 之前设置它们.为什么?以此Grunt配置为例:

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']);
};
Run Code Online (Sandbox Code Playgroud)

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']);
    };
    
    Run Code Online (Sandbox Code Playgroud)

    但是你会注意到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']);
    };
    
    Run Code Online (Sandbox Code Playgroud)
  • 传递grunt一个参数来指定它应该使用的配置

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

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