UNPKG

cfn-forge

Version:

CloudFormation deployment automation tool with git-based workflows

147 lines (130 loc) 4.7 kB
/** * Quick validation status checker for interactive menu */ const fs = require('fs'); const yaml = require('js-yaml'); // CloudFormation schema for parsing templates with intrinsic functions const CloudFormationSchema = yaml.DEFAULT_SCHEMA.extend([ new yaml.Type('!Ref', { kind: 'scalar', construct: (data) => ({ 'Fn::Ref': data }) }), new yaml.Type('!Sub', { kind: 'scalar', construct: (data) => ({ 'Fn::Sub': data }) }), new yaml.Type('!GetAtt', { kind: 'sequence', construct: (data) => ({ 'Fn::GetAtt': data }) }), new yaml.Type('!GetAZs', { kind: 'scalar', construct: (data) => ({ 'Fn::GetAZs': data }) }), new yaml.Type('!ImportValue', { kind: 'scalar', construct: (data) => ({ 'Fn::ImportValue': data }) }), new yaml.Type('!Join', { kind: 'sequence', construct: (data) => ({ 'Fn::Join': data }) }), new yaml.Type('!Select', { kind: 'sequence', construct: (data) => ({ 'Fn::Select': data }) }), new yaml.Type('!Split', { kind: 'sequence', construct: (data) => ({ 'Fn::Split': data }) }), new yaml.Type('!Base64', { kind: 'scalar', construct: (data) => ({ 'Fn::Base64': data }) }), new yaml.Type('!Cidr', { kind: 'sequence', construct: (data) => ({ 'Fn::Cidr': data }) }), new yaml.Type('!FindInMap', { kind: 'sequence', construct: (data) => ({ 'Fn::FindInMap': data }) }), new yaml.Type('!If', { kind: 'sequence', construct: (data) => ({ 'Fn::If': data }) }), new yaml.Type('!Not', { kind: 'sequence', construct: (data) => ({ 'Fn::Not': data }) }), new yaml.Type('!Equals', { kind: 'sequence', construct: (data) => ({ 'Fn::Equals': data }) }), new yaml.Type('!And', { kind: 'sequence', construct: (data) => ({ 'Fn::And': data }) }), new yaml.Type('!Or', { kind: 'sequence', construct: (data) => ({ 'Fn::Or': data }) }), new yaml.Type('!Condition', { kind: 'scalar', construct: (data) => ({ Condition: data }) }), ]); /** * Quick validation status check (for menu display) * Returns: { status: 'valid'|'invalid'|'no-files', summary: string } */ function getValidationStatus() { try { const { templateFile, parameterFiles } = findFiles(); if (!templateFile) { return { status: 'no-files', summary: 'No template found', }; } // Quick template syntax check try { const templateContent = fs.readFileSync(templateFile, 'utf8'); yaml.load(templateContent, { schema: CloudFormationSchema }); } catch (error) { return { status: 'invalid', summary: 'Template has syntax errors', }; } if (parameterFiles.length === 0) { return { status: 'valid', summary: 'Template valid, no parameters', }; } // Quick parameter file format check for (const paramFile of parameterFiles) { try { const paramContent = fs.readFileSync(paramFile, 'utf8'); if (paramFile.endsWith('.json')) { JSON.parse(paramContent); } else { yaml.load(paramContent); } } catch (error) { return { status: 'invalid', summary: `${parameterFiles.length} param file(s) - format errors`, }; } } return { status: 'valid', summary: `Template + ${parameterFiles.length} param file(s)`, }; } catch (error) { return { status: 'invalid', summary: 'Validation error', }; } } /** * Find template and parameter files */ function findFiles() { const templateCandidates = [ 'template.yaml', 'template.yml', 'cloudformation.yaml', 'cloudformation.yml', 'stack.yaml', 'stack.yml', ]; const parameterCandidates = [ 'parameters.json', 'parameters.yaml', 'parameters.yml', 'params.json', 'params.yaml', 'params.yml', 'parameters-dev.json', 'parameters-staging.json', 'parameters-prod.json', 'parameters-dev.yaml', 'parameters-staging.yaml', 'parameters-prod.yaml', ]; let templateFile = null; // Check cfn-forge config first try { const configContent = fs.readFileSync('.cfn-forge.yaml', 'utf8'); const config = yaml.load(configContent); if (config.templates && config.templates.main) { templateFile = config.templates.main; } } catch { // No config file, continue with candidate search } // Find first existing template if none specified if (!templateFile || !fs.existsSync(templateFile)) { templateFile = templateCandidates.find((file) => fs.existsSync(file)); } // Find existing parameter files const parameterFiles = parameterCandidates.filter((file) => fs.existsSync(file)); return { templateFile, parameterFiles }; } module.exports = { getValidationStatus, findFiles, };