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