UNPKG

envx-cli

Version:

Environment file encryption and management tool

310 lines 13.3 kB
#!/usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createProgram = createProgram; const chalk_1 = __importDefault(require("chalk")); const commander_1 = require("commander"); const path_1 = __importDefault(require("path")); const copy_1 = require("./commands/copy"); const create_1 = require("./commands/create"); const decrypt_1 = require("./commands/decrypt"); const encrypt_1 = require("./commands/encrypt"); const interactive_1 = require("./commands/interactive"); const exec_1 = require("./utils/exec"); const file_1 = require("./utils/file"); const interactive_2 = require("./utils/interactive"); const packageJson = __importStar(require("../package.json")); async function createProgram() { const program = new commander_1.Command(); program .name('envx') .description('Environment file encryption and management tool') .version(packageJson.version) .option('-v, --verbose', 'Enable verbose output') .option('-q, --quiet', 'Suppress non-error output') .hook('preAction', async (thisCommand) => { const options = thisCommand.opts(); if (options.quiet) { const originalLog = console.log; console.log = (...args) => { if (!args.some(arg => typeof arg === 'string' && arg.includes('✗'))) { return; } originalLog(...args); }; } }); program.addCommand((0, encrypt_1.createEncryptCommand)()); program.addCommand((0, decrypt_1.createDecryptCommand)()); program.addCommand((0, create_1.createCreateCommand)()); program.addCommand((0, copy_1.createCopyCommand)()); program.addCommand((0, interactive_1.createInteractiveCommand)()); program .command('list') .alias('ls') .description('List all environment files and their status') .option('-c, --cwd <path>', 'Working directory path') .action(async (options) => { try { await executeList(options); } catch (error) { exec_1.CliUtils.error(`List failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); program .command('status') .description('Show project encryption status and recommendations') .option('-c, --cwd <path>', 'Working directory path') .action(async (options) => { try { await executeStatus(options); } catch (error) { exec_1.CliUtils.error(`Status check failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); program .command('init') .description('Initialize EnvX in a new project') .option('-c, --cwd <path>', 'Working directory path') .action(async (options) => { try { await executeInit(options); } catch (error) { exec_1.CliUtils.error(`Initialization failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); program .command('version') .description('Show version information') .action(() => { console.log(); console.log(chalk_1.default.bold.cyan('🔐 EnvX')); console.log(`Version: ${chalk_1.default.green(packageJson.version)}`); console.log(`Description: ${packageJson.description || 'Environment file encryption and management tool'}`); console.log(); console.log('Dependencies:'); console.log(`• GPG: ${exec_1.ExecUtils.isGpgAvailable() ? chalk_1.default.green('Available') : chalk_1.default.red('Not found')}`); console.log(`• Node.js: ${chalk_1.default.green(process.version)}`); console.log(); }); return program; } async function executeList(options) { const cwd = options.cwd || exec_1.ExecUtils.getCurrentDir(); exec_1.CliUtils.header('Environment Files'); const environments = await file_1.FileUtils.findAllEnvironments(cwd); if (environments.length === 0) { exec_1.CliUtils.warning('No environment files found in the current directory.'); console.log(); exec_1.CliUtils.info('To get started:'); console.log(chalk_1.default.cyan(' envx init')); console.log(chalk_1.default.cyan(' envx create -i')); return; } const tableRows = []; for (const env of environments.sort()) { const envFiles = await file_1.FileUtils.findEnvFiles(env, cwd); for (const file of envFiles) { const displayPath = file.encrypted ? file_1.FileUtils.getEncryptedPath(file.path) : file.path; const relativePath = file_1.FileUtils.getRelativePath(displayPath, cwd); const status = file.encrypted ? chalk_1.default.green('Encrypted') : chalk_1.default.yellow('Unencrypted'); const type = file.encrypted ? '.gpg' : '.env'; tableRows.push([ exec_1.CliUtils.formatEnvironment(env), chalk_1.default.cyan(relativePath), type, status, ]); } } if (tableRows.length > 0) { exec_1.CliUtils.printTable(['Environment', 'File Path', 'Type', 'Status'], tableRows); } console.log(); const envrcExists = await file_1.FileUtils.fileExists(path_1.default.join(cwd, '.envrc')); exec_1.CliUtils.info(`Secrets file (.envrc): ${envrcExists ? chalk_1.default.green('Present') : chalk_1.default.yellow('Not found')}`); if (!envrcExists) { console.log(chalk_1.default.gray(' Use "envx interactive" to set up secrets')); } } async function executeStatus(options) { const cwd = options.cwd || exec_1.ExecUtils.getCurrentDir(); exec_1.CliUtils.header('Project Status'); exec_1.CliUtils.subheader('Prerequisites'); console.log(`GPG: ${exec_1.ExecUtils.isGpgAvailable() ? chalk_1.default.green('✓ Available') : chalk_1.default.red('✗ Not found')}`); if (!exec_1.ExecUtils.isGpgAvailable()) { interactive_2.InteractiveUtils.displayPrerequisites(); return; } const environments = await file_1.FileUtils.findAllEnvironments(cwd); if (environments.length === 0) { exec_1.CliUtils.warning('No environment files found.'); console.log(); exec_1.CliUtils.info('Recommendations:'); console.log(chalk_1.default.yellow('• Run "envx init" to get started')); console.log(chalk_1.default.yellow('• Create environment files with "envx create"')); return; } exec_1.CliUtils.subheader('Environment Summary'); let totalFiles = 0; let encryptedFiles = 0; let unencryptedFiles = 0; const recommendations = []; for (const env of environments) { const envFiles = await file_1.FileUtils.findEnvFiles(env, cwd); const encrypted = envFiles.filter(f => f.encrypted).length; const unencrypted = envFiles.filter(f => !f.encrypted).length; totalFiles += encrypted + unencrypted; encryptedFiles += encrypted; unencryptedFiles += unencrypted; if (unencrypted > 0 && ['production', 'staging'].includes(env)) { recommendations.push(`Encrypt ${env} environment files for security`); } } console.log(`Total environments: ${chalk_1.default.cyan(environments.length)}`); console.log(`Total files: ${chalk_1.default.cyan(totalFiles)}`); console.log(`Encrypted: ${chalk_1.default.green(encryptedFiles)}`); console.log(`Unencrypted: ${unencryptedFiles > 0 ? chalk_1.default.yellow(unencryptedFiles) : chalk_1.default.gray(unencryptedFiles)}`); console.log(); const envrcExists = await file_1.FileUtils.fileExists(path_1.default.join(cwd, '.envrc')); console.log(`Secrets file (.envrc): ${envrcExists ? chalk_1.default.green('✓ Present') : chalk_1.default.yellow('✗ Missing')}`); if (!envrcExists) { recommendations.push('Set up .envrc file with "envx interactive"'); } if (recommendations.length > 0) { console.log(); exec_1.CliUtils.subheader('Recommendations'); recommendations.forEach(rec => { console.log(chalk_1.default.yellow(`• ${rec}`)); }); } else { console.log(); exec_1.CliUtils.success('Your project follows security best practices! 🎉'); } } async function executeInit(options) { const cwd = options.cwd || exec_1.ExecUtils.getCurrentDir(); interactive_2.InteractiveUtils.displayWelcome(); exec_1.CliUtils.info('Initializing EnvX in your project...'); console.log(`Directory: ${exec_1.CliUtils.formatPath(cwd, process.cwd())}`); console.log(); const existingEnvironments = await file_1.FileUtils.findAllEnvironments(cwd); const envrcExists = await file_1.FileUtils.fileExists(path_1.default.join(cwd, '.envrc')); if (existingEnvironments.length > 0 || envrcExists) { exec_1.CliUtils.warning('EnvX appears to already be set up in this project.'); if (existingEnvironments.length > 0) { console.log(`Found environments: ${existingEnvironments.map(env => exec_1.CliUtils.formatEnvironment(env)).join(', ')}`); } if (envrcExists) { console.log('Found .envrc file'); } const proceed = await interactive_2.InteractiveUtils.confirmOperation('Do you want to continue with initialization anyway?', false); if (!proceed) { exec_1.CliUtils.info('Initialization cancelled.'); return; } } if (!exec_1.ExecUtils.isGpgAvailable()) { exec_1.CliUtils.error('GPG is required but not found.'); interactive_2.InteractiveUtils.displayPrerequisites(); return; } exec_1.CliUtils.success('GPG is available'); exec_1.CliUtils.info('Setting up .gitignore...'); const gitignoreResult = await file_1.FileUtils.updateGitignore(cwd); if (gitignoreResult.success) { exec_1.CliUtils.success(gitignoreResult.message); } else { exec_1.CliUtils.warning(`Could not update .gitignore: ${gitignoreResult.message}`); } await (0, interactive_1.showQuickStart)(cwd); const startSetup = await interactive_2.InteractiveUtils.confirmOperation('Would you like to start the interactive setup now?'); if (startSetup) { console.log(); exec_1.CliUtils.info('Starting interactive setup...'); const { executeInteractive } = await Promise.resolve().then(() => __importStar(require('./commands/interactive'))); await executeInteractive({ cwd: options.cwd }); } else { console.log(); exec_1.CliUtils.info('You can run the setup later with:'); console.log(chalk_1.default.cyan(' envx interactive')); } } process.on('uncaughtException', error => { exec_1.CliUtils.error(`Uncaught error: ${error.message}`); if (process.env.NODE_ENV === 'development') { console.error(error.stack); } process.exit(1); }); process.on('unhandledRejection', (reason, promise) => { exec_1.CliUtils.error(`Unhandled rejection at: ${promise}, reason: ${reason}`); process.exit(1); }); async function main() { try { const program = await createProgram(); if (process.argv.length <= 2) { program.help(); return; } await program.parseAsync(process.argv); } catch (error) { exec_1.CliUtils.error(`Command failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } } if (require.main === module) { main(); } //# sourceMappingURL=index.js.map