From 3090bf4e4d45eb06d9a44050f6860750dc819f50 Mon Sep 17 00:00:00 2001 From: Olivier Hoareau Date: Sun, 1 Sep 2024 22:50:40 +0200 Subject: [PATCH] chore: fix format --- src/layer-actions/apply.ts | 2 +- src/layer-actions/clean-dirs.ts | 2 +- src/layer-actions/destroy.ts | 2 +- src/layer-actions/get.ts | 2 +- src/layer-actions/index.ts | 34 ++++---- src/layer-actions/init-full-upgrade.ts | 2 +- src/layer-actions/init-full.ts | 2 +- src/layer-actions/init-upgrade.ts | 24 ++++-- src/layer-actions/init.ts | 22 ++++- src/layer-actions/output-json.ts | 27 +++++- src/layer-actions/output.ts | 2 +- src/layer-actions/plan.ts | 2 +- src/layer-actions/providers-lock-delete.ts | 2 +- src/layer-actions/providers-lock.ts | 12 ++- src/layer-actions/refresh.ts | 2 +- src/layer-actions/sync-full.ts | 2 +- src/layer-actions/sync.ts | 54 +++++++++--- src/layer-actions/update.ts | 2 +- src/types.ts | 40 +++++++-- src/utils/buildLayer.ts | 22 +++-- src/utils/buildLayerDepends.ts | 39 ++++----- src/utils/buildLayerRequires.ts | 29 ++++--- src/utils/buildLayers.ts | 36 +++++--- src/utils/buildVarValue.ts | 14 ++- src/utils/buildVars.ts | 50 +++++++---- src/utils/fetchLayer.ts | 14 +-- src/utils/flattenJsonVars.ts | 8 +- src/utils/generateEnvLayerFromFile.ts | 20 +++-- src/utils/generateLayerVars.ts | 7 +- src/utils/generateVarsFromTerraformOutputs.ts | 9 +- src/utils/getDirectories.ts | 7 +- src/utils/mergeEnvConfig.ts | 8 +- src/utils/parseLayerVariableDsn.ts | 3 +- src/utils/rawLogger.ts | 17 +++- src/utils/replaceVars.ts | 17 ++-- src/utils/runLayer.ts | 32 +++++-- src/utils/runLayerCommand.ts | 85 +++++++++++++------ src/utils/runLayers.ts | 85 ++++++++++++------- src/utils/tfgen.ts | 67 ++++++++++----- src/utils/tflayer.ts | 51 ++++++++--- 40 files changed, 596 insertions(+), 261 deletions(-) diff --git a/src/layer-actions/apply.ts b/src/layer-actions/apply.ts index bb42528..1ce1c0a 100644 --- a/src/layer-actions/apply.ts +++ b/src/layer-actions/apply.ts @@ -2,4 +2,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'apply', 'plan.tfplan']); -} +}; diff --git a/src/layer-actions/clean-dirs.ts b/src/layer-actions/clean-dirs.ts index 4e74126..b9fa307 100644 --- a/src/layer-actions/clean-dirs.ts +++ b/src/layer-actions/clean-dirs.ts @@ -2,4 +2,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['rm', '-rf', '.terraform/plugins', '.terraform/providers']); -} +}; diff --git a/src/layer-actions/destroy.ts b/src/layer-actions/destroy.ts index 5ec02cf..8602505 100644 --- a/src/layer-actions/destroy.ts +++ b/src/layer-actions/destroy.ts @@ -3,4 +3,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'plan', '-destroy', '-out', 'destroy.tfplan']); await run(['terraform', 'apply', 'destroy.tfplan']); -} +}; diff --git a/src/layer-actions/get.ts b/src/layer-actions/get.ts index ca5ff7a..5de9cc1 100644 --- a/src/layer-actions/get.ts +++ b/src/layer-actions/get.ts @@ -2,4 +2,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'get']); -} +}; diff --git a/src/layer-actions/index.ts b/src/layer-actions/index.ts index c8f386b..d0978f2 100644 --- a/src/layer-actions/index.ts +++ b/src/layer-actions/index.ts @@ -1,17 +1,17 @@ -export { default as get } from './get'; -export { default as init } from './init'; -export { default as init_full } from './init-full'; -export { default as init_upgrade } from './init-upgrade'; -export { default as init_full_upgrade } from './init-full-upgrade'; -export { default as providers_lock_delete } from './providers-lock-delete'; -export { default as clean_dirs } from './clean-dirs'; -export { default as providers_lock } from './providers-lock'; -export { default as update } from './update'; -export { default as plan } from './plan'; -export { default as refresh } from './refresh'; -export { default as apply } from './apply'; -export { default as output_json } from './output-json'; -export { default as output } from './output'; -export { default as sync } from './sync'; -export { default as sync_full } from './sync-full'; -export { default as destroy } from './destroy'; +export {default as get} from './get'; +export {default as init} from './init'; +export {default as init_full} from './init-full'; +export {default as init_upgrade} from './init-upgrade'; +export {default as init_full_upgrade} from './init-full-upgrade'; +export {default as providers_lock_delete} from './providers-lock-delete'; +export {default as clean_dirs} from './clean-dirs'; +export {default as providers_lock} from './providers-lock'; +export {default as update} from './update'; +export {default as plan} from './plan'; +export {default as refresh} from './refresh'; +export {default as apply} from './apply'; +export {default as output_json} from './output-json'; +export {default as output} from './output'; +export {default as sync} from './sync'; +export {default as sync_full} from './sync-full'; +export {default as destroy} from './destroy'; diff --git a/src/layer-actions/init-full-upgrade.ts b/src/layer-actions/init-full-upgrade.ts index 201d55d..0fd11f3 100644 --- a/src/layer-actions/init-full-upgrade.ts +++ b/src/layer-actions/init-full-upgrade.ts @@ -2,4 +2,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'init', '-upgrade=true']); -} +}; diff --git a/src/layer-actions/init-full.ts b/src/layer-actions/init-full.ts index 588af5e..fd01565 100644 --- a/src/layer-actions/init-full.ts +++ b/src/layer-actions/init-full.ts @@ -2,4 +2,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'init']); -} +}; diff --git a/src/layer-actions/init-upgrade.ts b/src/layer-actions/init-upgrade.ts index 8429893..56e94bb 100644 --- a/src/layer-actions/init-upgrade.ts +++ b/src/layer-actions/init-upgrade.ts @@ -2,14 +2,28 @@ import {layer_run, loggable, logger_factory, raw_logger} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'init', '-upgrade=true'], createLogger); -} +}; -const createLogger:logger_factory = (rawLogger: raw_logger) => { +const createLogger: logger_factory = (rawLogger: raw_logger) => { let messagesBuffer: (loggable | string)[] = []; - return ({ group, type, data, error }: { group :string; type: string, data: unknown; error?: boolean }) => { + return ({ + group, + type, + data, + error, + }: { + group: string; + type: string; + data: unknown; + error?: boolean; + }) => { switch (type) { case 'starting': - rawLogger({group, type: 'message', data: 'Re-initializing terraform workspace...'}); + rawLogger({ + group, + type: 'message', + data: 'Re-initializing terraform workspace...', + }); break; case 'message': messagesBuffer.push({group, type, data, error}); @@ -22,4 +36,4 @@ const createLogger:logger_factory = (rawLogger: raw_logger) => { break; } }; -} +}; diff --git a/src/layer-actions/init.ts b/src/layer-actions/init.ts index 003b868..928649c 100644 --- a/src/layer-actions/init.ts +++ b/src/layer-actions/init.ts @@ -2,13 +2,27 @@ import {layer_run, loggable, raw_logger} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'init'], createLogger); -} +}; const createLogger = (rawLogger: raw_logger) => { let messagesBuffer: (loggable | string)[] = []; - return ({ group, type, data, error }: { group :string; type: string, data: unknown; error?: boolean }) => { + return ({ + group, + type, + data, + error, + }: { + group: string; + type: string; + data: unknown; + error?: boolean; + }) => { switch (type) { case 'starting': - rawLogger({group, type: 'message', data: 'Re-initializing terraform workspace...'}); + rawLogger({ + group, + type: 'message', + data: 'Re-initializing terraform workspace...', + }); break; case 'message': messagesBuffer.push({group, type, data, error}); @@ -21,4 +35,4 @@ const createLogger = (rawLogger: raw_logger) => { break; } }; -} +}; diff --git a/src/layer-actions/output-json.ts b/src/layer-actions/output-json.ts index c4310af..3591195 100644 --- a/src/layer-actions/output-json.ts +++ b/src/layer-actions/output-json.ts @@ -1,12 +1,26 @@ import {layer_run, loggable, logger_factory, raw_logger} from '../types'; export default async (run: layer_run) => { - await run(['terraform', 'output', '-json', '-no-color'], createLogger, true); -} + await run( + ['terraform', 'output', '-json', '-no-color'], + createLogger, + true, + ); +}; const createLogger: logger_factory = (rawLogger: raw_logger) => { const messagesBuffer: (loggable | string)[] = []; - return ({ group, type, data, error }: { group :string; type: string, data: unknown; error?: boolean }) => { + return ({ + group, + type, + data, + error, + }: { + group: string; + type: string; + data: unknown; + error?: boolean; + }) => { switch (type) { case 'message': if (error) { @@ -16,7 +30,12 @@ const createLogger: logger_factory = (rawLogger: raw_logger) => { } break; case 'completed': - console.log(JSON.stringify({id: group, variables: JSON.parse(messagesBuffer.join("\n"))})); + console.log( + JSON.stringify({ + id: group, + variables: JSON.parse(messagesBuffer.join('\n')), + }), + ); break; default: } diff --git a/src/layer-actions/output.ts b/src/layer-actions/output.ts index 31340c6..791ff5a 100644 --- a/src/layer-actions/output.ts +++ b/src/layer-actions/output.ts @@ -2,4 +2,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'output'], undefined, true); -} +}; diff --git a/src/layer-actions/plan.ts b/src/layer-actions/plan.ts index 8aa0acb..ab23891 100644 --- a/src/layer-actions/plan.ts +++ b/src/layer-actions/plan.ts @@ -2,4 +2,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'plan', '-out', 'plan.tfplan']); -} +}; diff --git a/src/layer-actions/providers-lock-delete.ts b/src/layer-actions/providers-lock-delete.ts index 69197ae..e18cac8 100644 --- a/src/layer-actions/providers-lock-delete.ts +++ b/src/layer-actions/providers-lock-delete.ts @@ -2,4 +2,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['rm', '-f', '.terraform.lock.hcl']); -} +}; diff --git a/src/layer-actions/providers-lock.ts b/src/layer-actions/providers-lock.ts index 57004ca..9910214 100644 --- a/src/layer-actions/providers-lock.ts +++ b/src/layer-actions/providers-lock.ts @@ -1,5 +1,13 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { - await run(['terraform', 'providers', 'lock', '-platform=darwin_amd64', '-platform=linux_amd64', '-platform=windows_amd64', '-platform=darwin_arm64']); -} + await run([ + 'terraform', + 'providers', + 'lock', + '-platform=darwin_amd64', + '-platform=linux_amd64', + '-platform=windows_amd64', + '-platform=darwin_arm64', + ]); +}; diff --git a/src/layer-actions/refresh.ts b/src/layer-actions/refresh.ts index 0d4f91d..1f7d1e2 100644 --- a/src/layer-actions/refresh.ts +++ b/src/layer-actions/refresh.ts @@ -2,4 +2,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'refresh']); -} +}; diff --git a/src/layer-actions/sync-full.ts b/src/layer-actions/sync-full.ts index 44b4bf3..d04db4d 100644 --- a/src/layer-actions/sync-full.ts +++ b/src/layer-actions/sync-full.ts @@ -3,4 +3,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'plan', '-out', 'plan.tfplan']); await run(['terraform', 'apply', 'plan.tfplan']); -} +}; diff --git a/src/layer-actions/sync.ts b/src/layer-actions/sync.ts index 679dec4..fe46cb9 100644 --- a/src/layer-actions/sync.ts +++ b/src/layer-actions/sync.ts @@ -1,24 +1,52 @@ import {layer_run, loggable} from '../types'; -import rawLogger from "../utils/rawLogger"; +import rawLogger from '../utils/rawLogger'; export default async (run: layer_run) => { let needApply: boolean | undefined; const createLogger = () => { let messagesBuffer: (loggable | string)[] = []; - return ({ group, type, data, error }: { group :string; type: string, data: unknown; error?: boolean }) => { + return ({ + group, + type, + data, + error, + }: { + group: string; + type: string; + data: unknown; + error?: boolean; + }) => { switch (type) { case 'starting': - rawLogger({group, type: 'message', data: 'Planning changes...'}); + rawLogger({ + group, + type: 'message', + data: 'Planning changes...', + }); break; case 'message': if (undefined === needApply) { - if (/ 0 to add, 0 to change, 0 to destroy/.test(data as string)) { + if ( + / 0 to add, 0 to change, 0 to destroy/.test( + data as string, + ) + ) { needApply = false; - } else if (/To perform exactly these actions, run the following command to apply/.test(data as string)) { + } else if ( + /To perform exactly these actions, run the following command to apply/.test( + data as string, + ) + ) { needApply = true; - messagesBuffer.forEach(m => rawLogger(m as loggable)); + messagesBuffer.forEach(m => + rawLogger(m as loggable), + ); messagesBuffer = []; - } else if (/No changes. Infrastructure is up-to-date./.test(data as string)) { + } else if ( + /No changes. Infrastructure is up-to-date./.test( + data as string, + ) + ) { needApply = false; } } @@ -35,7 +63,11 @@ export default async (run: layer_run) => { break; case 'completed': if (!needApply) { - rawLogger({group, type: 'message', data: 'No changes detected, skipping.'}) + rawLogger({ + group, + type: 'message', + data: 'No changes detected, skipping.', + }); } else { messagesBuffer.forEach(m => rawLogger(m as loggable)); messagesBuffer = []; @@ -43,7 +75,7 @@ export default async (run: layer_run) => { break; } }; - } + }; await run(['terraform', 'plan', '-out', 'plan.tfplan'], createLogger); - needApply && await run(['terraform', 'apply', 'plan.tfplan']); -} + needApply && (await run(['terraform', 'apply', 'plan.tfplan'])); +}; diff --git a/src/layer-actions/update.ts b/src/layer-actions/update.ts index c23026b..3e4e6a3 100644 --- a/src/layer-actions/update.ts +++ b/src/layer-actions/update.ts @@ -2,4 +2,4 @@ import {layer_run} from '../types'; export default async (run: layer_run) => { await run(['terraform', 'get', '-update']); -} +}; diff --git a/src/types.ts b/src/types.ts index b7592a3..58beae1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,17 +18,45 @@ export type layer_config = { }; export type loggable = { - group :string; - type: string, + group: string; + type: string; data: unknown; error?: boolean; }; -export type raw_logger = ({group, type, data, error}: { group: string; type: string; data: unknown; error?: boolean }) => void; -export type logger = ({group, type, data, error}: { group: string; type: string; data: unknown; error?: boolean }) => void; +export type raw_logger = ({ + group, + type, + data, + error, +}: { + group: string; + type: string; + data: unknown; + error?: boolean; +}) => void; +export type logger = ({ + group, + type, + data, + error, +}: { + group: string; + type: string; + data: unknown; + error?: boolean; +}) => void; export type logger_factory = (rawLogger: raw_logger) => logger; -export type layer_run = (args: string[], loggerFactory?: logger_factory, silent?: boolean) => Promise; +export type layer_run = ( + args: string[], + loggerFactory?: logger_factory, + silent?: boolean, +) => Promise; export type layer_action = (run: layer_run, layer: layer) => Promise; -export type fetch_layer = (root: string, env: string, name: string) => Promise<[ Record, string ]>; +export type fetch_layer = ( + root: string, + env: string, + name: string, +) => Promise<[Record, string]>; diff --git a/src/utils/buildLayer.ts b/src/utils/buildLayer.ts index 4e91e06..3ba8bf8 100644 --- a/src/utils/buildLayer.ts +++ b/src/utils/buildLayer.ts @@ -1,16 +1,24 @@ -import {fetch_layer, layer} from "../types"; +import {fetch_layer, layer} from '../types'; -export const buildLayer = async (root: string, env: string, name: string, fetchLayer: fetch_layer): Promise => { - const [ paths, s] = await fetchLayer(root, env, name); +export const buildLayer = async ( + root: string, + env: string, + name: string, + fetchLayer: fetch_layer, +): Promise => { + const [paths, s] = await fetchLayer(root, env, name); const requires: string[] = []; let array1: RegExpExecArray | null; - const pattern = new RegExp('data "terraform_remote_state" "([^"]+)" {', 'g'); - let v: string|undefined; - while ((array1 = pattern.exec(s)) as unknown !== null) { + const pattern = new RegExp( + 'data "terraform_remote_state" "([^"]+)" {', + 'g', + ); + let v: string | undefined; + while (((array1 = pattern.exec(s)) as unknown) !== null) { v = array1?.[1]?.replace(/_/g, '-'); v && requires.push(v); } - return { name, path: paths['.'], requires, paths, depends: [] }; + return {name, path: paths['.'], requires, paths, depends: []}; }; export default buildLayer; diff --git a/src/utils/buildLayerDepends.ts b/src/utils/buildLayerDepends.ts index d8e63ed..a68cd8d 100644 --- a/src/utils/buildLayerDepends.ts +++ b/src/utils/buildLayerDepends.ts @@ -1,29 +1,30 @@ -import {layer} from "../types"; +import {layer} from '../types'; export const buildLayerDepends = async (layer: layer, layers: layer[]) => Object.keys( - (await Promise.all( - Object.values(layers).reduce( - (acc: layer[], l: layer) => { - if (l.requires.find((r: string) => r === layer.name)) { - acc.push(l); - } - return acc; - }, - [] as layer[] - ).map( - async (l: layer): Promise => { - return [...await buildLayerDepends(l, layers), l.name]; - } + ( + await Promise.all( + Object.values(layers) + .reduce((acc: layer[], l: layer) => { + if (l.requires.find((r: string) => r === layer.name)) { + acc.push(l); + } + return acc; + }, [] as layer[]) + .map(async (l: layer): Promise => { + return [ + ...(await buildLayerDepends(l, layers)), + l.name, + ]; + }), ) - )).reduce( + ).reduce( (acc: Record, d: string[] = []) => { d.forEach(dd => !acc[dd] && (acc[dd] = true)); return acc; }, - {} as Record - ) - ) -; + {} as Record, + ), + ); export default buildLayerDepends; diff --git a/src/utils/buildLayerRequires.ts b/src/utils/buildLayerRequires.ts index 5602441..6069e12 100644 --- a/src/utils/buildLayerRequires.ts +++ b/src/utils/buildLayerRequires.ts @@ -1,16 +1,22 @@ -import {layer} from "../types"; +import {layer} from '../types'; -export const buildLayerRequires = async (layer: layer, layers: layer[]): Promise => +export const buildLayerRequires = async ( + layer: layer, + layers: layer[], +): Promise => Object.keys( - (await Promise.all( - layer.requires.map( - async (r: string) => { + ( + await Promise.all( + layer.requires.map(async (r: string) => { const lll = layers.find((ll: layer) => ll.name === r); if (!lll) return [] as string[]; - return [...await buildLayerRequires(lll, layers), lll.name] as string[]; - } + return [ + ...(await buildLayerRequires(lll, layers)), + lll.name, + ] as string[]; + }), ) - )).reduce( + ).reduce( (acc: Record, rrr: string[]) => { rrr.forEach((rrrr: string) => { if (!acc[rrrr]) { @@ -19,9 +25,8 @@ export const buildLayerRequires = async (layer: layer, layers: layer[]): Promise }); return acc; }, - {} as Record - ) - ) -; + {} as Record, + ), + ); export default buildLayerRequires; diff --git a/src/utils/buildLayers.ts b/src/utils/buildLayers.ts index 14a63c3..7597908 100644 --- a/src/utils/buildLayers.ts +++ b/src/utils/buildLayers.ts @@ -1,16 +1,26 @@ -import getDirectories from "./getDirectories"; -import buildLayer from "./buildLayer"; -import buildLayerRequires from "./buildLayerRequires"; -import buildLayerDepends from "./buildLayerDepends"; -import {fetch_layer, layer} from "../types"; +import getDirectories from './getDirectories'; +import buildLayer from './buildLayer'; +import buildLayerRequires from './buildLayerRequires'; +import buildLayerDepends from './buildLayerDepends'; +import {fetch_layer, layer} from '../types'; -export const buildLayers = async (root: string, env: string, fetchLayer: fetch_layer) => { - const layers = await Promise.all(getDirectories(`${root}/${env}`).map(async (l: string) => buildLayer(root, env, l, fetchLayer))); - const sorted = (await Promise.all(layers.map(async l => { - const requires = await buildLayerRequires(l, layers); - const depends = await buildLayerDepends(l, layers); - return ({...l, requires, depends}); - }))); +export const buildLayers = async ( + root: string, + env: string, + fetchLayer: fetch_layer, +) => { + const layers = await Promise.all( + getDirectories(`${root}/${env}`).map(async (l: string) => + buildLayer(root, env, l, fetchLayer), + ), + ); + const sorted = await Promise.all( + layers.map(async l => { + const requires = await buildLayerRequires(l, layers); + const depends = await buildLayerDepends(l, layers); + return {...l, requires, depends}; + }), + ); sorted.sort((a, b) => { let r: number = 0; if (a.requires.find((x: string) => b.name === x)) { @@ -20,7 +30,7 @@ export const buildLayers = async (root: string, env: string, fetchLayer: fetch_l } else if (a.requires.length > b.requires.length) { r = 1; } else if (a.requires.length < b.requires.length) { - r = -1 + r = -1; } return r; }); diff --git a/src/utils/buildVarValue.ts b/src/utils/buildVarValue.ts index 90fb7bd..3b8dcc4 100644 --- a/src/utils/buildVarValue.ts +++ b/src/utils/buildVarValue.ts @@ -1,6 +1,12 @@ -export const buildVarValue = (s: string, d: string, ss: Record>) => { - const v = (('_' === s) ? `${d}` : (ss[s] ? (ss[s][d] ? `${ss[s][d]}` : '') : '')).trim(); - return ((0 <= v.indexOf(' ')) || (0 <= v.indexOf(';'))) ? `"${v}"` : v; -} +export const buildVarValue = ( + s: string, + d: string, + ss: Record>, +) => { + const v = ( + '_' === s ? `${d}` : ss[s] ? (ss[s][d] ? `${ss[s][d]}` : '') : '' + ).trim(); + return 0 <= v.indexOf(' ') || 0 <= v.indexOf(';') ? `"${v}"` : v; +}; export default buildVarValue; diff --git a/src/utils/buildVars.ts b/src/utils/buildVars.ts index 2d07bba..da37983 100644 --- a/src/utils/buildVars.ts +++ b/src/utils/buildVars.ts @@ -1,31 +1,47 @@ -import {readFileSync} from "fs"; -import parseLayerVariableDsn from "./parseLayerVariableDsn"; -import flattenJsonVars from "./flattenJsonVars"; -import buildVarKey from "./buildVarKey"; -import buildVarValue from "./buildVarValue"; +import {readFileSync} from 'fs'; +import parseLayerVariableDsn from './parseLayerVariableDsn'; +import flattenJsonVars from './flattenJsonVars'; +import buildVarKey from './buildVarKey'; +import buildVarValue from './buildVarValue'; export const buildVars = (vars: Record, repo: string) => { - const [layers, variables]: [Record, Record] = Object.keys(vars).reduce((acc, k) => { - const [ layer, varName ] = parseLayerVariableDsn(vars[k]); - acc[0][layer] = true; - acc[1][k] = [layer, varName] as [ string, string ]; - return acc; - }, [{}, {}] as [ Record, Record ]); + const [layers, variables]: [ + Record, + Record, + ] = Object.keys(vars).reduce( + (acc, k) => { + const [layer, varName] = parseLayerVariableDsn(vars[k]); + acc[0][layer] = true; + acc[1][k] = [layer, varName] as [string, string]; + return acc; + }, + [{}, {}] as [Record, Record], + ); const layerNames = Object.keys(layers); layerNames.sort(); const sources: Record> = {}; layerNames.forEach(l => { try { - sources[l] = flattenJsonVars(JSON.parse(readFileSync(`${repo}/${l}.json`, 'utf8'))) || {}; + sources[l] = + flattenJsonVars( + JSON.parse(readFileSync(`${repo}/${l}.json`, 'utf8')), + ) || {}; } catch (_) { sources[l] = {}; } }); - return Object.keys(variables).reduce((acc, k) => { - const [source, data] = variables[k]; - acc.push(`${buildVarKey(k)}=${buildVarValue(source, data, sources)}`); - return acc; - }, []).join("\n"); + return Object.keys(variables) + .reduce( + (acc, k) => { + const [source, data] = variables[k]; + acc.push( + `${buildVarKey(k)}=${buildVarValue(source, data, sources)}`, + ); + return acc; + }, + [], + ) + .join('\n'); }; export default buildVars; diff --git a/src/utils/fetchLayer.ts b/src/utils/fetchLayer.ts index b287f5f..1adb41e 100644 --- a/src/utils/fetchLayer.ts +++ b/src/utils/fetchLayer.ts @@ -1,8 +1,12 @@ -import {existsSync, readFileSync} from "fs"; -import {resolve} from "path"; -import {fetch_layer} from "../types"; +import {existsSync, readFileSync} from 'fs'; +import {resolve} from 'path'; +import {fetch_layer} from '../types'; -export const fetchLayer: fetch_layer = async (root: string, env: string, name: string): Promise<[ Record, string ]> => { +export const fetchLayer: fetch_layer = async ( + root: string, + env: string, + name: string, +): Promise<[Record, string]> => { const dir = `${root}/${env}/${name}`; if (!existsSync(dir)) throw new Error(`Unknown layer '${name}'`); const paths: Record = { @@ -12,7 +16,7 @@ export const fetchLayer: fetch_layer = async (root: string, env: string, name: s 'variables.tf': resolve(`${dir}/variables.tf`), }; const s = readFileSync(paths['main.tf'], 'utf8'); - return [ paths, s ]; + return [paths, s]; }; export default fetchLayer; diff --git a/src/utils/flattenJsonVars.ts b/src/utils/flattenJsonVars.ts index 2ae3b0f..21902e5 100644 --- a/src/utils/flattenJsonVars.ts +++ b/src/utils/flattenJsonVars.ts @@ -1,3 +1,9 @@ -export const flattenJsonVars = (v: { variables: Record }) => Object.keys(v.variables || {}).reduce((acc, k) => Object.assign(acc, {[k]: v.variables[k].value}), {}); +export const flattenJsonVars = (v: { + variables: Record; +}) => + Object.keys(v.variables || {}).reduce( + (acc, k) => Object.assign(acc, {[k]: v.variables[k].value}), + {}, + ); export default flattenJsonVars; diff --git a/src/utils/generateEnvLayerFromFile.ts b/src/utils/generateEnvLayerFromFile.ts index 9e79366..f54263c 100644 --- a/src/utils/generateEnvLayerFromFile.ts +++ b/src/utils/generateEnvLayerFromFile.ts @@ -1,11 +1,21 @@ -import {dirname} from "path"; -import {existsSync, mkdirSync, readFileSync, writeFileSync} from "fs"; -import replaceVars from "./replaceVars"; +import {dirname} from 'path'; +import {existsSync, mkdirSync, readFileSync, writeFileSync} from 'fs'; +import replaceVars from './replaceVars'; -export const generateEnvLayerFromFile = async (sourceFile: string, targetFile: string, vars: Record) => { +export const generateEnvLayerFromFile = async ( + sourceFile: string, + targetFile: string, + vars: Record, +) => { const parentDir = dirname(targetFile); existsSync(parentDir) || mkdirSync(parentDir, {recursive: true}); - writeFileSync(targetFile, replaceVars(readFileSync(sourceFile, 'utf8') as string, vars) as unknown as string); + writeFileSync( + targetFile, + replaceVars( + readFileSync(sourceFile, 'utf8') as string, + vars, + ) as unknown as string, + ); }; export default generateEnvLayerFromFile; diff --git a/src/utils/generateLayerVars.ts b/src/utils/generateLayerVars.ts index 61ae0ee..147b123 100644 --- a/src/utils/generateLayerVars.ts +++ b/src/utils/generateLayerVars.ts @@ -1,5 +1,8 @@ -import replaceVars from "./replaceVars"; +import replaceVars from './replaceVars'; -export const generateLayerVars = (vars: unknown, layer: Record) => replaceVars(vars, layer) as Record; +export const generateLayerVars = ( + vars: unknown, + layer: Record, +) => replaceVars(vars, layer) as Record; export default generateLayerVars; diff --git a/src/utils/generateVarsFromTerraformOutputs.ts b/src/utils/generateVarsFromTerraformOutputs.ts index 4ba4582..76d4ccc 100644 --- a/src/utils/generateVarsFromTerraformOutputs.ts +++ b/src/utils/generateVarsFromTerraformOutputs.ts @@ -1,7 +1,10 @@ -import {existsSync, readFileSync} from "fs"; -import buildVars from "./buildVars"; +import {existsSync, readFileSync} from 'fs'; +import buildVars from './buildVars'; -export const generateVarsFromTerraformOutputs = (configFile: string, repo: string) => { +export const generateVarsFromTerraformOutputs = ( + configFile: string, + repo: string, +) => { if (!existsSync(configFile)) return ''; const config = JSON.parse(readFileSync(configFile, 'utf-8')); return buildVars(config, repo); diff --git a/src/utils/getDirectories.ts b/src/utils/getDirectories.ts index 630b272..9c39dea 100644 --- a/src/utils/getDirectories.ts +++ b/src/utils/getDirectories.ts @@ -1,9 +1,8 @@ -import {readdirSync} from "fs"; +import {readdirSync} from 'fs'; export const getDirectories = (source: string) => - readdirSync(source, { withFileTypes: true }) + readdirSync(source, {withFileTypes: true}) .filter(dirent => dirent.isDirectory()) - .map(dirent => dirent.name) -; + .map(dirent => dirent.name); export default getDirectories; diff --git a/src/utils/mergeEnvConfig.ts b/src/utils/mergeEnvConfig.ts index a72a0ed..98be81e 100644 --- a/src/utils/mergeEnvConfig.ts +++ b/src/utils/mergeEnvConfig.ts @@ -1,5 +1,9 @@ -import {config} from "../types"; +import {config} from '../types'; -export const mergeEnvConfig = (config: config, name: string) => ({...config.common, ...config.environments[name], env: name}); +export const mergeEnvConfig = (config: config, name: string) => ({ + ...config.common, + ...config.environments[name], + env: name, +}); export default mergeEnvConfig; diff --git a/src/utils/parseLayerVariableDsn.ts b/src/utils/parseLayerVariableDsn.ts index dc4dad5..403c358 100644 --- a/src/utils/parseLayerVariableDsn.ts +++ b/src/utils/parseLayerVariableDsn.ts @@ -1,3 +1,4 @@ -export const parseLayerVariableDsn = (d: string) => (!/^@[^:]+:.+$/.test(d)) ? ['_', d] : d.slice(1).split(/:/).slice(0, 2); +export const parseLayerVariableDsn = (d: string) => + !/^@[^:]+:.+$/.test(d) ? ['_', d] : d.slice(1).split(/:/).slice(0, 2); export default parseLayerVariableDsn; diff --git a/src/utils/rawLogger.ts b/src/utils/rawLogger.ts index fd089c0..3702943 100644 --- a/src/utils/rawLogger.ts +++ b/src/utils/rawLogger.ts @@ -1,7 +1,18 @@ -import {raw_logger} from "../types"; +import {raw_logger} from '../types'; -export const rawLogger:raw_logger = ({group, type, data, error = false}: { group: string; type: string; data: unknown; error?: boolean }) => { - type === 'message' && console[error ? 'error' : 'log'] || ((..._: unknown[]) => {})(`[${group}] ${data}`); +export const rawLogger: raw_logger = ({ + group, + type, + data, + error = false, +}: { + group: string; + type: string; + data: unknown; + error?: boolean; +}) => { + (type === 'message' && console[error ? 'error' : 'log']) || + ((..._: unknown[]) => {})(`[${group}] ${data}`); }; export default rawLogger; diff --git a/src/utils/replaceVars.ts b/src/utils/replaceVars.ts index 06223ed..633b200 100644 --- a/src/utils/replaceVars.ts +++ b/src/utils/replaceVars.ts @@ -2,15 +2,20 @@ import Handlebars from 'handlebars'; Handlebars.registerHelper('slugify', s => s.replace(/[^a-z0-9_]+/g, '-')); export const replaceVars = (a: unknown, b: Record) => { - if ((Array.isArray(a))) { - a.forEach((v, i) => {a[i] = replaceVars(v, b);}); + if (Array.isArray(a)) { + a.forEach((v, i) => { + a[i] = replaceVars(v, b); + }); return a; } if ('object' === typeof a) { - return Object.entries(a as object).reduce((acc, [k, v]) => { - acc[k] = replaceVars(v, b); - return acc; - }, a as Record); + return Object.entries(a as object).reduce( + (acc, [k, v]) => { + acc[k] = replaceVars(v, b); + return acc; + }, + a as Record, + ); } return Handlebars.compile(a)(b); }; diff --git a/src/utils/runLayer.ts b/src/utils/runLayer.ts index 2ee0d35..717f315 100644 --- a/src/utils/runLayer.ts +++ b/src/utils/runLayer.ts @@ -1,16 +1,32 @@ -import {layer, layer_action, logger_factory} from "../types"; -import * as layerActions from "../layer-actions"; -import lrun from "./runLayerCommand"; -import rawLogger from "./rawLogger"; +import {layer, layer_action, logger_factory} from '../types'; +import * as layerActions from '../layer-actions'; +import lrun from './runLayerCommand'; +import rawLogger from './rawLogger'; export const runLayer = async (layer: layer, action: string) => { - const a: layer_action = (layerActions as Record)[action?.replace(/-/g, '_') || '']; + const a: layer_action = (layerActions as Record)[ + action?.replace(/-/g, '_') || '' + ]; if (!a) throw new Error(`Unsupported layer action '${action}'`); await a( - async (args: string[], loggerFactory?: logger_factory, silent?: boolean) => { - await lrun(layer, {...(loggerFactory ? {logger: loggerFactory(rawLogger)} : {}), ...(silent ? {silent} : {})}, args[0], ...args.slice(1)); + async ( + args: string[], + loggerFactory?: logger_factory, + silent?: boolean, + ) => { + await lrun( + layer, + { + ...(loggerFactory + ? {logger: loggerFactory(rawLogger)} + : {}), + ...(silent ? {silent} : {}), + }, + args[0], + ...args.slice(1), + ); }, - layer + layer, ); }; diff --git a/src/utils/runLayerCommand.ts b/src/utils/runLayerCommand.ts index 570ecb9..1d6d3be 100644 --- a/src/utils/runLayerCommand.ts +++ b/src/utils/runLayerCommand.ts @@ -1,44 +1,81 @@ -import rawLogger from "./rawLogger"; -import {spawn} from "child_process"; -import {layer} from "../types"; +import rawLogger from './rawLogger'; +import {spawn} from 'child_process'; +import {layer} from '../types'; -export const runLayerCommand = async ({ name, path }: layer, {logger = rawLogger, silent = false}, cmd: string, ...args: string[]) => +export const runLayerCommand = async ( + {name, path}: layer, + {logger = rawLogger, silent = false}, + cmd: string, + ...args: string[] +) => new Promise((resolve, reject) => { const p = spawn(cmd, args, {cwd: path}); logger({group: name, type: 'starting', data: {cmd, args, path}}); - p.stdout.on('data', (data) => { - data.toString().replace(/(\r\n|\n)$/, '').split(/\r\n/).forEach((s: string) => { - s.split(/\n/).forEach((ss: string) => { - logger({group: name, type: 'message', data: ss}); + p.stdout.on('data', data => { + data.toString() + .replace(/(\r\n|\n)$/, '') + .split(/\r\n/) + .forEach((s: string) => { + s.split(/\n/).forEach((ss: string) => { + logger({group: name, type: 'message', data: ss}); + }); }); - }); }); - p.stderr.on('data', (data) => { - data.toString().replace(/(\r\n|\n)$/, '').split(/\r\n/).forEach((s: string) => { - s.split(/\n/).forEach((ss: string) => { - logger({group: name, type: 'message', data: ss, error: true}); + p.stderr.on('data', data => { + data.toString() + .replace(/(\r\n|\n)$/, '') + .split(/\r\n/) + .forEach((s: string) => { + s.split(/\n/).forEach((ss: string) => { + logger({ + group: name, + type: 'message', + data: ss, + error: true, + }); + }); }); - }); }); - p.on('error', (code) => { - logger({group: name, type: 'not-launched', data: {cmd, args, code, path}}); - throw new Error(`Failed to execute ${cmd} ${args.join(' ')} in ${path} (layer: ${name}, exit-code: ${code})`); + p.on('error', code => { + logger({ + group: name, + type: 'not-launched', + data: {cmd, args, code, path}, + }); + throw new Error( + `Failed to execute ${cmd} ${args.join(' ')} in ${path} (layer: ${name}, exit-code: ${code})`, + ); }); - p.on('close', (code) => { + p.on('close', code => { if (0 === code) { - logger({group: name, type: 'completed', data: {cmd, args, code, path}}); + logger({ + group: name, + type: 'completed', + data: {cmd, args, code, path}, + }); resolve(code); } else { if (silent) { - logger({group: name, type: 'completed', data: {cmd, args, code, path}}); + logger({ + group: name, + type: 'completed', + data: {cmd, args, code, path}, + }); resolve(code); } else { - logger({group: name, type: 'aborted', data: {cmd, args, code, path}}); - reject(new Error(`Layer '${name}' command [${cmd} ${args.join(' ')}] exited with code [${code}] (cwd: ${path})`)); + logger({ + group: name, + type: 'aborted', + data: {cmd, args, code, path}, + }); + reject( + new Error( + `Layer '${name}' command [${cmd} ${args.join(' ')}] exited with code [${code}] (cwd: ${path})`, + ), + ); } } }); - }) -; + }); export default runLayerCommand; diff --git a/src/utils/runLayers.ts b/src/utils/runLayers.ts index ce3f304..8a96f0c 100644 --- a/src/utils/runLayers.ts +++ b/src/utils/runLayers.ts @@ -1,8 +1,11 @@ -import runLayer from "./runLayer"; -import buildLayers from "./buildLayers"; -import {fetch_layer} from "../types"; +import runLayer from './runLayer'; +import buildLayers from './buildLayers'; +import {fetch_layer} from '../types'; -const actions: Record = { +const actions: Record< + string, + {executeDepends?: boolean; executeRequires?: boolean} +> = { init: {executeDepends: false}, get: {executeDepends: false}, update: {executeDepends: false}, @@ -13,47 +16,63 @@ const actions: Record = {}, opts: { transitive?: boolean } = {}) => { +export const runLayers = async ( + root: string, + env: string, + layerNames: string[], + action: string, + fetchLayer: fetch_layer, + _: Record = {}, + opts: {transitive?: boolean} = {}, +) => { const layers = await buildLayers(root, env, fetchLayer); const allMode = !!layerNames.find(n => n === 'all'); layerNames = allMode ? layers.map(l => l.name) : layerNames; layerNames = layerNames.reduce((acc, ln) => { if (/\*/.test(ln)) { const lnr = new RegExp(ln.replace(/\*/, '[^/]+')); - acc = [...acc, ...layers.filter(l => lnr.test(l.name)).map(l => l.name)]; + acc = [ + ...acc, + ...layers.filter(l => lnr.test(l.name)).map(l => l.name), + ]; } else { acc.push(ln); } return acc; }, [] as string[]); const done: Record = {}; - await layers.filter(l => layerNames.find(n => n === l.name)).reduce(async (p, l) => { - await p; - if (opts.transitive && actions[action].executeRequires) { - await l.requires.reduce(async (p: Promise, ll: string) => { - await p; - if (!done[ll]) { - const lll = layers.find(x => ll === x.name); - lll && await runLayer(lll, action); - done[ll] = true; - } - }, Promise.resolve()); - } - if (!done[l.name]) { - await runLayer(l, action); - done[l.name] = true; - } - if (opts.transitive && !allMode && actions[action].executeDepends) { - await l.depends.reduce(async (p, ll) => { - await p; - if (!done[ll]) { - const lll = layers.find(x => ll === x.name); - lll && await runLayer(lll, action); - done[ll] = true; - } - }, Promise.resolve()); - } - }, Promise.resolve()); + await layers + .filter(l => layerNames.find(n => n === l.name)) + .reduce(async (p, l) => { + await p; + if (opts.transitive && actions[action].executeRequires) { + await l.requires.reduce( + async (p: Promise, ll: string) => { + await p; + if (!done[ll]) { + const lll = layers.find(x => ll === x.name); + lll && (await runLayer(lll, action)); + done[ll] = true; + } + }, + Promise.resolve(), + ); + } + if (!done[l.name]) { + await runLayer(l, action); + done[l.name] = true; + } + if (opts.transitive && !allMode && actions[action].executeDepends) { + await l.depends.reduce(async (p, ll) => { + await p; + if (!done[ll]) { + const lll = layers.find(x => ll === x.name); + lll && (await runLayer(lll, action)); + done[ll] = true; + } + }, Promise.resolve()); + } + }, Promise.resolve()); }; export default runLayers; diff --git a/src/utils/tfgen.ts b/src/utils/tfgen.ts index a980434..cbca9b0 100644 --- a/src/utils/tfgen.ts +++ b/src/utils/tfgen.ts @@ -1,25 +1,52 @@ -import {readdirSync} from "fs"; -import {resolve} from "path"; -import mergeEnvConfig from "./mergeEnvConfig"; -import generateEnvLayerFromFile from "./generateEnvLayerFromFile"; -import generateLayerVars from "./generateLayerVars"; -import {config} from "../types"; +import {readdirSync} from 'fs'; +import {resolve} from 'path'; +import mergeEnvConfig from './mergeEnvConfig'; +import generateEnvLayerFromFile from './generateEnvLayerFromFile'; +import generateLayerVars from './generateLayerVars'; +import {config} from '../types'; -export const tfgen = async (configFile: string, sourceDir: string, targetDir: string) => { - const layers = readdirSync(sourceDir, {withFileTypes: true}).filter(e => !e.isDirectory() && /.tmpl.tf$/.test(e.name)).map(e => ({name: e.name.replace(/\.tmpl\.tf$/, ''), file: e.name, filePath: `${sourceDir}/${e.name}`})); - const config: config = await import (resolve(configFile)); - await Promise.all(Object.keys(config.environments || {}).map(async (name: string) => { - const env = mergeEnvConfig(config, name); - await Promise.all(layers.map(async ({name: layer, file, filePath}) => { - const layerEnv = {...env, env: name, layer: layer}; - const layerConfig = ((config && config.layers && config.layers[layer]) || {}); - if (layerConfig) { - if (layerConfig.only_on_envs && !layerConfig.only_on_envs.includes(name)) return; - if (layerConfig.not_on_envs && layerConfig.not_on_envs.includes(name)) return; - } - await generateEnvLayerFromFile(filePath, `${targetDir}/${name}/${file.replace(/\.tmpl\.tf$/, '')}/main.tf`, generateLayerVars(layerEnv, layerEnv)); +export const tfgen = async ( + configFile: string, + sourceDir: string, + targetDir: string, +) => { + const layers = readdirSync(sourceDir, {withFileTypes: true}) + .filter(e => !e.isDirectory() && /.tmpl.tf$/.test(e.name)) + .map(e => ({ + name: e.name.replace(/\.tmpl\.tf$/, ''), + file: e.name, + filePath: `${sourceDir}/${e.name}`, })); - })); + const config: config = await import(resolve(configFile)); + await Promise.all( + Object.keys(config.environments || {}).map(async (name: string) => { + const env = mergeEnvConfig(config, name); + await Promise.all( + layers.map(async ({name: layer, file, filePath}) => { + const layerEnv = {...env, env: name, layer: layer}; + const layerConfig = + (config && config.layers && config.layers[layer]) || {}; + if (layerConfig) { + if ( + layerConfig.only_on_envs && + !layerConfig.only_on_envs.includes(name) + ) + return; + if ( + layerConfig.not_on_envs && + layerConfig.not_on_envs.includes(name) + ) + return; + } + await generateEnvLayerFromFile( + filePath, + `${targetDir}/${name}/${file.replace(/\.tmpl\.tf$/, '')}/main.tf`, + generateLayerVars(layerEnv, layerEnv), + ); + }), + ); + }), + ); }; export default tfgen; diff --git a/src/utils/tflayer.ts b/src/utils/tflayer.ts index 6f52e81..23ddae7 100644 --- a/src/utils/tflayer.ts +++ b/src/utils/tflayer.ts @@ -1,23 +1,52 @@ -import runLayers from "./runLayers"; -import buildLayers from "./buildLayers"; -import {fetch_layer} from "../types"; -import defaultFetchLayer from "./fetchLayer"; +import runLayers from './runLayers'; +import buildLayers from './buildLayers'; +import {fetch_layer} from '../types'; +import defaultFetchLayer from './fetchLayer'; -export const tflayer = ({layerNameString, env, action, actionArgs, targetDir, logger, fetchLayer}: { layerNameString?: string; env: string; action: string; actionArgs?: Record; targetDir: string; logger?: (msg: string) => void; fetchLayer?: fetch_layer }) => { +export const tflayer = ({ + layerNameString, + env, + action, + actionArgs, + targetDir, + logger, + fetchLayer, +}: { + layerNameString?: string; + env: string; + action: string; + actionArgs?: Record; + targetDir: string; + logger?: (msg: string) => void; + fetchLayer?: fetch_layer; +}) => { let layerNames: string[]; const opts = {transitive: false}; if (layerNameString && /\+$/.test(layerNameString)) { layerNames = layerNameString.slice(0, -1).split(/,/); opts.transitive = true; } else { - layerNames = (layerNameString || 'all').split(/,/) + layerNames = (layerNameString || 'all').split(/,/); } fetchLayer = fetchLayer || defaultFetchLayer; - if (action === 'list-layers') return buildLayers(targetDir, env, fetchLayer).then(layers => { - (logger || console.log)(Object.values(layers).map(l => l.name).join("\n")); - return; - }); - return runLayers(targetDir, env, layerNames, action, fetchLayer, actionArgs, opts) + if (action === 'list-layers') + return buildLayers(targetDir, env, fetchLayer).then(layers => { + (logger || console.log)( + Object.values(layers) + .map(l => l.name) + .join('\n'), + ); + return; + }); + return runLayers( + targetDir, + env, + layerNames, + action, + fetchLayer, + actionArgs, + opts, + ); }; export default tflayer;