@lando/acquia
Version:
A Lando plugin that provides a tight integration with Acquia.
168 lines (157 loc) • 5.94 kB
JavaScript
;
/**
* @file
* This file defines the `lando push` command integration for Acquia.
* It handles fetching available Acquia environments (excluding 'prod' for safety),
* constructing interactive prompts for selecting which components (code, database, files)
* to push and to which environment, and then executing the push operation via the `acquia-push.sh` script.
*/
// Modules
const _ = require('lodash');
const auth = require('./auth');
const API = require('./api');
const {getBestEnv} = require('./utils');
// Acquia
const api = new API();
/** @type {Array<object>} Caches the Acquia environments to avoid redundant API calls. */
let acquiaEnvs = [];
/**
* Fetches Acquia environments for a given application UUID, using provided credentials.
* Filters out the 'prod' environment. Results are cached in the `acquiaEnvs` variable
* for subsequent calls within the same Lando operation. Adds a 'none' option.
*
* @param {string} key The Acquia API client ID.
* @param {string} secret The Acquia API client secret.
* @param {string} uuid The Acquia application UUID.
* @return {Promise<Array<object>>} A promise that resolves to an array of environment objects
* (excluding 'prod'), each with `name` (for display) and `value` (the environment ID), plus a 'none' option.
*/
const getEnvs = (key, secret, uuid) => {
// If we already have it, return it
if (!_.isEmpty(acquiaEnvs)) return acquiaEnvs;
// Otherwise fetch them
return api.auth(key, secret, true, true)
.then(() => api.getEnvironments(uuid))
.then(envs => _(envs)
.filter(env => (env.name !== 'prod'))
.map(env => _.merge({}, env, {name: env.displayName, value: env.name}))
.value(),
)
.then(envs => {
acquiaEnvs = envs;
acquiaEnvs.push({'name': 'none', 'value': 'none'});
return envs;
});
};
/**
* Base configuration object for the `lando push` command specific to Acquia.
* Defines the service to run on, description, command script, execution level,
* I/O handling, and static command-line options.
*/
const task = {
service: 'appserver',
description: 'Push code, database and/or files to Acquia',
cmd: '/helpers/acquia-push.sh',
level: 'app',
stdio: ['inherit', 'pipe', 'pipe'],
options: {
key: {
describe: 'An Acquia API Client ID',
passthrough: true,
string: true,
interactive: {
type: 'list',
message: 'Choose an Acquia API Client ID',
choices: [],
when: () => false,
weight: 100,
},
},
secret: {
describe: 'An Acquia API Client Secret',
passthrough: true,
password: true,
},
code: {
description: 'The environment to which the local code will be pushed',
passthrough: true,
alias: ['c'],
interactive: {
type: 'list',
message: 'Push code to?',
weight: 200,
},
},
database: {
description: 'The environment to which the local database will be pushed',
passthrough: true,
alias: ['d'],
interactive: {
type: 'list',
message: 'Push database to?',
weight: 300,
},
},
files: {
description: 'The environment to which local files will be pushed',
passthrough: true,
alias: ['f'],
interactive: {
type: 'list',
message: 'Push files to?',
weight: 400,
},
},
},
};
/**
* Dynamically populates the interactive choices and default values for the `lando push` command.
* It fetches Acquia environments (excluding 'prod') and sets them as choices.
* Defaults to 'none' for database and files, and the best available non-prod environment (e.g., 'dev') for code.
*
* @param {object} task The base task configuration object.
* @param {object} options An object containing necessary details like `key`, `secret`, and `appUuid`.
* @return {object} The modified task configuration object with dynamic interactive options and environment variables set.
*/
const getDefaults = (task, options) => {
// Set interactive options
const {key, secret, appUuid} = options;
_.forEach(['code', 'database', 'files'], name => {
task.options[name].interactive.choices = answers => {
// Break up auth into parts
const authParts = answers['key'].split(':');
// If we have two parts then we need to separate, otherwise we assume
// secret and key were passed in separately
if (authParts.length === 2) {
answers['key'] = authParts[0];
answers['secret'] = authParts[1];
}
// Use the inputed creds, otherwise fallback
const bestKey = _.get(answers, 'key', key);
const bestSecret = _.get(answers, 'secret', secret);
// Get ENVS
return getEnvs(bestKey, bestSecret, appUuid);
};
// None seems like the safest default for push
task.options[name].interactive.default = 'none';
});
// Override the default so code puses to dev
task.options.code.interactive.default = getBestEnv(acquiaEnvs);
// Set the task env
task.env = {LANDO_DB_USER_TABLE: 'users'};
// Return
return task;
};
/**
* Constructs the complete configuration for the `lando push` command for Acquia.
* It merges the base task configuration, dynamically populated defaults (including environment choices
* and specific defaults for push), and authentication options (interactive or non-interactive).
*
* @param {object} options An object containing necessary details: `key`, `secret`, `account` (label for key), and `appUuid`.
* @param {Array<object>} [keys] An array of existing saved key objects for interactive auth prompts.
* @return {object} The complete command configuration object for `lando push`.
*/
exports.getAcquiaPush = (options, keys = []) => {
const {key, secret, account} = options;
return _.merge({}, getDefaults(task, options), {options: auth.getAuthOptions(key, secret, account, keys)});
};