草庐IT

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

coder 2025-02-17 原文

我正在使用 npm 模块 grunt envload-grunt-config在我的项目中。 grunt env 为您处理环境变量,而 load-grunt-config 处理,嗯,为您加载 grunt 配置。您可以将您的任务放入其他文件中,然后 load-grunt-config 会将它们打包并让 grunt 为您加载和使用它们。您还可以创建一个 aliases.js 文件,其中包含您希望将任务组合在一起的任务,一个接一个地运行。它类似于 grunt.registerTask原始 Gruntfile.js 中的任务。我将所有 grunt 任务放在根文件夹下的一个单独的 grunt/ 文件夹中,其中包含主要的 Gruntfile,没有额外的子文件夹,如Github 上的 load-grunt-config 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']
        }
    });

};

从理论上讲,为 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
            }
        }
    }
};

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: '.'
            }
        }
    }
};

grunt express任务:

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 的一部分(特定于环境的 Express 配置,如果 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'
};

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

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

部分backend/page.js(我的Express配置页面,也是仿照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。有许多其他类似的示例,其中未设置环境变量,因此我的应用程序使用默认值。但是,请注意 session 尝试连接到端口 27018 上的数据库(但失败了),因此 MONGO_PORT 最终会被设置。

如果我刚刚尝试了 grunt server 任务,我可以将其归结为 load-grunt-config 并行运行任务,而不是一个接一个地运行任务,或者一些其他错误,但即使我一个接一个地尝试任务,例如运行 grunt env:prod shell:mongod express-server:prod express-keepalive,我也会得到类似(不正确)的结果,所以 gruntgrunt env 也并行运行任务,或者其他事情正在发生。

这是怎么回事?为什么没有为以后的 grunt 任务正确设置环境变量?他们最终什么时候设置,为什么然后而不是其他时间?我怎样才能让他们为 grunt 任务本身而不是之后设置,假设有办法?

最佳答案

一旦你想通了,解决方案就很明显了,所以让我们从头开始:

问题

您正在使用 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)将查找您的环境变量会在它之前的任务中设置(即 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']);
};

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']);
    };
    
  • grunt 传递一个参数以指定它应该使用的配置

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

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

关于javascript - 在加载所有任务之前不会设置 Grunt 环境变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25332482/

有关javascript - 在加载所有任务之前不会设置 Grunt 环境变量的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  4. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  5. ruby - Highline 询问方法不会使用同一行 - 2

    设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

  6. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  7. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  8. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

  9. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  10. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

随机推荐