envx-cli
Version:
Environment file encryption and management tool
215 lines • 10.3 kB
JavaScript
;
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