@lando/acquia
Version:
A Lando plugin that provides a tight integration with Acquia.
198 lines (183 loc) • 7.26 kB
JavaScript
;
/**
* @file
* Provides utility functions for the Lando Acquia plugin.
* This includes functions for reading/writing Acquia CLI configuration,
* managing and formatting API keys, finding suitable environments,
* and constructing commands for Drush phar installation.
*/
// Modules
const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
/**
* Reads the Acquia CLI YAML configuration file (default: .acquia-cli.yml)
* and returns the `cloud_app_uuid`.
*
* @param {string} [file] Path to the Acquia CLI config file.
* @return {string|null} The cloud_app_uuid if found, otherwise null.
*/
exports.getAcliUuid = (file = '.acquia-cli.yml') => {
if (fs.existsSync(file)) {
const data = yaml.load(fs.readFileSync(file, 'utf8'));
return data.cloud_app_uuid;
}
return null;
};
/**
* Writes the `cloud_app_uuid` to the Acquia CLI YAML configuration file.
* Only writes the file if it does not already exist.
*
* @param {string} uuid The cloud_app_uuid to write.
* @param {string} [file] Path to the Acquia CLI config file.
* @return {boolean} True if the file was written, false otherwise (e.g., if it already existed).
*/
exports.writeAcliUuid = (uuid, file = '.acquia-cli.yml') => {
if (!fs.existsSync(file)) {
fs.writeFileSync(file, `cloud_app_uuid: ${uuid}\n`);
return true;
}
return false;
};
/**
* Retrieves Acquia keys and formats them for use as Inquirer choices.
* Adds an "add a key" option to the list.
*
* @param {object} lando The Lando instance.
* @return {Array<object>} An array of Inquirer choice objects, each with `name` and `value` properties.
*/
exports.getAcquiaKeyChoices = lando => {
const keys = exports.getAcquiaKeys(lando);
return _(keys)
.map(key => ({name: key.label, value: key.uuid, secret: key.secret}))
.thru(keys => keys.concat([{name: 'add a key', value: 'more'}]))
.value();
};
/**
* Reads the Acquia cloud API configuration from the appserver container
* (specifically from `/var/www/.acquia/cloud_api.conf` mapped to the host)
* and returns the currently active key object.
*
* @param {object} lando The Lando instance.
* @param {object} appConfig The Lando application configuration object.
* @return {object|null} The active key object if found, otherwise null.
*/
exports.getAcquiaKeyFromApp = (lando, appConfig) => {
const file = path.join(lando.config.home, '.lando', 'config', appConfig.name, '.acquia', 'cloud_api.conf');
if (fs.existsSync(file)) {
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
const activeKey = data.acli_key;
return _.find(data.keys, key => key.uuid === activeKey);
}
return null;
};
/**
* Selects the "best" environment from a list of environments.
* Prefers an environment named 'dev'. If not found, returns the first environment in the list.
*
* @param {Array<object>} [envs] An array of environment objects. Each object should have a `name` property.
* @return {object | undefined} The best environment object, or undefined if the input array is empty.
*/
exports.getBestEnv = (envs = []) => {
// Try to get the dev environment
const dev = _.find(envs, env => env.name === 'dev');
// Return dev environment if we have it otherwise just use the first one
return (dev) ? dev : _.first(envs);
};
/**
* Reads and parses the `composer.json` file from the current working directory.
*
* @return {object|null} The parsed composer.json object if the file exists, otherwise null.
*/
exports.getComposerConfig = () => {
const file = path.join('composer.json');
if (fs.existsSync(file)) {
return JSON.parse(fs.readFileSync(file, 'utf8'));
}
return null;
};
/**
* Retrieves Acquia API keys stored on the host machine from the `~/.acquia/cloud_api.conf` file.
*
* @param {string} home The user's home directory path.
* @return {Array<object>} An array of key objects found in the configuration file. Each object is augmented with a `key` property (its original key in the config object).
*/
exports.getHostKeys = home => {
// Path to acli conf
const file = path.join(home, '.acquia', 'cloud_api.conf');
// If no keyfile return empty
if (!fs.existsSync(file)) return [];
// Otherwise lets try to get some data
const keys = _.get(JSON.parse(fs.readFileSync(file, 'utf8')), 'keys', []);
// Loop through keys to merge in the key of the keys
_.forEach(keys, (value, key) => value.key = key);
// Return an array of keys
return _.map(keys);
};
/**
* Formats an array of key objects into Inquirer choices, suitable for prompting the user.
* Combines `key.key` and `key.secret` into a single `value` string for the choice.
* Adds an "add or refresh a key" option.
*
* @param {Array<object>} [keys] An array of key objects. Each should have `label`, `key`, and `secret` properties.
* @return {Array<object>} An array of Inquirer choice objects.
*/
exports.getKeys = (keys = []) => _(keys)
.map(key => ({name: key.label, value: `${key.key}:${key.secret}`}))
.thru(keys => keys.concat([{name: 'add or refresh a key', value: 'more'}]))
.value();
/**
* Merges multiple arrays of key objects, ensuring uniqueness by `uuid`,
* and then sorts them by `label`.
*
* @param {...Array<object>} sources One or more arrays of key objects.
* @return {Array<object>} A single array of unique, sorted key objects.
*/
exports.sortKeys = (...sources) => _(_.flattenDeep(sources))
.uniqBy('uuid')
.orderBy('label')
.value();
/**
* Constructs a shell command string to download and install a specific version of Drush phar.
*
* @param {string} version The Drush version to download (e.g., '10.x', '11.x').
* @param {string|boolean} status A command or condition to check after installation (e.g., 'drush status').
* @return {string} A shell command string for installing Drush.
*/
exports.getDrush = (version, status) => exports.getPhar(
getDrushUrl(version),
'/tmp/drush.phar',
'/usr/local/bin/drush',
status,
);
/**
* Constructs a shell command string to download a file (typically a .phar) and move it to a destination.
* Optionally includes a check command to run after the move.
*
* @param {string} url The URL to download the file from.
* @param {string} src The temporary path to save the downloaded file.
* @param {string} dest The final destination path for the file.
* @param {string|Array<string>} [check] A command or array of commands to run as a check after installation.
* @return {string} A combined shell command string.
*/
exports.getPhar = (url, src, dest, check = 'true') => {
// Arrayify the check if needed
if (_.isString(check)) check = [check];
// Phar install command
const pharInstall = [
['curl', url, '-LsS', '-o', src],
['chmod', '+x', src],
['mv', src, dest],
check,
];
// Return
return _.map(pharInstall, cmd => cmd.join(' ')).join(' && ');
};
/**
* Constructs the download URL for a specific Drush phar version from GitHub releases.
*
* @param {string} version The Drush version (e.g., '10.6.2', '11.0.0').
* @return {string} The full URL to the Drush phar file.
*/
const getDrushUrl = version => `https://github.com/drush-ops/drush/releases/download/${version}/drush.phar`;