@nu-art/commando
Version:
Shell command execution framework with interactive sessions, CLI parameter resolution, and plugin system for building and executing shell scripts programmatically
157 lines (156 loc) • 6.1 kB
JavaScript
/*
* commando provides shell command execution framework with interactive sessions and plugin system
*
* Copyright (C) 2020 Adam van der Kruk aka TacB0sS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as fs from 'fs';
import { promises as _fs } from 'fs';
import * as path from 'path';
import { Logger, LogLevel } from '@nu-art/ts-common';
const CONST__FILE_NVMRC = '.nvmrc';
/**
* NVM (Node Version Manager) service for managing Node.js installations.
*
* Handles installation, version management, and configuration of NVM.
* Works with Commando_NVM plugin to execute NVM commands.
*
* **Features**:
* - Install/uninstall NVM
* - Configure shell RC files (.bashrc, .zshrc)
* - Version management
* - Integration with .nvmrc files
*/
export class Cli_NVM extends Logger {
_expectedVersion = '0.39.7';
_homeEnvVar = '$NVM_DIR';
constructor() {
super();
this.setMinLevel(LogLevel.Verbose);
}
get homeEnvVar() {
return this._homeEnvVar;
}
set homeEnvVar(value) {
this._homeEnvVar = value;
}
get expectedVersion() {
return this._expectedVersion;
}
set expectedVersion(value) {
this._expectedVersion = value;
}
/**
* Installs NVM and configures shell RC files.
*
* **Behavior**:
* - Checks if NVM is already installed with expected version
* - Uninstalls if version mismatch
* - Installs NVM via Commando_NVM
* - Configures .bashrc with NVM initialization (if not already present)
*
*
* @param commando - Commando_NVM instance to use for installation
* @returns This instance for method chaining
*/
install = async (commando) => {
if (this.isInstalled()) {
const version = (await commando.getVersion()).trim();
if (this._expectedVersion === version)
return;
await this.uninstall();
}
await commando.install(this._expectedVersion);
const rcFile = path.resolve(process.env['HOME'], '.bashrc');
let rcFileContent = '';
if (fs.existsSync(rcFile)) {
rcFileContent = await _fs.readFile(rcFile, { encoding: 'utf8' });
// If NVM is already configured, don't add it again
if (rcFileContent.includes('NVM_DIR'))
return this;
}
// Append NVM initialization to the end of the file
rcFileContent = `${rcFileContent}${rcFileContent.endsWith('\n') ? '' : '\n'}`;
rcFileContent += `# generated NVM - start\n`;
rcFileContent += `export NVM_DIR="$HOME/.nvm"\n`;
rcFileContent += `[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm\n`;
rcFileContent += `[ -s "$NVM_DIR/bash_completion" ] && \\. "$NVM_DIR/bash_completion" # This loads nvm bash_completion\n`;
rcFileContent += `# generated NVM - end\n`;
await _fs.writeFile(rcFile, rcFileContent, { encoding: 'utf8' });
return this;
};
/**
* Checks if NVM is installed.
*
* Verifies both environment variable and directory existence.
*
* @returns True if NVM is installed
*/
isInstalled = () => !!process.env[this._homeEnvVar] && fs.existsSync(process.env[this._homeEnvVar]);
/**
* Reads the required Node.js version from .nvmrc file.
*
* @returns Promise resolving to version string from .nvmrc
* @throws Error if .nvmrc file doesn't exist
*/
getRequiredNode_Version = async () => {
const absolutePathToNvmrcFile = path.resolve(CONST__FILE_NVMRC);
if (!fs.existsSync(absolutePathToNvmrcFile))
throw new Error(`couldn't find .nvmrc file at: ${absolutePathToNvmrcFile}`);
const content = await _fs.readFile(absolutePathToNvmrcFile, { encoding: 'utf-8' });
return content.trim();
};
/**
* Installs required Node.js version if not already installed.
*
* Reads .nvmrc, checks installed versions, and installs if missing.
*
* @param commando - Commando_NVM instance to use
* @returns True if version was installed, false if already present
*/
installRequiredVersionIfNeeded = async (commando) => {
const requiredVersion = await this.getRequiredNode_Version();
const installedVersions = await commando.getInstalledNodeVersions();
this.logDebug('Found versions:', installedVersions);
if (installedVersions.includes(requiredVersion))
return false;
await this.installVersion(commando, requiredVersion);
return true;
};
/**
* Uninstalls NVM by removing its directory.
*
* @returns Promise that resolves when uninstall completes
*/
uninstall = async () => {
this.logDebug('Uninstalling NVM');
const absolutePathToNVM_Home = process.env[this._homeEnvVar];
if (!absolutePathToNVM_Home)
return;
fs.rmSync(absolutePathToNVM_Home, { recursive: true, force: true });
};
/**
* Installs a specific Node.js version via NVM.
*
* @param commando - Commando_NVM instance to use
* @param requiredVersion - Optional version (defaults to .nvmrc value)
* @returns Promise that resolves when installation completes
*/
installVersion = async (commando, requiredVersion) => {
requiredVersion ??= await this.getRequiredNode_Version();
this.logDebug(`Installing version: ${requiredVersion}`);
return commando.installNodeVersion(requiredVersion);
};
}
export const NVM = new Cli_NVM();