UNPKG

envx-cli

Version:

Environment file encryption and management tool

215 lines 10.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createInteractiveCommand = void 0; exports.executeInteractive = executeInteractive; exports.showQuickStart = showQuickStart; const chalk_1 = __importDefault(require("chalk")); const commander_1 = require("commander"); const path_1 = __importDefault(require("path")); const schemas_1 = require("../schemas"); const exec_1 = require("../utils/exec"); const file_1 = require("../utils/file"); const interactive_1 = require("../utils/interactive"); const createInteractiveCommand = () => { const command = new commander_1.Command('interactive'); command .description('Interactive mode for setting up .envrc file with secrets') .option('-c, --cwd <path>', 'Working directory path') .option('--overwrite', 'Overwrite existing .envrc file without confirmation') .option('--generate', 'Generate random secrets for all environments') .action(async (options) => { try { await executeInteractive(options); } catch (error) { exec_1.CliUtils.error(`Interactive setup failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); return command; }; exports.createInteractiveCommand = createInteractiveCommand; async function executeInteractive(rawOptions) { interactive_1.InteractiveUtils.displayWelcome(); const cwd = rawOptions.cwd || exec_1.ExecUtils.getCurrentDir(); (0, schemas_1.validateInteractiveOptions)({ cwd, overwrite: rawOptions.overwrite, }); const existingEnvironments = await file_1.FileUtils.findAllEnvironments(cwd); const envrcPath = path_1.default.join(cwd, '.envrc'); const envrcExists = await file_1.FileUtils.fileExists(envrcPath); if (envrcExists) { exec_1.CliUtils.info(`Found existing .envrc file: ${exec_1.CliUtils.formatPath(envrcPath, cwd)}`); if (!rawOptions.overwrite) { const showCurrent = await interactive_1.InteractiveUtils.confirmOperation('Do you want to see the current .envrc contents?', true); if (showCurrent) { try { const currentConfig = await file_1.FileUtils.readEnvrc(cwd); const secrets = Object.keys(currentConfig).filter(key => key.endsWith('_SECRET')); if (secrets.length > 0) { console.log(); exec_1.CliUtils.subheader('Current Secrets in .envrc'); secrets.forEach(secret => { const stage = secret.replace('_SECRET', '').toLowerCase(); console.log(` • ${exec_1.CliUtils.formatEnvironment(stage)}: ${chalk_1.default.cyan(secret)}`); }); } else { exec_1.CliUtils.warning('No secrets found in current .envrc file'); } } catch (error) { exec_1.CliUtils.error(`Could not read existing .envrc: ${error}`); } } const overwrite = await interactive_1.InteractiveUtils.confirmOperation('Do you want to overwrite the existing .envrc file?', false); if (!overwrite) { exec_1.CliUtils.info('Keeping existing .envrc file. Use --overwrite flag to force overwrite.'); await showEnvrcUsageHelp(cwd); return; } } } try { const envrcConfig = await interactive_1.InteractiveUtils.setupEnvrc(cwd, existingEnvironments); const writeResult = await file_1.FileUtils.writeEnvrc(cwd, envrcConfig); if (writeResult.success) { exec_1.CliUtils.success(`Successfully created .envrc file`); console.log(`Location: ${exec_1.CliUtils.formatPath(writeResult.filePath, cwd)}`); await showEnvrcUsageHelp(cwd); await showDirenvSetup(); } else { exec_1.CliUtils.error(`Failed to create .envrc file: ${writeResult.message}`); if (writeResult.error) { console.log(chalk_1.default.red(` • ${writeResult.error.message}`)); } process.exit(1); } } catch (error) { if (error instanceof Error && error.message === 'Setup cancelled by user') { exec_1.CliUtils.info('Setup cancelled.'); return; } throw error; } } async function showEnvrcUsageHelp(cwd) { console.log(); exec_1.CliUtils.subheader('How to Use .envrc'); const envrcConfig = await file_1.FileUtils.readEnvrc(cwd); const secrets = Object.keys(envrcConfig).filter(key => key.endsWith('_SECRET')); console.log('Your .envrc file contains secrets for encryption/decryption.'); console.log('You can now use envx commands without specifying passphrases:'); console.log(); secrets.forEach(secret => { const stage = secret.replace('_SECRET', '').toLowerCase(); console.log(chalk_1.default.cyan(` envx encrypt -e ${stage}`)); console.log(chalk_1.default.cyan(` envx decrypt -e ${stage}`)); }); console.log(); console.log('Or specify a custom secret:'); console.log(chalk_1.default.cyan(' envx encrypt -e production -s PRODUCTION_SECRET')); console.log(); } async function showDirenvSetup() { exec_1.CliUtils.subheader('Direnv Integration (Optional)'); console.log('For automatic environment loading, you can use direnv:'); console.log(); try { exec_1.ExecUtils.exec('direnv --version', { silent: true }); exec_1.CliUtils.success('Direnv is already installed!'); console.log(); console.log('To enable automatic loading:'); console.log(chalk_1.default.cyan(' direnv allow')); console.log(); console.log('This will automatically export the secrets when you enter this directory.'); } catch { exec_1.CliUtils.info('Direnv is not installed. To install:'); console.log(); console.log(chalk_1.default.yellow('macOS:')); console.log(chalk_1.default.cyan(' brew install direnv')); console.log(); console.log(chalk_1.default.yellow('Ubuntu/Debian:')); console.log(chalk_1.default.cyan(' sudo apt install direnv')); console.log(); console.log(chalk_1.default.yellow('Other systems:')); console.log(chalk_1.default.cyan(' https://direnv.net/docs/installation.html')); console.log(); console.log('After installation, add to your shell profile:'); console.log(chalk_1.default.cyan(' eval "$(direnv hook bash)" # for bash')); console.log(chalk_1.default.cyan(' eval "$(direnv hook zsh)" # for zsh')); console.log(); console.log('Then run:'); console.log(chalk_1.default.cyan(' direnv allow')); } console.log(); exec_1.CliUtils.warning('Security Note:'); console.log(chalk_1.default.yellow('• Never commit .envrc to version control')); console.log(chalk_1.default.yellow('• Add .envrc to your .gitignore file')); console.log(chalk_1.default.yellow('• The secrets in .envrc are sensitive data')); } async function showEnvironmentSummary(cwd) { const environments = await file_1.FileUtils.findAllEnvironments(cwd); if (environments.length === 0) { return; } exec_1.CliUtils.subheader('Environment Files Summary'); const tableRows = []; for (const env of environments.sort()) { const envFiles = await file_1.FileUtils.findEnvFiles(env, cwd); const unencrypted = envFiles.filter(f => !f.encrypted && f.exists); const encrypted = envFiles.filter(f => f.encrypted && f.exists); let status = ''; if (unencrypted.length > 0 && encrypted.length > 0) { status = chalk_1.default.yellow('Mixed'); } else if (encrypted.length > 0) { status = chalk_1.default.green('Encrypted'); } else if (unencrypted.length > 0) { status = chalk_1.default.red('Unencrypted'); } else { status = chalk_1.default.gray('No files'); } const totalFiles = unencrypted.length + encrypted.length; const fileCount = totalFiles > 0 ? `${totalFiles} file(s)` : 'No files'; tableRows.push([exec_1.CliUtils.formatEnvironment(env), fileCount, status]); } exec_1.CliUtils.printTable(['Environment', 'Files', 'Status'], tableRows); console.log(); exec_1.CliUtils.info('Commands to manage your environment files:'); console.log(chalk_1.default.gray('• envx create -e <env> Create new environment file')); console.log(chalk_1.default.gray('• envx encrypt -e <env> Encrypt environment files')); console.log(chalk_1.default.gray('• envx decrypt -e <env> Decrypt environment files')); } async function showQuickStart(cwd) { exec_1.CliUtils.header('EnvX Quick Start'); console.log("Welcome to EnvX! Here's how to get started:"); console.log(); console.log(chalk_1.default.bold('1. Create environment files:')); console.log(chalk_1.default.cyan(' envx create -i')); console.log(); console.log(chalk_1.default.bold('2. Set up secrets for encryption:')); console.log(chalk_1.default.cyan(' envx interactive')); console.log(); console.log(chalk_1.default.bold('3. Encrypt your environment files:')); console.log(chalk_1.default.cyan(' envx encrypt -e production')); console.log(); console.log(chalk_1.default.bold('4. Commit encrypted files to git:')); console.log(chalk_1.default.cyan(' git add *.gpg')); console.log(chalk_1.default.cyan(' git commit -m "Add encrypted environment files"')); console.log(); console.log(chalk_1.default.bold('5. Decrypt when needed:')); console.log(chalk_1.default.cyan(' envx decrypt -e production')); console.log(); await showEnvironmentSummary(cwd); } //# sourceMappingURL=interactive.js.map