envilder
Version:
A CLI and GitHub Action that securely centralizes your environment variables from AWS SSM or Azure Key Vault as a single source of truth
157 lines • 8.48 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var PullSecretsToEnvCommandHandler_1;
import { inject, injectable } from 'inversify';
import pc from 'picocolors';
import { EnvironmentVariable } from '../../domain/EnvironmentVariable.js';
import { ExpiredCredentialsError, SecretsFetchError, SsoSessionExpiredError, } from '../../domain/errors/DomainErrors.js';
import { describeError } from '../../infrastructure/describeError.js';
import { TYPES } from '../../types.js';
let PullSecretsToEnvCommandHandler = PullSecretsToEnvCommandHandler_1 = class PullSecretsToEnvCommandHandler {
constructor(secretProvider, variableStore, logger) {
this.secretProvider = secretProvider;
this.variableStore = variableStore;
this.logger = logger;
}
/**
* Handles the PullSecretsToEnvCommand which orchestrates the process of fetching
* environment variable values from a secret store and writing them to a local environment file.
*
* @param command - The PullSecretsToEnvCommand containing mapPath and envFilePath
*/
handle(command) {
return __awaiter(this, void 0, void 0, function* () {
const { requestVariables, currentVariables } = yield this.loadVariables(command);
const { variables, resolvedCount, totalCount } = yield this.envild(requestVariables, currentVariables);
yield this.saveEnvFile(command.envFilePath, variables);
this.logger.info(PullSecretsToEnvCommandHandler_1.buildSummary(resolvedCount, totalCount, command.envFilePath));
});
}
loadVariables(command) {
return __awaiter(this, void 0, void 0, function* () {
const requestVariables = yield this.variableStore.getMapping(command.mapPath);
const currentVariables = yield this.variableStore.getEnvironment(command.envFilePath);
return { requestVariables, currentVariables };
});
}
saveEnvFile(envFilePath, variables) {
return __awaiter(this, void 0, void 0, function* () {
yield this.variableStore.saveEnvironment(envFilePath, variables);
});
}
envild(paramMap, existingEnvVariables) {
return __awaiter(this, void 0, void 0, function* () {
const outcomes = yield Promise.all(Object.entries(paramMap).map(([envVar, secretName]) => this.processSecret(envVar, secretName, existingEnvVariables)));
const resolved = outcomes.filter((outcome) => outcome.status === 'resolved');
const warnings = outcomes.filter((outcome) => outcome.status === 'warning');
const errors = outcomes.filter((outcome) => outcome.status === 'error');
this.logSecretsSection(resolved, warnings);
if (errors.length > 0) {
throw new SecretsFetchError(errors.map((error) => ({
envVar: error.envVar,
path: error.path,
reason: error.reason,
})));
}
return {
variables: existingEnvVariables,
resolvedCount: resolved.length,
totalCount: Object.keys(paramMap).length,
};
});
}
processSecret(envVar, secretName, existingEnvVariables) {
return __awaiter(this, void 0, void 0, function* () {
try {
const value = yield this.secretProvider.getSecret(secretName);
if (value === undefined) {
return {
status: 'warning',
envVar,
path: secretName,
reason: 'not-found',
};
}
if (value === '') {
return { status: 'warning', envVar, path: secretName, reason: 'empty' };
}
existingEnvVariables[envVar] = value;
const masked = new EnvironmentVariable(envVar, value, true).maskedValue;
return { status: 'resolved', envVar, masked };
}
catch (error) {
if (error instanceof ExpiredCredentialsError ||
error instanceof SsoSessionExpiredError) {
throw error;
}
const maskedPath = EnvironmentVariable.maskSecretPath(secretName);
const reason = PullSecretsToEnvCommandHandler_1.describeErrorReason(error, maskedPath);
return { status: 'error', envVar, path: maskedPath, reason };
}
});
}
static describeErrorReason(error, maskedPath) {
const message = describeError(error);
const duplicatedPrefix = `${maskedPath}: `;
return message.startsWith(duplicatedPrefix)
? message.slice(duplicatedPrefix.length)
: message;
}
logSecretsSection(resolved, warnings) {
if (resolved.length === 0 && warnings.length === 0) {
return;
}
this.logger.info(`\n${pc.bold(pc.yellow('\u{1FA99} RESOLVING SECRETS'))}`);
this.logger.info(PullSecretsToEnvCommandHandler_1.RULE);
for (const outcome of resolved) {
this.logger.info(` ${pc.green('\u2713 ')}${pc.bold(PullSecretsToEnvCommandHandler_1.pad(outcome.envVar))}${pc.dim('\u2192 ')}${pc.dim(outcome.masked)}`);
}
this.logWarnings(warnings);
}
logWarnings(warnings) {
for (const outcome of warnings) {
const maskedPath = EnvironmentVariable.maskSecretPath(outcome.path);
if (outcome.reason === 'not-found') {
this.logger.warn(` ${pc.red('\u2717 ')}${pc.bold(pc.red(PullSecretsToEnvCommandHandler_1.pad(outcome.envVar)))} ${pc.red(`secret not found (path: ${maskedPath}) \u2014 skipped`)}`);
continue;
}
this.logger.warn(` ${pc.yellow('\u26A0 ')}${pc.bold(PullSecretsToEnvCommandHandler_1.pad(outcome.envVar))} ${pc.dim(`no value found (path: ${maskedPath}) \u2014 skipped`)}`);
}
}
static pad(name) {
return name.padEnd(PullSecretsToEnvCommandHandler_1.LABEL_WIDTH);
}
static buildSummary(resolvedCount, totalCount, envFilePath) {
return `\n${pc.bold(pc.green('\u2B50 LEVEL CLEARED'))}${pc.dim(` \u2014 ${resolvedCount}/${totalCount} secrets loaded \u00B7 `)}${pc.bold(envFilePath)}${pc.dim(' written')}\n`;
}
};
PullSecretsToEnvCommandHandler.LABEL_WIDTH = 20;
PullSecretsToEnvCommandHandler.RULE = pc.yellow('\u2501'.repeat(60));
PullSecretsToEnvCommandHandler = PullSecretsToEnvCommandHandler_1 = __decorate([
injectable(),
__param(0, inject(TYPES.ISecretProvider)),
__param(1, inject(TYPES.IVariableStore)),
__param(2, inject(TYPES.ILogger)),
__metadata("design:paramtypes", [Object, Object, Object])
], PullSecretsToEnvCommandHandler);
export { PullSecretsToEnvCommandHandler };
//# sourceMappingURL=PullSecretsToEnvCommandHandler.js.map