-
Notifications
You must be signed in to change notification settings - Fork 2
Add workflow option to stats command #247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
6198bd2
b134da4
9d71e6d
51fc334
a23a295
bf258ee
be800bc
9fd20b9
eea57ec
59481c7
e099926
cad924b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,16 +10,33 @@ async function generateOverview(sinceDate) { | |||||||||||||||||||||||||||||||||
| 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); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| async function generateWorkflowOverview(sinceDate, workflowHandle) { | ||||||||||||||||||||||||||||||||||
| const TODAY = new Date().toJSON().toString().slice(0, 10); | ||||||||||||||||||||||||||||||||||
| let workflowHandles; | ||||||||||||||||||||||||||||||||||
| if (workflowHandle) { | ||||||||||||||||||||||||||||||||||
| workflowHandles = [workflowHandle]; | ||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||
| const workflowsFolder = path.join(process.cwd(), "workflows"); | ||||||||||||||||||||||||||||||||||
| workflowHandles = fs.readdirSync(workflowsFolder).map((file) => path.basename(file, ".json")); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+23
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing error handling when If the 🛡️ Proposed fix to handle missing workflows folder } else {
const workflowsFolder = path.join(process.cwd(), "workflows");
+ if (!fs.existsSync(workflowsFolder)) {
+ consola.error("No workflows folder found. Please create a 'workflows' directory with workflow JSON files.");
+ process.exit(1);
+ }
workflowHandles = fs.readdirSync(workflowsFolder).map((file) => path.basename(file, ".json"));
+ if (workflowHandles.length === 0) {
+ consola.info("No workflow files found in the workflows folder.");
+ return;
+ }
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Additional context: the same There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
} else {
const workflowsFolder = path.join(process.cwd(), "workflows");
if (!fs.existsSync(workflowsFolder)) {
consola.error("No workflows folder found. Please create a 'workflows' directory with workflow JSON files.");
process.exit(1);
}
workflowHandles = fs.readdirSync(workflowsFolder)
+ .filter((file) => file.endsWith(".json"))
.map((file) => path.basename(file, ".json"));
if (workflowHandles.length === 0) {
consola.info("No workflow files found in the workflows folder.");
return;
}
}The ✏️ Learnings added
|
||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||
| // 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) { | ||||||||||||||||||||||||||||||||||
|
|
@@ -35,7 +52,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("|")})` : `.*`; | ||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Empty template list produces a broken regex pattern. When a workflow has no reconciliations and no accounts, |
||||||||||||||||||||||||||||||||||
| const YAML_EXPRESSION = `.*/${TEMPLATE_PATTERN}/tests/.*_liquid_test.*.y(a)?ml`; | ||||||||||||||||||||||||||||||||||
| const fileTypeRegExp = RegExp(YAML_EXPRESSION, "g"); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+55
to
58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regex metacharacters in template names are not escaped. If template names in the workflow contain regex special characters (e.g., 🔧 Proposed fix to escape regex special characters+function escapeRegex(string) {
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+
async function yamlFilesActivity(sinceDate, workflow) {
// ...
const templatesInWorkflow = workflow ? workflow.templates.reconciliations.concat(workflow.templates.accounts) : [];
- const TEMPLATE_PATTERN = workflow ? `(${templatesInWorkflow.join("|")})` : `.*`;
+ const TEMPLATE_PATTERN = workflow ? `(${templatesInWorkflow.map(escapeRegex).join("|")})` : `.*`;
const YAML_EXPRESSION = `.*/${TEMPLATE_PATTERN}/tests/.*_liquid_test.*.y(a)?ml`;🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The identical pattern exists in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
+function escapeRegex(string) {
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+
async function yamlFilesActivity(sinceDate, workflow) {
// ...
const templatesInWorkflow = workflow
? workflow.templates.reconciliations.concat(workflow.templates.accounts)
: [];
- const TEMPLATE_PATTERN = workflow ? `(${templatesInWorkflow.join("|")})` : `.*`;
+ const TEMPLATE_PATTERN = workflow
+ ? `(${templatesInWorkflow.map(escapeRegex).join("|")})`
+ : `.*`;
const YAML_EXPRESSION = `.*/${TEMPLATE_PATTERN}/tests/.*_liquid_test.*.y(a)?ml`;
// ...
}
async function countYamlFiles(templateType, templatesInWorkflow) {
// ...
- const TEMPLATE_PATTERN = templatesInWorkflow ? `(${templatesInWorkflow.join("|")})` : `.*`;
+ const TEMPLATE_PATTERN = templatesInWorkflow
+ ? `(${templatesInWorkflow.map(escapeRegex).join("|")})`
+ : `.*`;
const YAML_EXPRESSION = `.*${FOLDER}/${TEMPLATE_PATTERN}/tests/.*_liquid_test.*.y(a)?ml`;
// ...
}Both the activity scan and the YAML file count share the same vulnerable pattern-building logic, so both need the same fix. Thanks for catching this! |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| for (const row of nonEmptyRows) { | ||||||||||||||||||||||||||||||||||
|
|
@@ -74,10 +93,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? | ||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leftover open question in a comment ( |
||||||||||||||||||||||||||||||||||
| 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; | ||||||||||||||||||||||||||||||||||
|
|
@@ -255,8 +276,99 @@ async function getTemplatesSummary() { | |||||||||||||||||||||||||||||||||
| return summary; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| async function getYamlSummary(sinceDate) { | ||||||||||||||||||||||||||||||||||
| const yamlActivity = await yamlFilesActivity(sinceDate); | ||||||||||||||||||||||||||||||||||
| async function getWorkflowTemplateSummary(workflow) { | ||||||||||||||||||||||||||||||||||
| const summary = { | ||||||||||||||||||||||||||||||||||
| workflow_name: "", | ||||||||||||||||||||||||||||||||||
| 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, | ||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Fetch workflow | ||||||||||||||||||||||||||||||||||
| // Assume no empty items if present within the workflow. | ||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Global vs workflow stats count different populations.
|
||||||||||||||||||||||||||||||||||
| summary.workflow_name = workflow.name; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Reconciliations | ||||||||||||||||||||||||||||||||||
| 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.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.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); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return summary; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| async function getYamlSummary(sinceDate, templatesInWorkflow) { | ||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: the second parameter is named |
||||||||||||||||||||||||||||||||||
| 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; | ||||||||||||||||||||||||||||||||||
|
|
@@ -309,6 +421,54 @@ function displayOverview(sinceDate, today, templateSummary, yamlSummary) { | |||||||||||||||||||||||||||||||||
| consola.log("------------------------------------"); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| function displayWorkflowOverview(sinceDate, today, templateSummary, yamlSummary) { | ||||||||||||||||||||||||||||||||||
| // Header | ||||||||||||||||||||||||||||||||||
| consola.log(""); | ||||||||||||||||||||||||||||||||||
| consola.info(`${chalk.bold(`Workflow Summary - ${templateSummary.workflow_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) { | ||||||||||||||||||||||||||||||||||
| // Row to append to file | ||||||||||||||||||||||||||||||||||
| const rowContent = [ | ||||||||||||||||||||||||||||||||||
|
|
@@ -349,6 +509,43 @@ function createRow(sinceDate, today, templateSummary, yamlSummary) { | |||||||||||||||||||||||||||||||||
| return row; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| function createWorkflowRow(sinceDate, today, templateSummary, yamlSummary) { | ||||||||||||||||||||||||||||||||||
| const rowContent = [ | ||||||||||||||||||||||||||||||||||
| templateSummary.workflow_name, | ||||||||||||||||||||||||||||||||||
| sinceDate, | ||||||||||||||||||||||||||||||||||
| today, | ||||||||||||||||||||||||||||||||||
| 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 = [ | ||||||||||||||||||||||||||||||||||
|
|
@@ -400,4 +597,53 @@ function saveOverviewToFile(row) { | |||||||||||||||||||||||||||||||||
| fs.appendFileSync(CSV_PATH, row); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| module.exports = { generateOverview }; | ||||||||||||||||||||||||||||||||||
| // 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) => { | ||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Remove the callback and either let the exception propagate naturally, or wrap the call in a |
||||||||||||||||||||||||||||||||||
| consola.error(err); | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| // Append content | ||||||||||||||||||||||||||||||||||
| fs.appendFileSync(CSV_PATH, row); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| module.exports = { generateOverview, generateWorkflowOverview }; | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -483,6 +483,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); | ||
| } | ||
| } | ||
|
Comment on lines
+483
to
+495
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unsanitized Both 🤖 Prompt for AI Agents |
||
|
|
||
| // Recursive option for fs.watch is not available in every OS (e.g. Linux) | ||
| function recursiveInspectDirectory({ basePath, collection, pathsArray = [], typeCheck = "liquid" }) { | ||
| collection.forEach((filePath) => { | ||
|
|
@@ -622,5 +636,6 @@ module.exports = { | |
| getTemplateId, | ||
| setTemplateId, | ||
| checkLiquidTestDependencies, | ||
| getWorkflow, | ||
| scanTextParts, | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--workflowwithout a value silently runs all-workflows scan instead of erroring.The option is declared with optional-argument syntax
[handle], so runningsilverfin stats -s 2024-01-01 --workflow(flag present, no value) causes Commander to setoptions.workflow = true(boolean). Theif (options.workflow)check on the line above is truthy, but thistypeof === 'string'guard then setsworkflowHandle = undefined. As a result,generateWorkflowOverview(sinceDate, undefined)is called and silently reads every file in theworkflows/folder — the user gets a full-repo scan with no indication their argument was dropped.Consider using
<handle>(required argument) instead of[handle], or explicitly checkoptions.workflow === trueand print an error.