cfn-forge
Version:
CloudFormation deployment automation tool with git-based workflows
158 lines (138 loc) • 4.12 kB
JavaScript
/**
* Configuration utilities for CFN-Forge
*/
const fs = require('fs').promises;
const path = require('path');
const yaml = require('js-yaml');
const os = require('os');
async function loadConfig(configPath) {
try {
const content = await fs.readFile(configPath, 'utf8');
return yaml.load(content);
} catch (error) {
throw new Error(`Failed to load config from ${configPath}: ${error.message}`);
}
}
/**
* Load and merge AWS configuration into environment
*/
function setupAWSEnvironment(profileName = null) {
try {
const { setAWSEnvironmentVariables } = require('../commands/config');
setAWSEnvironmentVariables(profileName);
} catch (error) {
// Silently fail if AWS config not available
}
}
/**
* Get AWS profile from project configuration or global default
*/
function getProjectAWSProfile(environment = null) {
try {
// Check for project-level AWS profile in .cfn-forge.yaml
const projectConfigPath = path.join(process.cwd(), '.cfn-forge.yaml');
if (require('fs').existsSync(projectConfigPath)) {
const yaml = require('js-yaml');
const content = require('fs').readFileSync(projectConfigPath, 'utf8');
const config = yaml.load(content);
// Check environment-specific AWS settings first
if (environment && config.environments && config.environments[environment]) {
const envConfig = config.environments[environment];
if (envConfig.aws && envConfig.aws.profile) {
return {
profile: envConfig.aws.profile,
region: envConfig.aws.region,
accountId: envConfig.aws.accountId,
};
}
}
// Fall back to global AWS settings
if (config.aws && config.aws.profile) {
return {
profile: config.aws.profile,
region: config.aws.region,
accountId: config.aws.accountId,
};
}
}
return null;
} catch (error) {
return null;
}
}
/**
* Validate AWS account ID if specified in config
*/
async function validateAWSAccount(expectedAccountId) {
if (!expectedAccountId) {
return true; // No validation needed
}
try {
const { execSync } = require('child_process');
const result = execSync(
'aws sts get-caller-identity --query Account --output text',
{ encoding: 'utf8', timeout: 10000 },
);
const actualAccountId = result.trim();
if (actualAccountId !== expectedAccountId) {
throw new Error(
`AWS Account ID mismatch! Expected: ${expectedAccountId}, Actual: ${actualAccountId}\n`
+ 'This safety check prevents deploying to the wrong AWS account.',
);
}
return true;
} catch (error) {
if (error.message.includes('Account ID mismatch')) {
throw error;
}
// If AWS CLI not available or other error, skip validation
console.warn('Warning: Could not validate AWS account ID');
return true;
}
}
class Config {
constructor(global = false) {
this.global = global;
this.configPath = global
? path.join(os.homedir(), '.cfn-forge', 'config.yaml')
: path.join(process.cwd(), '.cfn-forge.yaml');
}
async get(key) {
try {
const config = await this.load();
return config[key];
} catch {
return null;
}
}
async set(key, value) {
const config = await this.load().catch(() => ({}));
config[key] = value;
await this.save(config);
}
async unset(key) {
const config = await this.load().catch(() => ({}));
delete config[key];
await this.save(config);
}
async list() {
try {
return await this.load();
} catch {
return {};
}
}
async load() {
const content = await fs.readFile(this.configPath, 'utf8');
return yaml.load(content) || {};
}
async save(config) {
const dir = path.dirname(this.configPath);
await fs.mkdir(dir, { recursive: true });
const content = yaml.dump(config);
await fs.writeFile(this.configPath, content, 'utf8');
}
}
module.exports = {
loadConfig, Config, setupAWSEnvironment, getProjectAWSProfile, validateAWSAccount,
};