Переменные окружения Grunt не устанавливаются до тех пор, пока не будут загружены все задачи



Я использую модули npm grunt env и load-grunt-config в своем проекте. grunt env обрабатывает переменные окружения для вас, в то время как load-grunt-config обрабатывает, ну, загружает конфигурацию grunt для вас. Вы можете поместить свои задачи в другие файлы, затем load-grunt-config соберет их и будет grunt загружать и потреблять их для вас. Вы также можете создать файл aliases.js, в котором задачи, которые вы хотите объединить в одну задачу, будут выполняться одна за другой. Это похоже на ворчание.registerTask задача в оригинале Gruntfile.js. Я помещаю все мои задачи grunt в отдельную папку grunt/ под корневой папкой с главной Gruntfile, без дополнительных вложенных папок, как это предлагает 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 (специфичная для среды Экспресс-конфигурация, загружается, если 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, загружается, если переменная 'NODE_ENV не установлена или установлена в "dev"):

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

Часть 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;

Когда я бегу 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, я получаю аналогичные (неправильные) результаты, поэтому либо grunt, либо grunt env запускают задачи параллельно, либо происходит что-то еще.

, Что здесь происходит? Почему переменные среды не установлены правильно для последующих задач grunt? Когда они в конце концов будут установлены, и почему именно тогда, а не в какой-то другой раз? Как я могу заставить их получить набор для самих задач grunt, а не после, предполагая, что даже есть путь?

194   2  

2 ответов:

Решение довольно очевидно, как только вы его поймете, поэтому давайте начнем с самого начала:

Проблема

Вы используете 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);

В принципе, таким образом вы можете разделить конфигурацию 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, но что о других задачах, например, экспресс?

Вам нужно будет отойти от переменных окружения (если вы не хотите настроить их перед вызовом 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.dev и Gruntfile.js.prod) и переименовывать их по мере необходимости
  • прочитайте конфигурационный файл разработки (например, grunt.file.readJSON('config.development.json')), если он существует и падает вернуться к файлу производственной конфигурации, если он не существует
  • какой-то лучший способ, не указанный здесь
Но все вышеперечисленное должно привести к одному и тому же конечному результату.

Это, кажется, суть того, что вы пытаетесь сделать, и это работает для меня. Важной частью было то, что я упомянул в своем комментарии-связывание задачи среды перед запуском других задач.

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.
    Ничего не найдено.

Добавить ответ:
Отменить.