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