twilio-release-tool
Version:
Twilio Release Tool
290 lines (264 loc) • 8.23 kB
JavaScript
;
var util = require('./util');
/**
* Add a command-line option for a variable.
* @private
* @param {object} program
* @param {string} variable
* @returns {object}
*/
function addCommandLineOptionForVariable(program, variable) {
var commandLineOption = '--' +
variable.toLowerCase()
.replace(/--/g, '-')
.replace(/^-/, '')
.replace(/-$/, '')
.replace(/_/, '-') + ' [value]';
program.option(commandLineOption,
'assign the ' + variable + ' environment variable');
return program;
}
/**
* Add command-line options for variables.
* @private
* @param {object} program
* @param {Set<string>} variables
* @returns {object}
*/
function addCommandLineOptionsForVariables(program, variables) {
variables.forEach(addCommandLineOptionForVariable.bind(null, program));
return program;
}
/**
* Add command-line options for variables unassigned in config plans.
* @param {object} program
* @param {object} config
* @param {?Map<string, *>} [excluding]
* @param {?Set<string>} [unassigned]
* @returns {object}
*/
function addCommandLineOptionsForVariablesUnassignedInConfigPlans(program, config, excluding, unassigned) {
return addCommandLineOptionsForVariables(program,
getVariablesUnassignedInConfigPlans(config, excluding, unassigned));
}
/**
* CamelCase an uppercased, snake_cased variable.
* @private
* @param {string} variable
* @returns {string}
*/
function camelCaseVariable(variable) {
return variable
.toLowerCase()
.split('_')
.filter(function filterEmptyStrings(string) {
return string.length;
}).map(function capitalizeFirstLetter(string, i) {
if (i > 0) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
return string;
}).join('');
}
/**
* Get variables assigned by the program, giving precedence to any values
* already in the Map.
* @private
* @param {object} program
* @param {Set<string>} variableNames
* @param {?Map<string, string>} [variables]
* @returns {Map<string, string>}
*/
function getVariablesAssignedByProgram(program, variableNames, variables) {
variables = variables || new Map();
variableNames.forEach(function forEachVariableName(variableName) {
var camelCased = camelCaseVariable(variableName);
if (camelCased in program && !variables.has(variableName)) {
variables.set(variableName, program[camelCased]);
}
});
return variables;
}
/**
* Get variables assigned in the config env, giving precedence to any values
* already in the Map.
* @private
* @param {object} config
* @param {?Map<string, string>} [variables]
* @returns {Map<string, string>}
*/
function getVariablesAssignedInConfigEnv(config, variables) {
variables = variables || new Map();
if (config.env) {
for (var variable in config.env) {
if (!variables.has(variable)) {
variables.set(variable, config.env[variable]);
}
}
}
return variables;
}
/**
* Get variables assigned in the config plan env, giving precedence to any
* values already in the Map.
* @private
* @param {object} config
* @param {string} planName
* @param {?Map<string, string>} [variables]
* @returns {Map<string, string>}
*/
function getVariablesAssignedInConfigPlanEnv(config, planName, variables) {
if (config.plans) {
var plan = config.plans[planName];
if (plan) {
return getVariablesAssignedInConfigEnv(plan, variables);
}
}
return variables || new Map();
}
/**
* Get the variables assigned in the process env, giving precedence to any
* values already in the Map.
* @private
* @param {object} process
* @param {?Map<string, string>} [variables]
* @returns {Map<string, string>}
*/
function getVariablesAssignedInProcessEnv(process, variables) {
return getVariablesAssignedInConfigEnv(process, variables);
}
/**
* Get variables for a config plan.
* @param {object} program
* @param {object} config
* @param {string} planName
* @param {?Map<string, string>} [constants]
* @param {?object} [process] - pass null to ignore the process env
* @returns {{assigned: Map<string, string>, unassigned: Set<string>}}
*/
function getVariablesForConfigPlan(program, config, planName, constants, process) {
var required = getVariablesRequiredByConfigPlan(config, planName);
var assigned = [
getVariablesAssignedInConfigPlanEnv.bind(null, config, planName),
getVariablesAssignedInConfigEnv.bind(null, config),
getVariablesAssignedByProgram.bind(null, program, required),
].reduce(function assignVariables(variables, assign) {
return assign(variables);
}, constants);
if (process) {
assigned = getVariablesAssignedInProcessEnv(process, assigned);
}
var unassigned = new Set();
required.forEach(function forEachRequiredVariable(variable) {
if (!assigned.has(variable)) {
unassigned.add(variable);
}
});
return { assigned: assigned, unassigned: unassigned };
}
/**
* Get variables required by a command.
* @private
* @param {string} command
* @param {?Set<string>} [required]
* @returns {Set<string>}
*/
function getVariablesRequiredByCommand(command, required) {
required = required || new Set();
var matches = command.replace(/\\$/g, '\\') // Remove escaped "$"
.match(/\$({[a-zA-Z0-9_]+}|[a-zA-Z0-9_]+)/g);
if (matches) {
matches.forEach(function forEachMatch(match) {
var variable = match.match(/^\${?([a-zA-Z0-9_]+)}?$/)[1];
required.add(variable);
});
}
return required;
}
/**
* Get variables required by commands.
* @private
* @param {Array<string>} commands
* @param {?Set<string>} [required]
* @returns {Set<string>}
*/
function getVariablesRequiredByCommands(commands, required) {
return commands.reduce(util.flip2(getVariablesRequiredByCommand), required);
}
/**
* Get variables required by a config plan.
* @private
* @param {object} config
* @param {string} planName
* @param {?Set<string>} [required]
* @returns {Set<string>}
*/
function getVariablesRequiredByConfigPlan(config, planName, required) {
if (config.plans) {
var plan = config.plans[planName];
if (plan && plan.commands instanceof Array) {
return getVariablesRequiredByCommands(plan.commands, required);
}
}
return required || new Set();
}
/**
* Get variables required by a config plans.
* @private
* @param {object} config
* @param {?Set<string>} [required]
* @returns {Set<string>}
*/
function getVariablesRequiredByConfigPlans(config, required) {
if (config.plans) {
for (var planName in config.plans) {
required = getVariablesRequiredByConfigPlan(config, planName, required);
}
}
return required || new Set();
}
/**
* Get variables unassigned in a config plan.
* @private
* @param {object} config
* @param {string} planName
* @params {?Map<string, *>} [excluding]
* @param {?Set<string>} [unassigned]
* @returns {Set<string>}
*/
function getVariablesUnassignedInConfigPlan(config, planName, excluding, unassigned) {
unassigned = unassigned || new Set();
var required = getVariablesRequiredByConfigPlan(config, planName);
var assigned = [
getVariablesAssignedInConfigPlanEnv.bind(null, config, planName),
getVariablesAssignedInConfigEnv.bind(null, config)
].reduce(function assignVariables(variables, assign) {
return assign(variables);
}, excluding);
required.forEach(function forEachRequiredVariable(variable) {
if (!assigned.has(variable)) {
unassigned.add(variable);
}
});
return unassigned;
}
/**
* Get variables unassigned in config plans.
* @private
* @param {object} config
* @param {?Map<string, *>} [excluding]
* @param {?Set<string>} [unassigned]
* @returns {Set<string>}
*/
function getVariablesUnassignedInConfigPlans(config, excluding, unassigned) {
if (config.plans) {
for (var planName in config.plans) {
unassigned = getVariablesUnassignedInConfigPlan(config, planName,
excluding, unassigned);
}
}
return unassigned || new Set();
}
module.exports.addCommandLineOptionsForVariablesUnassignedInConfigPlans =
addCommandLineOptionsForVariablesUnassignedInConfigPlans;
module.exports.getVariablesForConfigPlan = getVariablesForConfigPlan;