Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions src/interactive.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ try {
}

/**
* Launches the interactive Lagoon CLI wrapper, allowing users to manage projects and environments through a guided command-line interface.
* Starts the interactive Lagoon CLI session for managing projects and environments.
*
* Presents menus for selecting Lagoon instances and projects, and provides options to list environments or users, delete environments, generate login links, clear Drupal cache, configure SSH keys, and change selections. Handles errors gracefully and logs major actions throughout the session.
* Guides the user through selecting a Lagoon instance and project, then presents a menu of actions such as listing environments or users, deleting environments, generating login links, clearing Drupal cache, deploying branches, configuring SSH keys, and changing selections. Handles errors gracefully and logs key actions throughout the session.
*/
export async function startInteractiveMode() {
console.log(chalk.green('Welcome to the Lagoon CLI Wrapper!'));
Expand Down Expand Up @@ -143,6 +143,12 @@ async function selectLagoonInstance() {
return instance;
}

/**
* Prompts the user to select a project from the specified Lagoon instance and returns the selected project's name and details.
*
* @param {string} instance - The Lagoon instance from which to load projects.
* @returns {{ projectName: string, projectDetails: object }} An object containing the selected project's name and its details.
*/
async function selectProjectWithDetails(instance) {
const spinner = ora(`Loading projects for ${instance}...`).start();
const projectsWithDetails = await getProjectsWithDetails(instance);
Expand Down Expand Up @@ -171,11 +177,11 @@ async function selectProjectWithDetails(instance) {
}

/**
* Displays the main menu for the interactive CLI and prompts the user to select an action.
* Presents the main menu for the interactive CLI session and returns the user's selected action.
*
* @param {string} instance - The name of the currently selected Lagoon instance.
* @param {string} project - The name of the currently selected project.
* @returns {Promise<string>} The action selected by the user.
* @param {string} instance - The currently selected Lagoon instance.
* @param {string} project - The currently selected project.
* @returns {Promise<string>} The action chosen by the user from the menu.
*/
async function showMainMenu(instance, project) {
console.log(chalk.blue(`\nCurrent Instance: ${chalk.bold(instance)}`));
Expand Down Expand Up @@ -409,6 +415,11 @@ async function generateLoginLinkFlow(instance, project, githubBaseUrl) {
]);
}

/**
* Guides the user through clearing the Drupal cache for a selected environment in a Lagoon project.
*
* Prompts the user to choose an environment, attempts to clear its Drupal cache, and displays the result or any errors encountered.
*/
async function clearCacheFlow(instance, project, githubBaseUrl) {
const spinner = ora(`Loading environments for ${project}...`).start();
const allEnvironments = await getEnvironments(instance, project);
Expand Down Expand Up @@ -467,6 +478,15 @@ async function clearCacheFlow(instance, project, githubBaseUrl) {
]);
}

/**
* Guides the user through deploying a selected Git branch for a Lagoon project via the interactive CLI.
*
* Prompts the user to select a branch from the project's Git repository, confirms deployment, and initiates the deployment process. Provides feedback on success or failure and informs the user that deployment is asynchronous.
*
* @param {string} instance - The Lagoon instance identifier.
* @param {string} project - The Lagoon project name.
* @param {object} projectDetails - Details of the Lagoon project, including the Git URL.
*/
async function deployBranchFlow(instance, project, projectDetails) {
// Check if project has a git URL
if (!projectDetails || !projectDetails.giturl) {
Expand Down
72 changes: 61 additions & 11 deletions src/lagoon-api.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,17 @@ export async function getEnvironments(instance, project) {
}
}

// Get all users for a project
/**
* Retrieves the list of usernames associated with a Lagoon project.
*
* Executes the Lagoon CLI to list all users for the specified project and parses the output to extract usernames.
*
* @param {string} instance - The Lagoon instance name.
* @param {string} project - The project name.
* @returns {Promise<string[]>} An array of usernames for the project.
*
* @throws {Error} If the command fails or output cannot be parsed.
*/
export async function getUsers(instance, project) {
try {
const command = `lagoon -l ${instance} -p ${project} list all-users`;
Expand All @@ -117,11 +127,11 @@ export async function getUsers(instance, project) {
/**
* Deletes a Lagoon environment unless it is protected.
*
* Throws an error if the environment is protected (such as 'production', 'master', 'develop', or names starting with 'project/'). Executes the Lagoon CLI to delete the specified environment and returns true if successful.
* Prevents deletion of protected environments such as 'production', 'master', 'develop', or any environment whose name starts with 'project/'. Executes the Lagoon CLI to delete the specified environment and returns true if the operation succeeds.
*
* @param {string} instance - The Lagoon instance name.
* @param {string} project - The project name.
* @param {string} environment - The environment name to delete.
* @param {string} instance - Name of the Lagoon instance.
* @param {string} project - Name of the project.
* @param {string} environment - Name of the environment to delete.
* @returns {boolean} True if the environment was deleted successfully.
*
* @throws {Error} If the environment is protected or if the deletion fails.
Expand Down Expand Up @@ -153,7 +163,18 @@ export async function deleteEnvironment(instance, project, environment) {
}
}

// Generate login link for an environment
/**
* Generates a one-time login link for a specified Lagoon environment using Drush.
*
* Throws an error if the environment is protected (e.g., 'production' or 'master').
*
* @param {string} instance - The Lagoon instance name.
* @param {string} project - The Lagoon project name.
* @param {string} environment - The environment for which to generate the login link.
* @returns {string} The generated one-time login URL.
*
* @throws {Error} If the environment is protected or if the login link generation fails.
*/
export async function generateLoginLink(instance, project, environment) {
// Check if environment is protected
if (environment === 'production' || environment === 'master') {
Expand All @@ -170,10 +191,12 @@ export async function generateLoginLink(instance, project, environment) {
}

/**
* Converts a Git SSH or HTTPS URL to a standard GitHub HTTPS URL without the `.git` suffix.
* Converts a GitHub SSH or HTTPS repository URL to a standard HTTPS URL without the `.git` suffix.
*
* Returns `null` if the input is not a GitHub URL.
*
* @param {string} gitUrl - The Git repository URL to convert.
* @returns {string|null} The corresponding GitHub HTTPS URL, or {@code null} if the input is not a GitHub URL.
* @returns {string|null} The normalized GitHub HTTPS URL, or `null` if the input is not a GitHub URL.
*/
export function gitUrlToGithubUrl(gitUrl) {
// Handle SSH URLs like git@github.com:org/repo.git
Expand All @@ -193,7 +216,16 @@ export function extractPrNumber(environmentName) {
return match ? match[1] : null;
}

// Clear Drupal cache for an environment
/**
* Clears the Drupal cache for a specified environment using Lagoon CLI and Drush.
*
* @param {string} instance - The Lagoon instance name.
* @param {string} project - The project name within the Lagoon instance.
* @param {string} environment - The environment name to clear the cache for.
* @returns {string} The trimmed output from the Drush cache clear command.
*
* @throws {Error} If the cache clearing operation fails for the specified environment.
*/
export async function clearDrupalCache(instance, project, environment) {
try {
const command = `lagoon ssh -l ${instance} -p ${project} -e ${environment} -C "drush cr"`;
Expand All @@ -206,7 +238,16 @@ export async function clearDrupalCache(instance, project, environment) {

// No helper functions needed for the more efficient git ls-remote approach

// Get all branches from a git repository using ls-remote (much more efficient)
/**
* Retrieves all branch names from a remote Git repository.
*
* Executes `git ls-remote --heads` on the provided repository URL and parses the output to return an array of branch names.
*
* @param {string} gitUrl - The URL of the Git repository.
* @returns {Promise<string[]>} An array of branch names found in the repository.
*
* @throws {Error} If the Git URL is missing, invalid, authentication fails, the repository is not found, or another error occurs during command execution.
*/
export async function getGitBranches(gitUrl) {
try {
if (!gitUrl) {
Expand Down Expand Up @@ -257,7 +298,16 @@ export async function getGitBranches(gitUrl) {
}
}

// Deploy a branch to Lagoon
/**
* Deploys a specified branch to a Lagoon project environment.
*
* Validates the branch name for allowed characters and escapes special shell characters to prevent command injection. Executes the Lagoon CLI to initiate deployment and parses the JSON response.
*
* @param {string} branch - The name of the branch to deploy. Must contain only alphanumeric characters, slashes, underscores, hyphens, and periods.
* @returns {{ success: true, message: string }} An object indicating successful initiation of the deployment and a descriptive message.
*
* @throws {Error} If the branch name is invalid, the Lagoon CLI command fails, or the deployment is unsuccessful.
*/
export async function deployBranch(instance, project, branch) {
try {
// Validate branch name to prevent command injection - allow slashes in addition to other safe characters
Expand Down
21 changes: 13 additions & 8 deletions src/logger.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ if (!fs.existsSync(LOG_DIR)) {
}

/**
* Get the current log file name based on the date
* @returns {string} The log file path
* Returns the file path for the current day's log file in the `.logs` directory.
*
* The log file is named using the format `log--YYYYMMDD.txt`, where `YYYYMMDD` is the current date.
*
* @returns {string} Absolute path to the log file for today.
*/
function getLogFilePath() {
const now = new Date();
Expand All @@ -22,11 +25,13 @@ function getLogFilePath() {
}

/**
* Appends an action entry with timestamp, action, command, and optional result to the current day's log file.
* Appends a timestamped action entry to the current day's log file.
*
* The log entry includes the action description, executed CLI command, and an optional result string.
*
* @param {string} action - Description of the action performed.
* @param {string} command - The CLI command executed.
* @param {string} [result] - Optional result of the command.
* @param {string} command - The CLI command that was executed.
* @param {string} [result] - Optional result or output of the command.
*/
export function logAction(action, command, result = null) {
const timestamp = new Date().toISOString(); // ISO8601 timestamp
Expand All @@ -38,11 +43,11 @@ export function logAction(action, command, result = null) {
}

/**
* Appends an error entry to the current day's log file with a timestamp, action, command, and error message.
* Logs an error entry with a timestamp, action description, command, and error message to the current day's log file.
*
* @param {string} action - Description of the action being performed.
* @param {string} action - Description of the action during which the error occurred.
* @param {string} command - The CLI command that was executed.
* @param {Error} error - The error encountered during execution.
* @param {Error} error - The error encountered.
*/
export function logError(action, command, error) {
const timestamp = new Date().toISOString(); // ISO8601 timestamp
Expand Down