diff --git a/src/configFile.ts b/src/configFile.ts index 6d5d9fd..9d59f52 100644 --- a/src/configFile.ts +++ b/src/configFile.ts @@ -1,8 +1,9 @@ import fs from 'fs-extra'; +import _ from 'lodash'; import { resolve } from 'path'; -import yaml from 'yaml'; +import { Document, parse, parseDocument, stringify, YAMLMap } from 'yaml'; -import { Config, configSchema } from './Config'; +import { type Config, configSchema } from './Config'; import { pkgDir } from './pkgDir'; const resolveConfigPath = async (path?: string) => { @@ -15,7 +16,7 @@ const resolveConfigPath = async (path?: string) => { if (await fs.exists(resolvedPath)) { try { - const { configPath } = yaml.parse( + const { configPath } = parse( await fs.readFile(resolvedPath, 'utf8'), ) as Partial; @@ -34,14 +35,33 @@ const resolveConfigPath = async (path?: string) => { return resolvedPath; }; -export const readConfigFile = async (path?: string) => +const readConfigDoc = async (path?: string) => + parseDocument(await fs.readFile(await resolveConfigPath(path), 'utf8')); + +export const readConfig = async (path?: string) => configSchema.parse( - yaml.parse(await fs.readFile(await resolveConfigPath(path), 'utf8')), + parse(await fs.readFile(await resolveConfigPath(path), 'utf8')), ); -export const writeConfigFile = async (config: Config, path?: string) => { +export const writeConfig = async (config: Config, path?: string) => { + const doc = await readConfigDoc(path); + await fs.writeFile( await resolveConfigPath(path), - yaml.stringify(config, { doubleQuotedAsJSON: true }), + stringify(updateYamlDoc(doc, config), { doubleQuotedAsJSON: true }), ); }; + +function updateYamlDoc(doc: Document.Parsed, update: object | object[]) { + for (const key in update) { + const value = _.get(update, key) as object | object[]; + if (_.isPlainObject(value)) { + if (!doc.has(key)) doc.set(key, new YAMLMap()); + updateYamlDoc(doc.get(key) as Document.Parsed, value); + } else { + doc.set(key, value); + } + } + + return doc; +} diff --git a/src/parseConfig.ts b/src/parseConfig.ts index 55456d5..117ddef 100644 --- a/src/parseConfig.ts +++ b/src/parseConfig.ts @@ -2,7 +2,7 @@ import { Handlebars } from '@karmaniverous/handlebars'; import chalk from 'chalk'; import { type Config } from './Config'; -import { readConfigFile } from './configFile'; +import { readConfig } from './configFile'; import { type ConsoleParams } from './ConsoleParams'; import { getErrorMessage } from './getErrorMessage'; @@ -17,7 +17,7 @@ export const parseConfig = async ({ process.stdout.write(chalk.black.bold('Parsing config file...')); // Load & parse config file. - config = await readConfigFile(configPath); + config = await readConfig(configPath); // Recursively apply config to itself as a handlebars template. let thisPass = JSON.stringify(config); diff --git a/src/updateConfig.ts b/src/updateConfig.ts index ee7ace1..40043db 100644 --- a/src/updateConfig.ts +++ b/src/updateConfig.ts @@ -4,7 +4,7 @@ import _ from 'lodash'; import { resolve } from 'path'; import { type Actionable, type Config } from './Config'; -import { readConfigFile, writeConfigFile } from './configFile'; +import { readConfig, writeConfig } from './configFile'; import { type ConsoleParams } from './ConsoleParams'; import { getErrorMessage } from './getErrorMessage'; import { pkgDir } from './pkgDir'; @@ -33,7 +33,7 @@ export const updateConfig = async ({ ); // Load & parse config file. - const config = await readConfigFile(configPath); + const config = await readConfig(configPath); // Validate batch. if (!config.batches?.[batch]) { @@ -66,7 +66,7 @@ export const updateConfig = async ({ ); // Write updated config to file. - await writeConfigFile(config, configPath); + await writeConfig(config, configPath); if (stdOut) process.stdout.write(chalk.black.bold(' Done!\n')); } catch (error) { diff --git a/test/metastructure.yml b/test/metastructure.yml index 31a5611..d05836d 100644 --- a/test/metastructure.yml +++ b/test/metastructure.yml @@ -1,29 +1,29 @@ # ******************* DO NOT EDIT THIS NOTICE ***************** # This legal notice is added to every supported source code -# file at every commit. See the README for more info! +# file at every commit. See the README for more info! # ************************************************************* accounts: dev: - id: "339712993254" email: jscroft+karma.003.dev@gmail.com name: Development Account organizational_unit: dev + id: '339712993254' master: - id: "891377150698" + id: '891377150698' email: jscroft+karma.003.master@gmail.com name: Master Account prod: - id: "381491905215" + id: '381491905215' email: jscroft+karma.003.prod@gmail.com name: Production Account organizational_unit: prod test: - id: "381491918621" + id: '381491918621' email: jscroft+karma.003.test@gmail.com name: Testing Account organizational_unit: test shared_services: - id: "975050301644" + id: '975050301644' email: jscroft+karma.003.shared_services@gmail.com name: Shared Services Account organizational_unit: infrastructure @@ -89,14 +89,16 @@ organizational_units: name: Workloads OU terraform: aws_profile: KARMA-INIT - aws_version: ">= 5.56.1" + aws_version: '>= 5.56.1' paths: test roles: admin: TerraformAdmin deployment: TerraformDeployment reader: TerraformReader state_account: shared_services - state_bucket: "{{#if organization.namespace}}{{organization.namespace}}-{{/if}}terraform-state" + state_bucket: '{{#if + organization.namespace}}{{organization.namespace}}-{{/if}}terraform-state' state_key: terraform.tfstate state_lock_table: terraform-state-lock - terraform_version: ">= 1.9.0" + terraform_version: '>= 1.9.0' +