< Summary - Envilder Core (TypeScript)

Information
Class: src/envilder/apps/cli/Cli.ts
Assembly: Default
File(s): src/envilder/apps/cli/Cli.ts
Tag: 151_24479375065
Line coverage
88%
Covered lines: 24
Uncovered lines: 3
Coverable lines: 27
Total lines: 152
Line coverage: 88.8%
Branch coverage
57%
Covered branches: 8
Total branches: 14
Branch coverage: 57.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

File(s)

src/envilder/apps/cli/Cli.ts

#LineLine coverage
 1import 'reflect-metadata';
 2import { dirname, join } from 'node:path';
 3import { fileURLToPath } from 'node:url';
 4import { Command } from 'commander';
 5import type { Container } from 'inversify';
 6import pc from 'picocolors';
 7import { DispatchActionCommand } from '../../core/application/dispatch/DispatchActionCommand.js';
 8import type { DispatchActionCommandHandler } from '../../core/application/dispatch/DispatchActionCommandHandler.js';
 9import type { CliOptions } from '../../core/domain/CliOptions.js';
 10import type { MapFileConfig } from '../../core/domain/MapFileConfig.js';
 11import { PackageVersionReader } from '../../core/infrastructure/package/PackageVersionReader.js';
 12import { readMapFileConfig } from '../../core/infrastructure/variableStore/FileVariableStore.js';
 13import { TYPES } from '../../core/types.js';
 14import { Startup } from './Startup.js';
 15
 16let serviceProvider: Container;
 17
 18async function executeCommand(options: CliOptions): Promise<void> {
 319  const commandHandler = serviceProvider.get<DispatchActionCommandHandler>(
 20    TYPES.DispatchActionCommandHandler,
 21  );
 22
 323  const command = DispatchActionCommand.fromCliOptions(options);
 324  await commandHandler.handleCommand(command);
 25}
 26
 27export async function main() {
 428  const program = new Command();
 429  const version = await readPackageVersion();
 30
 431  const banner = `
 32  ${pc.green('███████╗')}${pc.cyan('███╗   ██╗')}${pc.magenta('██╗   ██╗')}${pc.yellow('██╗')}${pc.red('██╗     ')}${pc.
 33  ${pc.green('██╔════╝')}${pc.cyan('████╗  ██║')}${pc.magenta('██║   ██║')}${pc.yellow('██║')}${pc.red('██║     ')}${pc.
 34  ${pc.green('█████╗  ')}${pc.cyan('██╔██╗ ██║')}${pc.magenta('██║   ██║')}${pc.yellow('██║')}${pc.red('██║     ')}${pc.
 35  ${pc.green('██╔══╝  ')}${pc.cyan('██║╚██╗██║')}${pc.magenta('╚██╗ ██╔╝')}${pc.yellow('██║')}${pc.red('██║     ')}${pc.
 36  ${pc.green('███████╗')}${pc.cyan('██║ ╚████║')}${pc.magenta(' ╚████╔╝ ')}${pc.yellow('██║')}${pc.red('███████╗')}${pc.
 37  ${pc.green('╚══════╝')}${pc.cyan('╚═╝  ╚═══╝')}${pc.magenta('  ╚═══╝  ')}${pc.yellow('╚═╝')}${pc.red('╚══════╝')}${pc.
 38  ${pc.dim('Your secrets, one command away')}          ${pc.dim('aws & azure')}
 39
 40  ${pc.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}
 41  ${pc.green('WORLD 1-1')} ${pc.dim('— SELECT YOUR MISSION')}
 42  ${pc.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}
 43
 44  ${pc.green('>')} ${pc.bold('Generate a .env file')}  ${pc.dim('(pull secrets from the cloud)')}
 45    ${pc.cyan('envilder --map=param-map.json --envfile=.env')}
 46
 47  ${pc.magenta('>')} ${pc.bold('Sync .env back to cloud')}  ${pc.dim('(push secrets up)')}
 48    ${pc.cyan('envilder --push --map=param-map.json --envfile=.env')}
 49
 50  ${pc.red('>')} ${pc.bold('Push a single secret')}
 51    ${pc.cyan('envilder --push --key=API_KEY --value=s3cret --secret-path=/my/path')}
 52
 53  ${pc.blue('>')} ${pc.bold('Use Azure Key Vault')}
 54    ${pc.cyan('envilder --provider=azure --map=param-map.json --envfile=.env')}
 55`;
 56
 457  program
 58    .name('envilder')
 59    .description(banner)
 60    .version(version)
 61    .option(
 62      '--map <path>',
 63      'Path to the JSON file with environment variable mapping (required for most commands)',
 64    )
 65    .option(
 66      '--envfile <path>',
 67      'Path to the .env file to be generated or imported (required for most commands)',
 68    )
 69    .option('--profile <name>', 'AWS CLI profile to use (optional)')
 70    .option(
 71      '--provider <name>',
 72      'Cloud provider to use: aws or azure (default: aws)',
 73    )
 74    .option(
 75      '--vault-url <url>',
 76      'Azure Key Vault URL (overrides $config.vaultUrl in map file)',
 77    )
 78    .option('--push', 'Push local .env file back to cloud provider')
 79    .option(
 80      '--key <name>',
 81      'Single environment variable name to push (only with --push)',
 82    )
 83    .option(
 84      '--value <value>',
 85      'Value of the single environment variable to push (only with --push)',
 86    )
 87    .option(
 88      '--secret-path <path>',
 89      'Secret path in your cloud provider for the single variable (only with --push)',
 90    )
 91    .option(
 92      '--ssm-path <path>',
 93      '[DEPRECATED: use --secret-path] Alias for --secret-path',
 94    )
 95    .hook('preAction', (thisCommand) => {
 396      const opts = thisCommand.opts();
 397      if (opts.ssmPath) {
 098        console.warn(
 99          pc.yellow(
 100            '⚠️  --ssm-path is deprecated and will be removed in a future release. Use --secret-path instead.',
 101          ),
 102        );
 0103        if (!opts.secretPath) {
 0104          thisCommand.setOptionValue('secretPath', opts.ssmPath);
 105        }
 106      }
 107    })
 108    .action(
 109      async ({
 110        provider,
 111        vaultUrl,
 112        ...options
 113      }: CliOptions & { provider?: string; vaultUrl?: string }) => {
 3114        const fileConfig = options.map
 115          ? await readMapFileConfig(options.map)
 116          : {};
 117
 3118        const config: MapFileConfig = {
 119          ...fileConfig,
 120          ...(provider && { provider }),
 121          ...(vaultUrl && { vaultUrl }),
 122          ...(options.profile && { profile: options.profile }),
 123        };
 124
 3125        const infraOptions: Record<string, unknown> = {};
 3126        const extraHosts = process.env.ENVILDER_ALLOWED_VAULT_HOSTS;
 3127        if (extraHosts) {
 1128          infraOptions.allowedVaultHosts = extraHosts
 129            .split(',')
 2130            .map((h) => h.trim());
 1131          infraOptions.disableChallengeResourceVerification = true;
 132        }
 133
 3134        serviceProvider = Startup.build()
 135          .configureServices()
 136          .configureInfrastructure(config, infraOptions)
 137          .create();
 138
 3139        await executeCommand(options);
 140      },
 141    );
 142
 4143  await program.parseAsync(process.argv);
 144}
 145
 146function readPackageVersion(): Promise<string> {
 4147  const __filename = fileURLToPath(import.meta.url);
 4148  const __dirname = dirname(__filename);
 4149  const packageJsonPath = join(__dirname, '../../../../package.json');
 150
 4151  return new PackageVersionReader().getVersion(packageJsonPath);
 152}