@starship-ci/generator
Version:
Kubernetes manifest generator for Starship deployments
315 lines (314 loc) • 10.6 kB
JavaScript
import * as fs from 'fs';
import * as yaml from 'js-yaml';
import * as path from 'path';
/**
* Deep merge utility for nested objects
*/
export function deepMerge(target, source) {
const result = { ...target };
Object.keys(source).forEach((key) => {
if (source[key] !== undefined) {
if (source[key] &&
typeof source[key] === 'object' &&
!Array.isArray(source[key])) {
result[key] = deepMerge(result[key] || {}, source[key]);
}
else {
result[key] = source[key];
}
}
});
return result;
}
export class DefaultsManager {
defaultsData;
defaultsPath;
config;
constructor(defaultsPath) {
// Default to the configs/defaults.yaml in the generator package
this.defaultsPath =
defaultsPath || path.join(__dirname, '../configs/defaults.yaml');
this.loadDefaults();
}
/**
* Load defaults from the YAML file
*/
loadDefaults() {
try {
if (fs.existsSync(this.defaultsPath)) {
const yamlContent = fs.readFileSync(this.defaultsPath, 'utf8');
this.defaultsData = yaml.load(yamlContent);
}
else {
console.warn(`Defaults file not found at ${this.defaultsPath}, using empty defaults`);
this.defaultsData = {
defaultChains: {},
defaultFaucet: {},
defaultRelayers: {},
defaultScripts: {},
defaultCometmock: {
image: 'ghcr.io/informalsystems/cometmock:v0.37.x'
}
};
}
}
catch (error) {
console.error('Failed to load defaults.yaml:', error);
this.defaultsData = {
defaultChains: {},
defaultFaucet: {},
defaultRelayers: {},
defaultScripts: {},
defaultCometmock: {
image: 'ghcr.io/informalsystems/cometmock:v0.37.x'
}
};
}
}
/**
* Get chain defaults for a specific chain name
*/
getChainDefaults(chainName) {
return this.defaultsData.defaultChains?.[chainName];
}
/**
* Get faucet defaults for a specific faucet type
*/
getFaucetDefaults(faucetType) {
return this.defaultsData.defaultFaucet?.[faucetType];
}
/**
* Get default scripts
*/
getDefaultScripts() {
return this.defaultsData.defaultScripts || {};
}
/**
* Get default relayers
*/
getDefaultRelayers() {
return this.defaultsData.defaultRelayers || {};
}
/**
* Get default relayer configuration for a specific type
*/
getRelayerDefaults(relayerType) {
return this.defaultsData.defaultRelayers?.[relayerType] || {};
}
/**
* Get default cometmock configuration
*/
getDefaultCometmock() {
return this.defaultsData.defaultCometmock || {};
}
/**
* Process explorer configuration by merging with defaults
*/
processExplorer(explorerConfig) {
if (!explorerConfig)
return undefined;
const defaultExplorer = this.defaultsData.explorer || {};
return deepMerge(defaultExplorer, explorerConfig);
}
/**
* Process registry configuration by merging with defaults
*/
processRegistry(registryConfig) {
if (!registryConfig)
return undefined;
const defaultRegistry = this.defaultsData.registry || {};
return deepMerge(defaultRegistry, registryConfig);
}
/**
* Process faucet configuration by merging with defaults
*/
processFaucet(faucetConfig) {
if (!faucetConfig)
return undefined;
const defaultFaucet = this.defaultsData.faucet || {};
return deepMerge(defaultFaucet, faucetConfig);
}
/**
* Process monitoring configuration by merging with defaults
*/
processMonitoring(monitoringConfig) {
if (!monitoringConfig)
return undefined;
const defaultMonitoring = this.defaultsData.monitoring || {};
return deepMerge(defaultMonitoring, monitoringConfig);
}
/**
* Process ingress configuration by merging with defaults
*/
processIngress(ingressConfig) {
if (!ingressConfig)
return undefined;
const defaultIngress = this.defaultsData.ingress || {};
return deepMerge(defaultIngress, ingressConfig);
}
/**
* Process exposer configuration by merging with defaults
*/
processExposer(exposerConfig) {
if (!exposerConfig)
return undefined;
const defaultExposer = this.defaultsData.exposer || {};
return deepMerge(defaultExposer, exposerConfig);
}
/**
* Process images configuration by merging with defaults
*/
processImages(imagesConfig) {
if (!imagesConfig)
return undefined;
const defaultImages = this.defaultsData.images || {};
return deepMerge(defaultImages, imagesConfig);
}
/**
* Process resources configuration by merging with defaults
*/
processResources(resourcesConfig) {
if (!resourcesConfig)
return undefined;
const defaultResources = this.defaultsData.resources || {};
return deepMerge(defaultResources, resourcesConfig);
}
/**
* Process timeouts configuration by merging with defaults
*/
processTimeouts(timeoutsConfig) {
if (!timeoutsConfig)
return undefined;
const defaultTimeouts = this.defaultsData.timeouts || {};
return deepMerge(defaultTimeouts, timeoutsConfig);
}
/**
* Process a relayer configuration by merging with defaults
* This handles partial overrides properly using deep merge
*/
processRelayer(relayerConfig) {
// Get default relayer configuration for this type
const defaultRelayer = this.getRelayerDefaults(relayerConfig.type);
// Deep merge the configurations (relayer config takes precedence)
const mergedRelayer = deepMerge(defaultRelayer, relayerConfig);
return mergedRelayer;
}
/**
* Process a chain configuration by merging with defaults
* This replaces the complex _chains.tpl logic
*/
processChain(chainConfig) {
// Get default chain configuration
const defaultChain = this.getChainDefaults(chainConfig.name);
// Merge configurations (chain config takes precedence)
const mergedChain = {
...defaultChain,
...chainConfig
};
// Process faucet configuration
const defaultFaucet = this.getFaucetDefaults('starship');
const faucetConfig = {
enabled: true,
type: 'starship',
...defaultFaucet,
...mergedChain.faucet
};
// Process cometmock configuration
const cometmockConfig = {
enabled: false,
...this.getDefaultCometmock(),
...mergedChain.cometmock
};
// Process upgrade/build settings
const upgradeConfig = mergedChain.upgrade || { enabled: false };
const buildConfig = mergedChain.build || { enabled: false };
// Set image based on build requirements
let image = mergedChain.image;
if (mergedChain.build?.enabled || mergedChain.upgrade?.enabled) {
image = 'ghcr.io/cosmology-tech/starship/runner:latest';
}
// Process scripts - merge default scripts with chain-specific scripts
const defaultScripts = this.getDefaultScripts();
const chainDefaultScripts = defaultChain?.scripts || {};
const scripts = {
...defaultScripts,
...chainDefaultScripts,
...mergedChain.scripts
};
return {
...mergedChain,
image,
faucet: faucetConfig,
cometmock: cometmockConfig,
upgrade: upgradeConfig,
build: buildConfig,
scripts
};
}
/**
* Get all available chain types from defaults
*/
getAvailableChainTypes() {
return Object.keys(this.defaultsData.defaultChains || {});
}
/**
* Check if a chain type is supported
*/
isChainTypeSupported(chainName) {
return chainName in (this.defaultsData.defaultChains || {});
}
/**
* Get all defaults data (for debugging or advanced usage)
*/
getAllDefaults() {
return this.defaultsData;
}
}
/**
* Apply defaults to a StarshipConfig
* This is a standalone function that processes all chains, relayers, and global configs
*/
export function applyDefaults(config) {
const defaultsManager = new DefaultsManager();
// Process chains with defaults
const processedChains = config.chains?.map((chain) => defaultsManager.processChain(chain));
// Process relayers with defaults
let processedRelayers;
if (config.relayers && config.relayers?.length > 0) {
processedRelayers = config.relayers.map((relayer) => defaultsManager.processRelayer(relayer));
}
// Process global configurations individually to maintain type safety
const processedConfig = {
...config,
chains: processedChains,
relayers: processedRelayers,
...(config.explorer && {
explorer: defaultsManager.processExplorer(config.explorer)
}),
...(config.registry && {
registry: defaultsManager.processRegistry(config.registry)
}),
...(config.faucet && {
faucet: defaultsManager.processFaucet(config.faucet)
}),
...(config.monitoring && {
monitoring: defaultsManager.processMonitoring(config.monitoring)
}),
...(config.ingress && {
ingress: defaultsManager.processIngress(config.ingress)
}),
...(config.exposer && {
exposer: defaultsManager.processExposer(config.exposer)
}),
...(config.images && {
images: defaultsManager.processImages(config.images)
}),
...(config.resources && {
resources: defaultsManager.processResources(config.resources)
}),
...(config.timeouts && {
timeouts: defaultsManager.processTimeouts(config.timeouts)
})
};
return processedConfig;
}