diff --git a/lib/API/schema.json b/lib/API/schema.json index 7debd78d0..e2042a92e 100644 --- a/lib/API/schema.json +++ b/lib/API/schema.json @@ -204,6 +204,7 @@ "array", "number" ], + "alias": ["stopExitCodes"], "docDescription": "List of exit codes that should allow the process to stop (skip autorestart)." }, "watch_delay": { diff --git a/lib/God/ForkMode.js b/lib/God/ForkMode.js index 8d0c1fbc0..655e5e5de 100644 --- a/lib/God/ForkMode.js +++ b/lib/God/ForkMode.js @@ -24,6 +24,32 @@ var cst = require('../../constants.js'); * @param {} God * @return */ +/** + * Sanitize pm2_env object for use as child process environment variables. + * Filters out non-primitive values (objects, arrays, functions) that would + * be stringified to "[object Object]" or similar when passed as env vars. + * @method sanitizeProcessEnv + * @param {Object} pm2_env - The PM2 environment object + * @return {Object} A new object containing only string-safe environment variables + */ +function sanitizeProcessEnv(pm2_env) { + var sanitized = {}; + + Object.keys(pm2_env).forEach(function(key) { + var value = pm2_env[key]; + if (value === null || value === undefined) { + return; + } + // Only pass primitive types that can be safely represented as strings + var type = typeof value; + if (type === 'string' || type === 'number' || type === 'boolean') { + sanitized[key] = String(value); + } + }); + + return sanitized; +} + module.exports = function ForkMode(God) { /** * For all apps - FORK MODE @@ -94,7 +120,7 @@ module.exports = function ForkMode(God) { try { var options = { - env : pm2_env, + env : sanitizeProcessEnv(pm2_env), detached : true, cwd : pm2_env.pm_cwd || process.cwd(), stdio : ['pipe', 'pipe', 'pipe', 'ipc'] //Same as fork() in node core diff --git a/lib/tools/Config.js b/lib/tools/Config.js index a3da91c4c..37cc68ef6 100644 --- a/lib/tools/Config.js +++ b/lib/tools/Config.js @@ -58,17 +58,56 @@ var Config = module.exports = { /** * Filter / Alias options */ +/** + * Convert a snake_case string to camelCase. + * @method snakeToCamel + * @param {String} str + * @return {String} + */ +function snakeToCamel(str) { + return str.replace(/_([a-z])/g, function(match, letter) { + return letter.toUpperCase(); + }); +} + +/** + * Filter commander options and map them to schema keys. + * Supports explicit aliases defined in the schema as well as + * automatic camelCase matching (e.g. commander's --stop-exit-codes + * becomes stopExitCodes which maps to schema key stop_exit_codes). + * @method filterOptions + * @param {Object} cmd - Commander options object + * @return {Object} Filtered configuration object with schema keys + */ Config.filterOptions = function(cmd) { var conf = {}; var schema = this.schema; for (var key in schema) { + // Check explicit aliases defined in schema var aliases = schema[key].alias; - aliases && aliases.forEach(function(alias){ - if (typeof(cmd[alias]) !== 'undefined') { - conf[key] || (conf[key] = cmd[alias]); + if (aliases) { + if (!Array.isArray(aliases)) { + aliases = [aliases]; } - }); + aliases.forEach(function(alias){ + if (typeof(cmd[alias]) !== 'undefined') { + conf[key] || (conf[key] = cmd[alias]); + } + }); + } + + // Check camelCase version of the schema key + // (commander.js converts --stop-exit-codes to stopExitCodes) + var camelKey = snakeToCamel(key); + if (camelKey !== key && typeof(cmd[camelKey]) !== 'undefined') { + conf[key] || (conf[key] = cmd[camelKey]); + } + + // Check if the schema key itself exists directly on cmd + if (typeof(cmd[key]) !== 'undefined') { + conf[key] || (conf[key] = cmd[key]); + } } return conf;