From 6198bd2c8b6a0c241dc10cd646052203c51b71b2 Mon Sep 17 00:00:00 2001 From: Toby Masters Date: Sun, 15 Feb 2026 21:10:28 +0000 Subject: [PATCH 01/11] Add workflow option to stats command --- bin/cli.js | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/cli.js b/bin/cli.js index 79858a1..bffddc3 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -570,6 +570,7 @@ program .command("stats") .description("Generate an overview with some statistics") .requiredOption("-s, --since , Specify the date which is going to be used to filter the data from (format: YYYY-MM-DD) (mandatory)") + .option("-w, --workflow [handle]", "Optionally filter test statistics by workflow (workflow handle is optional)") .action((options) => { stats.generateOverview(options.since); }); From b134da4080910560dde96147f266454b87b41321 Mon Sep 17 00:00:00 2001 From: Toby Masters Date: Sun, 15 Feb 2026 21:43:09 +0000 Subject: [PATCH 02/11] [241] Create new getWorkflowTemplateSummary function --- bin/cli.js | 4 +-- lib/cli/stats.js | 66 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index bffddc3..d7fe15c 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -570,9 +570,9 @@ program .command("stats") .description("Generate an overview with some statistics") .requiredOption("-s, --since , Specify the date which is going to be used to filter the data from (format: YYYY-MM-DD) (mandatory)") - .option("-w, --workflow [handle]", "Optionally filter test statistics by workflow (workflow handle is optional)") + .option("-w, --workflow [handle], Optionally filter test statistics by workflow (optional)") .action((options) => { - stats.generateOverview(options.since); + stats.generateOverview(options.since, options.workflow); }); // Set/Get FIRM ID diff --git a/lib/cli/stats.js b/lib/cli/stats.js index 0aa4f94..74c472c 100644 --- a/lib/cli/stats.js +++ b/lib/cli/stats.js @@ -6,15 +6,20 @@ const fsUtils = require("../utils/fsUtils"); const yaml = require("yaml"); const { consola } = require("consola"); -async function generateOverview(sinceDate) { +async function generateOverview(sinceDate, workflow) { const TODAY = new Date().toJSON().toString().slice(0, 10); - const templateSummary = await getTemplatesSummary(); - const yamlSummary = await getYamlSummary(sinceDate); - // Terminal - displayOverview(sinceDate, TODAY, templateSummary, yamlSummary); - // File - const row = createRow(sinceDate, TODAY, templateSummary, yamlSummary); - saveOverviewToFile(row); + if (workflow) { + const templateSummary = await getWorkflowTemplateSummary(workflow); + consola.log("Workflow summary test A") + } else { + const templateSummary = await getTemplatesSummary(); + const yamlSummary = await getYamlSummary(sinceDate); + // Terminal + displayOverview(sinceDate, TODAY, templateSummary, yamlSummary); + // File + const row = createRow(sinceDate, TODAY, templateSummary, yamlSummary); + saveOverviewToFile(row); + } } // Return an object with the count of activities by file and by type @@ -255,6 +260,51 @@ async function getTemplatesSummary() { return summary; } +async function getWorkflowTemplateSummary(workflowHandle) { + const summary = { + reconciliations: { + total: 0, + externallyManaged: 0, + externallyManagedPerc: 0, + yamlFiles: 0, + yamlFilesPerc: 0, + unitTests: 0, + yamlFilesWithAtLeastTwoTests: 0, + yamlFilesWithAtLeastTwoTestsPerc: 0, + }, + exportFiles: { + total: 0, + externallyManaged: 0, + externallyManagedPerc: 0, + }, + accountTemplates: { + total: 0, + externallyManaged: 0, + externallyManagedPerc: 0, + yamlFiles: 0, + yamlFilesPerc: 0, + unitTests: 0, + yamlFilesWithAtLeastTwoTests: 0, + yamlFilesWithAtLeastTwoTestsPerc: 0, + }, + all: { + total: 0, + externallyManaged: 0, + externallyManagedPerc: 0, + yamlFiles: 0, + yamlFilesPerc: 0, + unitTests: 0, + yamlFilesWithAtLeastTwoTests: 0, + yamlFilesWithAtLeastTwoTestsPerc: 0, + }, + }; + + // Reconciliations + // Assume no empty reconciliations within the workflow. + + return summary; +} + async function getYamlSummary(sinceDate) { const yamlActivity = await yamlFilesActivity(sinceDate); const summary = { created: 0, updated: 0 }; From 9d71e6de0eff0533d0b6d77ca5b8ddbfac14f6e8 Mon Sep 17 00:00:00 2001 From: Toby Masters Date: Wed, 18 Mar 2026 15:39:41 +0000 Subject: [PATCH 03/11] Get template lists from the workflow --- lib/cli/stats.js | 11 ++++++++++- lib/utils/fsUtils.js | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/cli/stats.js b/lib/cli/stats.js index 74c472c..59a3496 100644 --- a/lib/cli/stats.js +++ b/lib/cli/stats.js @@ -10,7 +10,6 @@ async function generateOverview(sinceDate, workflow) { const TODAY = new Date().toJSON().toString().slice(0, 10); if (workflow) { const templateSummary = await getWorkflowTemplateSummary(workflow); - consola.log("Workflow summary test A") } else { const templateSummary = await getTemplatesSummary(); const yamlSummary = await getYamlSummary(sinceDate); @@ -299,8 +298,18 @@ async function getWorkflowTemplateSummary(workflowHandle) { }, }; + // Fetch workflow + const workflow = await fsUtils.getWorkflow(workflowHandle); + // Reconciliations // Assume no empty reconciliations within the workflow. + const reconciliationsInWorkflow = workflow.templates.reconciliations; + + // Export Files + const exportFilesInWorkflow = workflow.templates.exportFiles; + + // Account Templates + const accountTemplatesInWorkflow = workflow.templates.accountTemplates; return summary; } diff --git a/lib/utils/fsUtils.js b/lib/utils/fsUtils.js index cf4b835..9591ab9 100644 --- a/lib/utils/fsUtils.js +++ b/lib/utils/fsUtils.js @@ -480,6 +480,20 @@ function checkLiquidTestDependencies(targetHandle) { return dependentHandles; } +function getWorkflow(workflowHandle) { + try { + const workflowPath = path.join(process.cwd(), "workflows", `${workflowHandle}.json`); + if (!fs.existsSync(workflowPath)) { + throw new Error(`Workflow "${workflowHandle}" not found`); + } + return JSON.parse(fs.readFileSync(workflowPath).toString()); + } catch (error) { + consola.error(`An error occurred when trying to read the workflow "${workflowHandle}"`); + consola.error(error); + process.exit(1); + } +} + // Recursive option for fs.watch is not available in every OS (e.g. Linux) function recursiveInspectDirectory({ basePath, collection, pathsArray = [], typeCheck = "liquid" }) { collection.forEach((filePath) => { @@ -567,4 +581,5 @@ module.exports = { getTemplateId, setTemplateId, checkLiquidTestDependencies, + getWorkflow, }; From 51fc334e62286e5f8a79a7b0a38eeda08ad3d436 Mon Sep 17 00:00:00 2001 From: Toby Masters Date: Wed, 18 Mar 2026 16:24:18 +0000 Subject: [PATCH 04/11] Add filter for files in countYamlFiles and complete assignments --- lib/cli/stats.js | 56 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/lib/cli/stats.js b/lib/cli/stats.js index 59a3496..35e3713 100644 --- a/lib/cli/stats.js +++ b/lib/cli/stats.js @@ -8,10 +8,8 @@ const { consola } = require("consola"); async function generateOverview(sinceDate, workflow) { const TODAY = new Date().toJSON().toString().slice(0, 10); - if (workflow) { - const templateSummary = await getWorkflowTemplateSummary(workflow); - } else { - const templateSummary = await getTemplatesSummary(); + const templateSummary = workflow ? await getWorkflowTemplateSummary(workflow) : await getTemplatesSummary(); + if (!workflow) { const yamlSummary = await getYamlSummary(sinceDate); // Terminal displayOverview(sinceDate, TODAY, templateSummary, yamlSummary); @@ -78,10 +76,12 @@ async function yamlFilesActivity(sinceDate) { // Count how many YAML files are stored. We base on the presence of a non empty file // Count how many unit tests are stored. We base on the presence of a title for each unit test // Count how many YAML files have at least two unit tests -async function countYamlFiles(templateType) { +async function countYamlFiles(templateType, templatesInWorkflow) { const files = fsUtils.listExistingFiles("yml"); const FOLDER = fsUtils.FOLDERS[templateType]; - const YAML_EXPRESSION = `.*${FOLDER}/.*/tests/.*_liquid_test.*.y(a)?ml`; + const TEMPLATE_PATTERN = templatesInWorkflow ? `(${templatesInWorkflow.join("|")})` : `.*`; + // Issue with counting multiple yml files in the same tests folder? + const YAML_EXPRESSION = `.*${FOLDER}/${TEMPLATE_PATTERN}/tests/.*_liquid_test.*.y(a)?ml`; const re = new RegExp(YAML_EXPRESSION, "g"); let countFiles = 0; let countFilesWithAtLeastTwoTests = 0; @@ -261,6 +261,7 @@ async function getTemplatesSummary() { async function getWorkflowTemplateSummary(workflowHandle) { const summary = { + template_name: "", reconciliations: { total: 0, externallyManaged: 0, @@ -299,18 +300,55 @@ async function getWorkflowTemplateSummary(workflowHandle) { }; // Fetch workflow + // Assume no empty items if present within the workflow. const workflow = await fsUtils.getWorkflow(workflowHandle); + summary.template_name = workflow.name; // Reconciliations - // Assume no empty reconciliations within the workflow. const reconciliationsInWorkflow = workflow.templates.reconciliations; + const reconciliationsExtMan = await listExternallyManagedTemplates("reconciliationText", reconciliationsInWorkflow); + const reconciliationsTests = await countYamlFiles("reconciliationText", reconciliationsInWorkflow); + summary.reconciliations.total = reconciliationsInWorkflow.length; + summary.reconciliations.externallyManaged = reconciliationsExtMan.length; + summary.reconciliations.externallyManagedPerc = percentageRoundTwo(summary.reconciliations.externallyManaged, summary.reconciliations.total); + summary.reconciliations.yamlFiles = reconciliationsTests.files; + summary.reconciliations.yamlFilesPerc = percentageRoundTwo(summary.reconciliations.yamlFiles, summary.reconciliations.total); + summary.reconciliations.unitTests = reconciliationsTests.tests; + summary.reconciliations.yamlFilesWithAtLeastTwoTests = reconciliationsTests.filesWithAtLeastTwoTests; + summary.reconciliations.yamlFilesWithAtLeastTwoTestsPerc = percentageRoundTwo(summary.reconciliations.yamlFilesWithAtLeastTwoTests, summary.reconciliations.total); // Export Files - const exportFilesInWorkflow = workflow.templates.exportFiles; + const exportFilesInWorkflow = workflow.templates.exports; + const exportFilesExtMan = await listExternallyManagedTemplates("exportFile", exportFilesInWorkflow); + summary.exportFiles.total = exportFilesInWorkflow.length; + summary.exportFiles.externallyManaged = exportFilesExtMan.length; + summary.exportFiles.externallyManagedPerc = percentageRoundTwo(summary.exportFiles.externallyManaged, summary.exportFiles.total); // Account Templates - const accountTemplatesInWorkflow = workflow.templates.accountTemplates; + const accountTemplatesInWorkflow = workflow.templates.accounts; + const accountTemplatesExtMan = await listExternallyManagedTemplates("accountTemplate", accountTemplatesInWorkflow); + const accountTemplatesTests = await countYamlFiles("accountTemplate", accountTemplatesInWorkflow); + summary.accountTemplates.total = accountTemplatesInWorkflow.length; + summary.accountTemplates.externallyManaged = accountTemplatesExtMan.length; + summary.accountTemplates.externallyManagedPerc = percentageRoundTwo(summary.accountTemplates.externallyManaged, summary.accountTemplates.total); + summary.accountTemplates.yamlFiles = accountTemplatesTests.files; + summary.accountTemplates.yamlFilesPerc = percentageRoundTwo(summary.accountTemplates.yamlFiles, summary.accountTemplates.total); + summary.accountTemplates.unitTests = accountTemplatesTests.tests; + summary.accountTemplates.yamlFilesWithAtLeastTwoTests = accountTemplatesTests.filesWithAtLeastTwoTests; + summary.accountTemplates.yamlFilesWithAtLeastTwoTestsPerc = percentageRoundTwo(summary.accountTemplates.yamlFilesWithAtLeastTwoTests, summary.accountTemplates.total); + + // All + summary.all.total = summary.reconciliations.total + summary.exportFiles.total + summary.accountTemplates.total; + summary.all.externallyManaged = + summary.reconciliations.externallyManaged + summary.exportFiles.externallyManaged + summary.accountTemplates.externallyManaged; + summary.all.externallyManagedPerc = percentageRoundTwo(summary.all.externallyManaged, summary.all.total); + summary.all.yamlFiles = summary.reconciliations.yamlFiles + summary.accountTemplates.yamlFiles; + summary.all.yamlFilesPerc = percentageRoundTwo(summary.all.yamlFiles, summary.reconciliations.total + summary.accountTemplates.total); + summary.all.unitTests = summary.reconciliations.unitTests + summary.accountTemplates.unitTests; + summary.all.yamlFilesWithAtLeastTwoTests = summary.reconciliations.yamlFilesWithAtLeastTwoTests + summary.accountTemplates.yamlFilesWithAtLeastTwoTests; + summary.all.yamlFilesWithAtLeastTwoTestsPerc = percentageRoundTwo(summary.all.yamlFilesWithAtLeastTwoTests, summary.reconciliations.total + summary.accountTemplates.total); + consola.log("TEST SUMMARY: ", summary); return summary; } From a23a2954356fa6d5bde1a6233350dd21442b9a1e Mon Sep 17 00:00:00 2001 From: Toby Masters Date: Tue, 31 Mar 2026 12:26:18 +0100 Subject: [PATCH 05/11] [241] Update yamlFilesActivity function for workflows --- lib/cli/stats.js | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/lib/cli/stats.js b/lib/cli/stats.js index 35e3713..81d694f 100644 --- a/lib/cli/stats.js +++ b/lib/cli/stats.js @@ -6,22 +6,25 @@ const fsUtils = require("../utils/fsUtils"); const yaml = require("yaml"); const { consola } = require("consola"); -async function generateOverview(sinceDate, workflow) { +async function generateOverview(sinceDate, workflowHandle) { + const workflow = workflowHandle ? await fsUtils.getWorkflow(workflowHandle) : null; const TODAY = new Date().toJSON().toString().slice(0, 10); const templateSummary = workflow ? await getWorkflowTemplateSummary(workflow) : await getTemplatesSummary(); - if (!workflow) { - const yamlSummary = await getYamlSummary(sinceDate); - // Terminal + const yamlSummary = workflow ? await getYamlSummary(sinceDate, workflow) : await getYamlSummary(sinceDate); + // Terminal + if (workflow) { + displayWorkflowOverview(sinceDate, TODAY, templateSummary, yamlSummary); + } else { displayOverview(sinceDate, TODAY, templateSummary, yamlSummary); - // File - const row = createRow(sinceDate, TODAY, templateSummary, yamlSummary); - saveOverviewToFile(row); } + // File + // const row = createRow(sinceDate, TODAY, templateSummary, yamlSummary); + // saveOverviewToFile(row); } // Return an object with the count of activities by file and by type // Type could be: A (added), M (modified), D (deleted) -async function yamlFilesActivity(sinceDate) { +async function yamlFilesActivity(sinceDate, workflow) { const countByType = {}; const filesChanged = exec.execSync(`git whatchanged --since="${sinceDate}" --name-status --pretty="format:"`); if (!filesChanged) { @@ -37,7 +40,9 @@ async function yamlFilesActivity(sinceDate) { } // Files to Search (YAML) - const YAML_EXPRESSION = `.*/.*/tests/.*_liquid_test.*.y(a)?ml`; + const templatesInWorkflow = workflow ? workflow.templates.reconciliations.concat(workflow.templates.accounts) : []; + const TEMPLATE_PATTERN = workflow ? `(${templatesInWorkflow.join("|")})` : `.*`; + const YAML_EXPRESSION = `.*/.${TEMPLATE_PATTERN}/tests/.*_liquid_test.*.y(a)?ml`; const fileTypeRegExp = RegExp(YAML_EXPRESSION, "g"); for (const row of nonEmptyRows) { @@ -259,7 +264,7 @@ async function getTemplatesSummary() { return summary; } -async function getWorkflowTemplateSummary(workflowHandle) { +async function getWorkflowTemplateSummary(workflow) { const summary = { template_name: "", reconciliations: { @@ -301,7 +306,6 @@ async function getWorkflowTemplateSummary(workflowHandle) { // Fetch workflow // Assume no empty items if present within the workflow. - const workflow = await fsUtils.getWorkflow(workflowHandle); summary.template_name = workflow.name; // Reconciliations @@ -348,12 +352,11 @@ async function getWorkflowTemplateSummary(workflowHandle) { summary.all.yamlFilesWithAtLeastTwoTests = summary.reconciliations.yamlFilesWithAtLeastTwoTests + summary.accountTemplates.yamlFilesWithAtLeastTwoTests; summary.all.yamlFilesWithAtLeastTwoTestsPerc = percentageRoundTwo(summary.all.yamlFilesWithAtLeastTwoTests, summary.reconciliations.total + summary.accountTemplates.total); - consola.log("TEST SUMMARY: ", summary); return summary; } -async function getYamlSummary(sinceDate) { - const yamlActivity = await yamlFilesActivity(sinceDate); +async function getYamlSummary(sinceDate, templatesInWorkflow) { + const yamlActivity = await yamlFilesActivity(sinceDate, templatesInWorkflow); const summary = { created: 0, updated: 0 }; summary.created = (yamlActivity["A"] || 0) - (yamlActivity["D"] || 0); summary.updated = yamlActivity["M"] || 0; @@ -406,6 +409,10 @@ function displayOverview(sinceDate, today, templateSummary, yamlSummary) { consola.log("------------------------------------"); } +function displayWorkflowOverview(sinceDate, today, templateSummary, yamlSummary) { + +} + function createRow(sinceDate, today, templateSummary, yamlSummary) { // Row to append to file const rowContent = [ From bf258ee9b80c7c451d712af02b409357657bc374 Mon Sep 17 00:00:00 2001 From: Toby Masters Date: Tue, 31 Mar 2026 12:31:26 +0100 Subject: [PATCH 06/11] [241] Create displayWorkflowOverview function --- lib/cli/stats.js | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/lib/cli/stats.js b/lib/cli/stats.js index 81d694f..16f2a5a 100644 --- a/lib/cli/stats.js +++ b/lib/cli/stats.js @@ -410,7 +410,51 @@ function displayOverview(sinceDate, today, templateSummary, yamlSummary) { } function displayWorkflowOverview(sinceDate, today, templateSummary, yamlSummary) { - + // Header + consola.log(""); + consola.info(`${chalk.bold(`Workflow Summary - ${templateSummary.template_name} ( ${sinceDate} - ${today} ):`)}`); + consola.log("------------------------------------"); + consola.log(""); + // YAML file changes + consola.log(`New YAML files created in the period: ${yamlSummary.created}`); + consola.log(`Updates to existing YAML files in the period: ${yamlSummary.updated}`); + consola.log(""); + consola.log("------------------------------------"); + consola.log(""); + // Reconciliations + consola.log(`${chalk.bold("Reconciliations:")}`); + consola.log(`Templates: ${templateSummary.reconciliations.total}`); + consola.log(`Externally Managed: ${templateSummary.reconciliations.externallyManaged} (${templateSummary.reconciliations.externallyManagedPerc}%)`); + consola.log(`YAML files: ${templateSummary.reconciliations.yamlFiles} (${templateSummary.reconciliations.yamlFilesPerc}%)`); + consola.log(`Unit Tests: ${templateSummary.reconciliations.unitTests}`); + consola.log( + `YAML files with at least two unit tests: ${templateSummary.reconciliations.yamlFilesWithAtLeastTwoTests} (${templateSummary.reconciliations.yamlFilesWithAtLeastTwoTestsPerc}%)` + ); + consola.log(""); + // Account Templates + consola.log(`${chalk.bold("Account Templates:")}`); + consola.log(`Templates: ${templateSummary.accountTemplates.total}`); + consola.log(`Externally Managed: ${templateSummary.accountTemplates.externallyManaged} (${templateSummary.accountTemplates.externallyManagedPerc}%)`); + consola.log(`YAML files: ${templateSummary.accountTemplates.yamlFiles} (${templateSummary.accountTemplates.yamlFilesPerc}%)`); + consola.log(`Unit Tests: ${templateSummary.accountTemplates.unitTests}`); + consola.log( + `YAML files with at least two unit tests: ${templateSummary.accountTemplates.yamlFilesWithAtLeastTwoTests} (${templateSummary.accountTemplates.yamlFilesWithAtLeastTwoTestsPerc}%)` + ); + consola.log(""); + // Export Files + consola.log(`${chalk.bold("Export Files:")}`); + consola.log(`Templates: ${templateSummary.exportFiles.total}`); + consola.log(`Externally Managed: ${templateSummary.exportFiles.externallyManaged} (${templateSummary.exportFiles.externallyManagedPerc}%)`); + consola.log(""); + // All + consola.log(`${chalk.bold("All:")}`); + consola.log(`Templates: ${templateSummary.all.total}`); + consola.log(`Externally Managed: ${templateSummary.all.externallyManaged} (${templateSummary.all.externallyManagedPerc}%)`); + consola.log(`YAML files: ${templateSummary.all.yamlFiles} (${templateSummary.all.yamlFilesPerc}%)`); + consola.log(`Unit Tests: ${templateSummary.all.unitTests}`); + consola.log(`YAML files with at least two unit tests: ${templateSummary.all.yamlFilesWithAtLeastTwoTests} (${templateSummary.all.yamlFilesWithAtLeastTwoTestsPerc}%)`); + consola.log(""); + consola.log("------------------------------------"); } function createRow(sinceDate, today, templateSummary, yamlSummary) { From be800bc915c1cdfabdbec22b9a547613963a633b Mon Sep 17 00:00:00 2001 From: Toby Masters Date: Tue, 31 Mar 2026 12:46:44 +0100 Subject: [PATCH 07/11] fixup! [241] Update yamlFilesActivity function for workflows --- lib/cli/stats.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/stats.js b/lib/cli/stats.js index 16f2a5a..8b68a10 100644 --- a/lib/cli/stats.js +++ b/lib/cli/stats.js @@ -42,7 +42,7 @@ async function yamlFilesActivity(sinceDate, workflow) { // Files to Search (YAML) const templatesInWorkflow = workflow ? workflow.templates.reconciliations.concat(workflow.templates.accounts) : []; const TEMPLATE_PATTERN = workflow ? `(${templatesInWorkflow.join("|")})` : `.*`; - const YAML_EXPRESSION = `.*/.${TEMPLATE_PATTERN}/tests/.*_liquid_test.*.y(a)?ml`; + const YAML_EXPRESSION = `.*/${TEMPLATE_PATTERN}/tests/.*_liquid_test.*.y(a)?ml`; const fileTypeRegExp = RegExp(YAML_EXPRESSION, "g"); for (const row of nonEmptyRows) { From 9fd20b90b42ea58ea417b46f82e5d05423db65fc Mon Sep 17 00:00:00 2001 From: Toby Masters Date: Tue, 31 Mar 2026 14:46:22 +0100 Subject: [PATCH 08/11] [241] Create a createWorkflowRow function --- lib/cli/stats.js | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/lib/cli/stats.js b/lib/cli/stats.js index 8b68a10..2aa98fd 100644 --- a/lib/cli/stats.js +++ b/lib/cli/stats.js @@ -14,12 +14,12 @@ async function generateOverview(sinceDate, workflowHandle) { // Terminal if (workflow) { displayWorkflowOverview(sinceDate, TODAY, templateSummary, yamlSummary); + const row = createWorkflowRow(sinceDate, TODAY, templateSummary, yamlSummary); } else { displayOverview(sinceDate, TODAY, templateSummary, yamlSummary); + const row = createRow(sinceDate, TODAY, templateSummary, yamlSummary); + saveOverviewToFile(row); } - // File - // const row = createRow(sinceDate, TODAY, templateSummary, yamlSummary); - // saveOverviewToFile(row); } // Return an object with the count of activities by file and by type @@ -497,6 +497,43 @@ function createRow(sinceDate, today, templateSummary, yamlSummary) { return row; } +function createWorkflowRow(sinceDate, today, templateSummary, yamlSummary) { + const rowContent = [ + sinceDate, + today, + templateSummary.template_name, + yamlSummary.created, + yamlSummary.updated, + templateSummary.all.total, + templateSummary.all.externallyManaged, + templateSummary.all.yamlFiles, + templateSummary.all.unitTests, + templateSummary.reconciliations.total, + templateSummary.reconciliations.externallyManaged, + templateSummary.reconciliations.yamlFiles, + templateSummary.reconciliations.unitTests, + templateSummary.accountTemplates.total, + templateSummary.accountTemplates.externallyManaged, + templateSummary.accountTemplates.yamlFiles, + templateSummary.accountTemplates.unitTests, + templateSummary.exportFiles.total, + templateSummary.exportFiles.externallyManaged, + templateSummary.all.externallyManagedPerc, + templateSummary.reconciliations.externallyManagedPerc, + templateSummary.accountTemplates.externallyManagedPerc, + templateSummary.exportFiles.externallyManagedPerc, + templateSummary.all.yamlFilesPerc, + templateSummary.reconciliations.yamlFilesPerc, + templateSummary.accountTemplates.yamlFilesPerc, + templateSummary.reconciliations.yamlFilesWithAtLeastTwoTests, + templateSummary.reconciliations.yamlFilesWithAtLeastTwoTestsPerc, + templateSummary.accountTemplates.yamlFilesWithAtLeastTwoTests, + templateSummary.accountTemplates.yamlFilesWithAtLeastTwoTestsPerc, + ]; + const row = `\r\n${rowContent.join(";")}`; + return row; +} + // content row must be a string with each column separated by ";" function saveOverviewToFile(row) { const COLUMNS = [ From eea57ec2decb3f14fd967208a4c424015c918660 Mon Sep 17 00:00:00 2001 From: Toby Masters Date: Tue, 31 Mar 2026 15:00:56 +0100 Subject: [PATCH 09/11] [241] Create a saveWorkflowOverviewToFile function --- lib/cli/stats.js | 58 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/lib/cli/stats.js b/lib/cli/stats.js index 2aa98fd..8bc49a3 100644 --- a/lib/cli/stats.js +++ b/lib/cli/stats.js @@ -15,6 +15,7 @@ async function generateOverview(sinceDate, workflowHandle) { if (workflow) { displayWorkflowOverview(sinceDate, TODAY, templateSummary, yamlSummary); const row = createWorkflowRow(sinceDate, TODAY, templateSummary, yamlSummary); + saveWorkflowOverviewToFile(row, workflowHandle); } else { displayOverview(sinceDate, TODAY, templateSummary, yamlSummary); const row = createRow(sinceDate, TODAY, templateSummary, yamlSummary); @@ -266,7 +267,7 @@ async function getTemplatesSummary() { async function getWorkflowTemplateSummary(workflow) { const summary = { - template_name: "", + workflow_name: "", reconciliations: { total: 0, externallyManaged: 0, @@ -306,7 +307,7 @@ async function getWorkflowTemplateSummary(workflow) { // Fetch workflow // Assume no empty items if present within the workflow. - summary.template_name = workflow.name; + summary.workflow_name = workflow.name; // Reconciliations const reconciliationsInWorkflow = workflow.templates.reconciliations; @@ -412,7 +413,7 @@ function displayOverview(sinceDate, today, templateSummary, yamlSummary) { function displayWorkflowOverview(sinceDate, today, templateSummary, yamlSummary) { // Header consola.log(""); - consola.info(`${chalk.bold(`Workflow Summary - ${templateSummary.template_name} ( ${sinceDate} - ${today} ):`)}`); + consola.info(`${chalk.bold(`Workflow Summary - ${templateSummary.workflow_name} ( ${sinceDate} - ${today} ):`)}`); consola.log("------------------------------------"); consola.log(""); // YAML file changes @@ -499,9 +500,9 @@ function createRow(sinceDate, today, templateSummary, yamlSummary) { function createWorkflowRow(sinceDate, today, templateSummary, yamlSummary) { const rowContent = [ + templateSummary.workflow_name, sinceDate, today, - templateSummary.template_name, yamlSummary.created, yamlSummary.updated, templateSummary.all.total, @@ -585,4 +586,53 @@ function saveOverviewToFile(row) { fs.appendFileSync(CSV_PATH, row); } +// content row must be a string with each column separated by ";" +function saveWorkflowOverviewToFile(row, workflowHandle) { + const COLUMNS = [ + "Workflow Name", + "Period - Start", + "Period - End", + "yaml files created in period", + "yaml files modified in period", + "All - templates", + "All - externally managed", + "All - yaml files", + "All - unit tests", + "Reconciliations - templates", + "Reconciliations - externally managed", + "Reconciliations - yaml files", + "Reconciliations - unit tests", + "Account Templates - templates", + "Account Templates - externally managed", + "Account Templates - yaml files", + "Account Templates - unit tests", + "Export Files - templates", + "Export Files - externally managed", + "All - externally managed (%)", + "Reconciliations - externally managed (%)", + "Account Templates - externally managed (%)", + "Export Files - externally managed (%)", + "All - yaml files (%)", + "Reconciliations - yaml files (%)", + "Account Templates - yaml files (%)", + "Reconciliations - yaml files with at least two tests", + "Reconciliations - yaml files with at least two tests (%)", + "Account Templates - yaml files with at least two tests", + "Account Templates - yaml files with at least two tests (%)", + ]; + const ROW_HEADER = `${COLUMNS.join(";")}`; + const CSV_PATH = `./stats/${workflowHandle}_stats.csv`; + // Create file and header columns + if (!fs.existsSync("./stats")) { + fs.mkdirSync("stats"); + } + if (!fs.existsSync(CSV_PATH)) { + fs.writeFileSync(CSV_PATH, ROW_HEADER, (err) => { + consola.error(err); + }); + } + // Append content + fs.appendFileSync(CSV_PATH, row); +} + module.exports = { generateOverview }; From 59481c7c11775a21db01637cb5835ec0f47e0d86 Mon Sep 17 00:00:00 2001 From: Toby Masters Date: Tue, 31 Mar 2026 15:06:53 +0100 Subject: [PATCH 10/11] [241] Split generateOverview functions --- bin/cli.js | 7 ++++++- lib/cli/stats.js | 32 +++++++++++++++++--------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index d7fe15c..3732417 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -572,7 +572,12 @@ program .requiredOption("-s, --since , Specify the date which is going to be used to filter the data from (format: YYYY-MM-DD) (mandatory)") .option("-w, --workflow [handle], Optionally filter test statistics by workflow (optional)") .action((options) => { - stats.generateOverview(options.since, options.workflow); + if (options.workflow) { + const workflowHandle = typeof options.workflow === "string" ? options.workflow : undefined; + stats.generateWorkflowOverview(options.since, workflowHandle); + } else { + stats.generateOverview(options.since); + } }); // Set/Get FIRM ID diff --git a/lib/cli/stats.js b/lib/cli/stats.js index 8bc49a3..731ce99 100644 --- a/lib/cli/stats.js +++ b/lib/cli/stats.js @@ -6,21 +6,23 @@ const fsUtils = require("../utils/fsUtils"); const yaml = require("yaml"); const { consola } = require("consola"); -async function generateOverview(sinceDate, workflowHandle) { - const workflow = workflowHandle ? await fsUtils.getWorkflow(workflowHandle) : null; +async function generateOverview(sinceDate) { const TODAY = new Date().toJSON().toString().slice(0, 10); - const templateSummary = workflow ? await getWorkflowTemplateSummary(workflow) : await getTemplatesSummary(); - const yamlSummary = workflow ? await getYamlSummary(sinceDate, workflow) : await getYamlSummary(sinceDate); - // Terminal - if (workflow) { - displayWorkflowOverview(sinceDate, TODAY, templateSummary, yamlSummary); - const row = createWorkflowRow(sinceDate, TODAY, templateSummary, yamlSummary); - saveWorkflowOverviewToFile(row, workflowHandle); - } else { - displayOverview(sinceDate, TODAY, templateSummary, yamlSummary); - const row = createRow(sinceDate, TODAY, templateSummary, yamlSummary); - saveOverviewToFile(row); - } + const templateSummary = await getTemplatesSummary(); + const yamlSummary = await getYamlSummary(sinceDate); + displayOverview(sinceDate, TODAY, templateSummary, yamlSummary); + const row = createRow(sinceDate, TODAY, templateSummary, yamlSummary); + saveOverviewToFile(row); +} + +async function generateWorkflowOverview(sinceDate, workflowHandle) { + const TODAY = new Date().toJSON().toString().slice(0, 10); + const workflow = await fsUtils.getWorkflow(workflowHandle); + const templateSummary = await getWorkflowTemplateSummary(workflow); + const yamlSummary = await getYamlSummary(sinceDate, workflow); + displayWorkflowOverview(sinceDate, TODAY, templateSummary, yamlSummary); + const row = createWorkflowRow(sinceDate, TODAY, templateSummary, yamlSummary); + saveWorkflowOverviewToFile(row, workflowHandle); } // Return an object with the count of activities by file and by type @@ -635,4 +637,4 @@ function saveWorkflowOverviewToFile(row, workflowHandle) { fs.appendFileSync(CSV_PATH, row); } -module.exports = { generateOverview }; +module.exports = { generateOverview, generateWorkflowOverview }; From e0999264e99d622cd1e9ae61b48313a01a74aaff Mon Sep 17 00:00:00 2001 From: Toby Masters Date: Tue, 31 Mar 2026 15:14:35 +0100 Subject: [PATCH 11/11] Add review all workflows functionality --- lib/cli/stats.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/cli/stats.js b/lib/cli/stats.js index 731ce99..3e39269 100644 --- a/lib/cli/stats.js +++ b/lib/cli/stats.js @@ -17,12 +17,21 @@ async function generateOverview(sinceDate) { async function generateWorkflowOverview(sinceDate, workflowHandle) { const TODAY = new Date().toJSON().toString().slice(0, 10); - const workflow = await fsUtils.getWorkflow(workflowHandle); - const templateSummary = await getWorkflowTemplateSummary(workflow); - const yamlSummary = await getYamlSummary(sinceDate, workflow); - displayWorkflowOverview(sinceDate, TODAY, templateSummary, yamlSummary); - const row = createWorkflowRow(sinceDate, TODAY, templateSummary, yamlSummary); - saveWorkflowOverviewToFile(row, workflowHandle); + let workflowHandles; + if (workflowHandle) { + workflowHandles = [workflowHandle]; + } else { + const workflowsFolder = path.join(process.cwd(), "workflows"); + workflowHandles = fs.readdirSync(workflowsFolder).map((file) => path.basename(file, ".json")); + } + for (const handle of workflowHandles) { + const workflow = await fsUtils.getWorkflow(handle); + const templateSummary = await getWorkflowTemplateSummary(workflow); + const yamlSummary = await getYamlSummary(sinceDate, workflow); + displayWorkflowOverview(sinceDate, TODAY, templateSummary, yamlSummary); + const row = createWorkflowRow(sinceDate, TODAY, templateSummary, yamlSummary); + saveWorkflowOverviewToFile(row, handle); + } } // Return an object with the count of activities by file and by type