-
Notifications
You must be signed in to change notification settings - Fork 1
Run multiple handles in parallel #239
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
Changes from all commits
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 | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -453,16 +453,16 @@ program | |||||||||||||||||||||||||||||||||||||
| .command("run-test") | ||||||||||||||||||||||||||||||||||||||
| .description("Run Liquid Tests for a reconciliation template from a YAML file") | ||||||||||||||||||||||||||||||||||||||
| .requiredOption("-f, --firm <firm-id>", "Specify the firm to be used", firmIdDefault) | ||||||||||||||||||||||||||||||||||||||
| .option("-h, --handle <handle>", "Specify the reconciliation to be used (mandatory)") | ||||||||||||||||||||||||||||||||||||||
| .option("-at, --account-template <name>", "Specify the account template to be used (mandatory)") | ||||||||||||||||||||||||||||||||||||||
| .option("-h, --handle <handle...>", "Specify one or more reconciliations to be used (mandatory)") | ||||||||||||||||||||||||||||||||||||||
| .option("-at, --account-template <name...>", "Specify one or more account templates to be used (mandatory)") | ||||||||||||||||||||||||||||||||||||||
| .option("-t, --test <test-name>", "Specify the name of the test to be run (optional)", "") | ||||||||||||||||||||||||||||||||||||||
| .option("--html-input", "Get a static html of the input-view of the template generated with the Liquid Test data (optional)", false) | ||||||||||||||||||||||||||||||||||||||
| .option("--html-preview", "Get a static html of the export-view of the template generated with the Liquid Test data (optional)", false) | ||||||||||||||||||||||||||||||||||||||
| .option("--preview-only", "Skip the checking of the results of the Liquid Test in case you only want to generate a preview template (optional)", false) | ||||||||||||||||||||||||||||||||||||||
| .option("--status", "Only return the status of the test runs as PASSED/FAILED (optional)", false) | ||||||||||||||||||||||||||||||||||||||
| .option("-p, --pattern <pattern>", "Run all tests that match this pattern (optional)", "") | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| .action((options) => { | ||||||||||||||||||||||||||||||||||||||
| .action(async (options) => { | ||||||||||||||||||||||||||||||||||||||
| if (!options.handle && !options.accountTemplate) { | ||||||||||||||||||||||||||||||||||||||
| consola.error("You need to specify either a reconciliation handle or an account template"); | ||||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -476,15 +476,41 @@ program | |||||||||||||||||||||||||||||||||||||
| const templateType = options.handle ? "reconciliationText" : "accountTemplate"; | ||||||||||||||||||||||||||||||||||||||
| const templateName = options.handle ? options.handle : options.accountTemplate; | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
476
to
477
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 mutual exclusivity check for If both 🔧 Proposed fix const templateType = options.handle ? "reconciliationText" : "accountTemplate";
const templateName = options.handle ? options.handle : options.accountTemplate;
+ if (options.handle && options.accountTemplate) {
+ consola.error("You cannot use both --handle and --account-template options at the same time");
+ process.exit(1);
+ }
+
if (!templateName || templateName.length === 0) {Alternatively, use the existing utility: cliUtils.checkUniqueOption(["handle", "accountTemplate"], options);🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (!templateName || templateName.length === 0) { | ||||||||||||||||||||||||||||||||||||||
| consola.error("You need to provide at least one handle or account template name"); | ||||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Block multiple handles/templates without --status | ||||||||||||||||||||||||||||||||||||||
| if (templateName.length > 1 && !options.status) { | ||||||||||||||||||||||||||||||||||||||
| consola.error("Multiple handles/templates are only allowed when used with the --status flag"); | ||||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (options.status) { | ||||||||||||||||||||||||||||||||||||||
| liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateName, options.test, options.pattern); | ||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||
| if (options.previewOnly && !options.htmlInput && !options.htmlPreview) { | ||||||||||||||||||||||||||||||||||||||
| consola.info(`When using "--preview-only" you need to specify at least one of the following options: "--html-input", "--html-preview"`); | ||||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| liquidTestRunner.runTestsWithOutput(options.firm, templateType, templateName, options.test, options.previewOnly, options.htmlInput, options.htmlPreview, options.pattern); | ||||||||||||||||||||||||||||||||||||||
| // Status mode: allow multiple, pass array of template names | ||||||||||||||||||||||||||||||||||||||
| await liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateName, options.test, options.pattern); | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
490
to
+494
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. 🧩 Analysis chain🏁 Script executed: # First, let's look at the structure of the repository
git ls-files | grep -E "(cli|test)" | head -20Repository: silverfin/silverfin-cli Length of output: 659 🏁 Script executed: # Find the CLI file and examine the status mode code
fd cli.jsRepository: silverfin/silverfin-cli Length of output: 77 🏁 Script executed: # Let's read the relevant section of bin/cli.js
wc -l bin/cli.jsRepository: silverfin/silverfin-cli Length of output: 81 🏁 Script executed: # Read around lines 495-499 with more context
sed -n '480,520p' bin/cli.jsRepository: silverfin/silverfin-cli Length of output: 2096 🏁 Script executed: # Find where runTestsStatusOnly is defined
rg "runTestsStatusOnly" --type js -B 2 -A 5Repository: silverfin/silverfin-cli Length of output: 1298 🏁 Script executed: # Check the return type/value of runTestsStatusOnly
ast-grep --pattern 'runTestsStatusOnly($$$) { $$$ }'Repository: silverfin/silverfin-cli Length of output: 49 🏁 Script executed: # Let's also check how other test modes handle exit codes
sed -n '450,550p' bin/cli.jsRepository: silverfin/silverfin-cli Length of output: 4967 🏁 Script executed: # Get the full implementation of runTestsStatusOnly
sed -n '1,100p' lib/liquidTestRunner.js | grep -A 50 "async function runTestsStatusOnly"Repository: silverfin/silverfin-cli Length of output: 49 🏁 Script executed: # Search for return statements in runTestsStatusOnly function
rg -A 100 "async function runTestsStatusOnly" lib/liquidTestRunner.js | head -120Repository: silverfin/silverfin-cli Length of output: 1910 Status mode should propagate failure via exit code (required for CI/CD workflows).
Proposed fix if (options.status) {
// Status mode: allow multiple, pass array of template names
- await liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateName, options.test, options.pattern);
- return;
+ const overallStatus = await liquidTestRunner.runTestsStatusOnly(options.firm, templateType, templateName, options.test, options.pattern);
+ process.exitCode = overallStatus === "PASSED" ? 0 : 1;
+ return;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Non-status mode: always run a single template, pass string handle/name | ||||||||||||||||||||||||||||||||||||||
| const singleTemplateName = templateName[0]; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (options.previewOnly && !options.htmlInput && !options.htmlPreview) { | ||||||||||||||||||||||||||||||||||||||
| consola.info(`When using "--preview-only" you need to specify at least one of the following options: "--html-input", "--html-preview"`); | ||||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| await liquidTestRunner.runTestsWithOutput( | ||||||||||||||||||||||||||||||||||||||
| options.firm, | ||||||||||||||||||||||||||||||||||||||
| templateType, | ||||||||||||||||||||||||||||||||||||||
| singleTemplateName, | ||||||||||||||||||||||||||||||||||||||
| options.test, | ||||||||||||||||||||||||||||||||||||||
| options.previewOnly, | ||||||||||||||||||||||||||||||||||||||
| options.htmlInput, | ||||||||||||||||||||||||||||||||||||||
| options.htmlPreview, | ||||||||||||||||||||||||||||||||||||||
| options.pattern | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Create Liquid Test | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -469,33 +469,58 @@ async function runTestsWithOutput(firmId, templateType, handle, testName = "", p | |
|
|
||
| // RETURN (AND LOG) ONLY PASSED OR FAILED | ||
| // CAN BE USED BY GITHUB ACTIONS | ||
| async function runTestsStatusOnly(firmId, templateType, handle, testName = "", pattern = "") { | ||
| async function runTestsStatusOnly(firmId, templateType, handles, testName = "", pattern = "") { | ||
| if (templateType !== "reconciliationText" && templateType !== "accountTemplate") { | ||
| consola.error(`Template type is missing or invalid`); | ||
| process.exit(1); | ||
| } | ||
|
Comment on lines
+472
to
476
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. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# First, let's examine the function implementation around lines 472-476
sed -n '472,530p' lib/liquidTestRunner.js | cat -nRepository: silverfin/silverfin-cli Length of output: 2354 🏁 Script executed: #!/bin/bash
# Search for all calls to runTestsStatusOnly in the codebase
rg -n 'runTestsStatusOnly\s*\(' --type js -A 2Repository: silverfin/silverfin-cli Length of output: 579 Add input validation to guard against string being passed to The function expects Add defensive validation at the beginning of the function: if (!Array.isArray(handles)) {
consola.error('handles must be an array');
process.exit(1);
}Alternatively, update the caller to wrap 🤖 Prompt for AI Agents |
||
|
|
||
| let status = "FAILED"; | ||
| const testResult = await runTests(firmId, templateType, handle, testName, false, "none", pattern); | ||
| const runSingleHandle = async (singleHandle) => { | ||
| let status = "FAILED"; | ||
| const failedTestNames = []; | ||
| const testResult = await runTests(firmId, templateType, singleHandle, testName, false, "none", pattern); | ||
|
|
||
| if (!testResult) { | ||
| status = "FAILED"; | ||
| consola.error(`Error running tests for ${singleHandle}`); | ||
| } else { | ||
| const testRun = testResult?.testRun; | ||
|
|
||
| if (testRun && testRun?.status === "completed") { | ||
| const errorsPresent = checkAllTestsErrorsPresent(testRun.tests); | ||
| if (errorsPresent === false) { | ||
| status = "PASSED"; | ||
| } else { | ||
| // Extract failed test names | ||
| const testNames = Object.keys(testRun.tests).sort(); | ||
| testNames.forEach((testName) => { | ||
| const testErrorsPresent = checkTestErrorsPresent(testName, testRun.tests); | ||
| if (testErrorsPresent) { | ||
| failedTestNames.push(testName); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (!testResult) { | ||
| status = "PASSED"; | ||
| consola.success(status); | ||
| return status; | ||
| } | ||
| if (status === "PASSED") { | ||
| consola.log(`${singleHandle}: ${status}`); | ||
| } else { | ||
| consola.log(`${singleHandle}: ${status}`); | ||
| // Display failed test names | ||
| failedTestNames.forEach((testName) => { | ||
| consola.log(` ${testName}: FAILED`); | ||
| }); | ||
| } | ||
|
|
||
| const testRun = testResult?.testRun; | ||
| return { handle: singleHandle, status, failedTestNames }; | ||
| }; | ||
|
|
||
| if (testRun && testRun?.status === "completed") { | ||
| const errorsPresent = checkAllTestsErrorsPresent(testRun.tests); | ||
| if (errorsPresent === false) { | ||
| status = "PASSED"; | ||
| consola.success(status); | ||
| return status; | ||
| } | ||
| } | ||
| consola.error(status); | ||
| return status; | ||
| const results = await Promise.all(handles.map(runSingleHandle)); | ||
|
|
||
| const overallStatus = results.every((result) => result.status === "PASSED") ? "PASSED" : "FAILED"; | ||
|
|
||
| return overallStatus; | ||
| } | ||
|
|
||
| module.exports = { | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| { | ||
| "name": "silverfin-cli", | ||
| "version": "1.51.0", | ||
| "version": "1.52.0", | ||
|
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. 🧩 Analysis chain🏁 Script executed: # First, find and read the package.json file
fd package.json -type fRepository: silverfin/silverfin-cli Length of output: 237 🏁 Script executed: # Search for PR template files
fd -i pull_request_template -type fRepository: silverfin/silverfin-cli Length of output: 237 🏁 Script executed: # Also check for common template locations
ls -la .github/ 2>/dev/null || echo "No .github directory"Repository: silverfin/silverfin-cli Length of output: 369 🏁 Script executed: # Get git diff to see what actually changed in package.json
git diff HEAD -- package.json 2>/dev/null || echo "Git diff not available"Repository: silverfin/silverfin-cli Length of output: 49 🏁 Script executed: # Read package.json
cat package.json | head -20Repository: silverfin/silverfin-cli Length of output: 585 🏁 Script executed: # Read the PR template
cat .github/pull_request_template.mdRepository: silverfin/silverfin-cli Length of output: 482 Confirm the version bump is intentional for this PR. The PR template checklist includes "Skip bumping the CLI version", but 🤖 Prompt for AI Agents |
||
| "description": "Command line tool for Silverfin template development", | ||
| "main": "index.js", | ||
| "license": "MIT", | ||
|
|
||
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.
Use an unambiguous date format in the changelog.
(12/01/2026)is locale-ambiguous; consider2026-01-12(ISO-8601) for consistency and clarity.🤖 Prompt for AI Agents