kubricate
Version:
A TypeScript framework for building reusable, type-safe Kubernetes infrastructure — without the YAML mess.
108 lines • 4.45 kB
JavaScript
import path from 'node:path';
import c from 'ansis';
import { merge } from 'lodash-es';
import { MARK_CHECK, MARK_NODE, MARK_TREE_END, MARK_TREE_LEAF } from '../../internal/constant.js';
import { extractStackInfoFromConfig } from '../../internal/utils.js';
import { GenerateRunner } from './GenerateRunner.js';
import { Renderer } from './Renderer.js';
export class GenerateCommand {
options;
logger;
constructor(options, logger) {
this.options = options;
this.logger = logger;
}
resolveDefaultGenerateOptions(config) {
const defaultOptions = {
outputDir: 'output',
outputMode: 'stack',
cleanOutputDir: true
};
const result = merge({}, defaultOptions, config.generate);
return result;
}
async execute(config) {
const logger = this.logger;
const generateOptions = this.resolveDefaultGenerateOptions(config);
logger.info('Generating stacks for Kubernetes...');
const renderedFiles = this.getRenderedFiles(config, generateOptions.outputMode);
const runner = new GenerateRunner(this.options, generateOptions, renderedFiles, this.logger);
this.showStacks(config);
this.logger.log('');
await runner.run();
logger.log(c.green`${MARK_CHECK} Done!`);
}
getRenderedFiles(config, outputMode) {
const renderer = new Renderer(config, this.logger);
const rendered = renderer.renderStacks(config);
const files = {};
const renderedFiles = [];
for (const r of rendered) {
const outPath = renderer.resolveOutputPath(r, outputMode, this.options.stdout);
if (!files[outPath]) files[outPath] = [];
files[outPath].push(r.content);
}
for (const [filePath, contents] of Object.entries(files)) {
const relatePath = path.join(this.options.outDir, filePath);
renderedFiles.push({
filePath: relatePath,
originalPath: filePath,
content: contents.join('\n')
});
}
if (this.options.filter) {
return this.filterResources(renderedFiles, this.options.filter);
}
return renderedFiles;
}
filterResources(renderedFiles, filters) {
if (filters.length === 0) return renderedFiles;
const filterSet = new Set(filters);
const matchedFilters = new Set();
const stackIds = new Set();
const fullResourceIds = new Set();
const filtered = renderedFiles.filter(file => {
const originalPath = file.originalPath; // e.g., myApp.deployment
const [stackId] = originalPath.split('.');
stackIds.add(stackId);
fullResourceIds.add(originalPath);
const matched = filterSet.has(stackId) || filterSet.has(originalPath);
if (matched) {
if (filterSet.has(stackId)) matchedFilters.add(stackId);
if (filterSet.has(originalPath)) matchedFilters.add(originalPath);
}
return matched;
});
const unmatchedFilters = filters.filter(f => !matchedFilters.has(f));
if (unmatchedFilters.length > 0) {
const stacksList = Array.from(stackIds).sort().join('\n - ');
const resourcesList = Array.from(fullResourceIds).sort().join('\n - ');
const stacksSection = stackIds.size > 0 ? ` • Stacks: \n` + ` - ${stacksList}\n` : '';
const resourcesSection = fullResourceIds.size > 0 ? ` • Resources: \n` + ` - ${resourcesList}\n` : '';
throw new Error(`The following filters did not match any resource: ${unmatchedFilters.join(', ')}.\n\n` + `Available filters:\n` + stacksSection + resourcesSection + `\nPlease check your --filter values and try again.`);
}
return filtered;
}
showStacks(config) {
const logger = this.logger;
const stacksLength = Object.keys(config.stacks ?? {}).length;
if (!config.stacks || stacksLength === 0) {
throw new Error('No stacks found in config');
}
logger.info(`Found ${stacksLength} stacks in config:`);
const renderListTree = kinds => {
const lastIndex = kinds.length - 1;
for (let i = 0; i < kinds.length; i++) {
const kind = kinds[i];
const marker = i === lastIndex ? MARK_TREE_END : MARK_TREE_LEAF;
logger.log(c.blue` ${marker} ${kind.kind}` + c.dim` (id: ${kind.id})`);
}
};
for (const stack of extractStackInfoFromConfig(config)) {
logger.log(c.blue` ${MARK_NODE} ${stack.name}` + c.dim` (type: ${stack.type})`);
renderListTree(stack.kinds);
logger.log('');
}
}
}
//# sourceMappingURL=GenerateCommand.js.map