From aec9503b99ceffb04f36f9f23f486ec547120a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Luiz=20Braga=20Mallmann?= Date: Sat, 20 Jun 2026 23:55:37 -0300 Subject: [PATCH 1/4] feat(cli): node selection for dataform compile --- cli/index.ts | 25 ++++++- cli/index_compile_test.ts | 133 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 2 deletions(-) diff --git a/cli/index.ts b/cli/index.ts index 452d71f5f..ae6b4b7b5 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -5,7 +5,7 @@ import parseDuration from "parse-duration"; import * as path from "path"; import yargs from "yargs"; -import { build, compile, credentials, init, install, run, test } from "df/cli/api"; +import { build, compile, credentials, init, install, prune, run, test } from "df/cli/api"; import { CREDENTIALS_FILENAME } from "df/cli/api/commands/credentials"; import { BigQueryDbAdapter } from "df/cli/api/dbadapters/bigquery"; import { prettyJsonStringify } from "df/cli/api/utils"; @@ -393,6 +393,10 @@ export function runCli() { dotOutputOption, timeoutOption, quietCompileOption, + actionsOption, + tagsOption, + includeDepsOption, + includeDependentsOption, { name: verboseOptionName, option: { @@ -429,7 +433,24 @@ export function runCli() { timeoutMillis: argv[timeoutOption.name] || undefined, verbose: argv[verboseOptionName] || false }); - printCompiledGraph(compiledGraph, outputType, argv[quietCompileOption.name]); + + // The whole project must compile (ref() resolution needs every action + // registered), but the printed output can be filtered to the selected + // action(s) -- mirroring how `run`/`build` prune the graph. We only prune + // a clean graph; if compilation produced errors we print the full graph + // plus the errors, keeping graph-level errors as-is. + const hasSelector = + argv[actionsOption.name]?.length > 0 || argv[tagsOption.name]?.length > 0; + const outputGraph = + hasSelector && !compiledGraphHasErrors(compiledGraph) + ? prune(compiledGraph, { + actions: argv[actionsOption.name], + tags: argv[tagsOption.name], + includeDependencies: argv[includeDepsOption.name], + includeDependents: argv[includeDependentsOption.name] + }) + : compiledGraph; + printCompiledGraph(outputGraph, outputType, argv[quietCompileOption.name]); if (compiledGraphHasErrors(compiledGraph)) { print(""); printCompiledGraphErrors(compiledGraph.graphErrors, argv[quietCompileOption.name]); diff --git a/cli/index_compile_test.ts b/cli/index_compile_test.ts index 4f82799b7..c59c7576d 100644 --- a/cli/index_compile_test.ts +++ b/cli/index_compile_test.ts @@ -361,6 +361,139 @@ SELECT 1 as id }); }); +suite("compile node selection", ({ afterEach }) => { + const tmpDirFixture = new TmpDirFixture(afterEach); + + // Builds a project with three tables: upstream -> midstream -> downstream. + async function setupSelectionProject(): Promise { + const projectDir = tmpDirFixture.createNewTmpDir(); + const npmCacheDir = tmpDirFixture.createNewTmpDir(); + + await getProcessResult( + execFile(nodePath, [cliEntryPointPath, "init", projectDir, DEFAULT_DATABASE, DEFAULT_LOCATION]) + ); + + const workflowSettingsPath = path.join(projectDir, "workflow_settings.yaml"); + const workflowSettings = dataform.WorkflowSettings.create( + loadYaml(fs.readFileSync(workflowSettingsPath, "utf8")) + ); + delete workflowSettings.dataformCoreVersion; + fs.writeFileSync(workflowSettingsPath, dumpYaml(workflowSettings)); + + fs.writeFileSync( + path.join(projectDir, "package.json"), + `{ + "dependencies":{ + "@dataform/core": "${version}" + } +}` + ); + await getProcessResult( + execFile(npmPath, [ + "install", + "--prefix", + projectDir, + "--cache", + npmCacheDir, + corePackageTarPath + ]) + ); + + const def = (name: string, contents: string) => { + const filePath = path.join(projectDir, "definitions", `${name}.sqlx`); + fs.ensureFileSync(filePath); + fs.writeFileSync(filePath, contents); + }; + def("upstream", `config { type: "table", tags: ["daily"] }\nSELECT 1 AS id`); + def("midstream", `config { type: "table" }\nSELECT * FROM \${ref("upstream")}`); + def("downstream", `config { type: "table" }\nSELECT * FROM \${ref("midstream")}`); + + return projectDir; + } + + const tableNames = (stdout: string): string[] => + JSON.parse(stdout).tables.map((table: any) => table.target.name).sort(); + + test("no selector emits the entire graph", async () => { + const projectDir = await setupSelectionProject(); + const result = await getProcessResult( + execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--json"]) + ); + expect(result.exitCode, result.stderr).equals(0); + expect(tableNames(result.stdout)).deep.equals(["downstream", "midstream", "upstream"]); + }); + + test("--actions filters output to the selected action", async () => { + const projectDir = await setupSelectionProject(); + const result = await getProcessResult( + execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--actions", "midstream", "--json"]) + ); + expect(result.exitCode, result.stderr).equals(0); + expect(tableNames(result.stdout)).deep.equals(["midstream"]); + }); + + test("--actions --include-deps pulls in upstream dependencies", async () => { + const projectDir = await setupSelectionProject(); + const result = await getProcessResult( + execFile(nodePath, [ + cliEntryPointPath, + "compile", + projectDir, + "--actions", + "midstream", + "--include-deps", + "--json" + ]) + ); + expect(result.exitCode, result.stderr).equals(0); + expect(tableNames(result.stdout)).deep.equals(["midstream", "upstream"]); + }); + + test("--actions --include-dependents pulls in downstream dependents", async () => { + const projectDir = await setupSelectionProject(); + const result = await getProcessResult( + execFile(nodePath, [ + cliEntryPointPath, + "compile", + projectDir, + "--actions", + "midstream", + "--include-dependents", + "--json" + ]) + ); + expect(result.exitCode, result.stderr).equals(0); + expect(tableNames(result.stdout)).deep.equals(["downstream", "midstream"]); + }); + + test("--tags filters output to actions carrying the tag", async () => { + const projectDir = await setupSelectionProject(); + const result = await getProcessResult( + execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--tags", "daily", "--json"]) + ); + expect(result.exitCode, result.stderr).equals(0); + expect(tableNames(result.stdout)).deep.equals(["upstream"]); + }); + + test("selector matching nothing emits an empty graph and exits zero", async () => { + const projectDir = await setupSelectionProject(); + const result = await getProcessResult( + execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--actions", "nope", "--json"]) + ); + expect(result.exitCode, result.stderr).equals(0); + expect(tableNames(result.stdout)).deep.equals([]); + }); + + test("--include-deps without a selector is rejected", async () => { + const projectDir = await setupSelectionProject(); + const result = await getProcessResult( + execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--include-deps", "--json"]) + ); + expect(result.exitCode).not.equals(0); + expect(result.stderr).contains("--include-deps"); + }); +}); + suite("extension config", ({ afterEach }) => { const tmpDirFixture = new TmpDirFixture(afterEach); From ba44749187dde254f8b81181d8b610ce9b7f1e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Luiz=20Braga=20Mallmann?= Date: Sun, 19 Jul 2026 01:06:06 -0300 Subject: [PATCH 2/4] refactor(cli): prefix compile selection flags with output- Renames the compile-only selection flags so it is explicit that they filter the printed output rather than change what gets compiled: --actions -> --output-actions --tags -> --output-tags --include-deps -> --output-include-deps --include-dependents -> --output-include-dependents run/build keep the unprefixed flags. Addresses review feedback on #2212. --- cli/index.ts | 77 ++++++++++++++++++++++++++++++++++----- cli/index_compile_test.ts | 28 +++++++------- 2 files changed, 82 insertions(+), 23 deletions(-) diff --git a/cli/index.ts b/cli/index.ts index ae6b4b7b5..ddc210db3 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -133,6 +133,65 @@ const includeDependentsOption: INamedOption = { } }; +// `compile` reuses the same prune() filtering as run/build, but these flags only +// filter the *printed output* -- the whole project still compiles. The `output-` +// prefix makes that distinction explicit (see PR #2212). +const outputActionsOption: INamedOption = { + name: "output-actions", + option: { + describe: + "A list of action names or patterns to filter the compiled output to. Can include '*' wildcards.", + type: "array", + coerce: (rawActions: string[] | null) => rawActions.map(actions => actions.split(",")).flat() + } +}; + +const outputTagsOption: INamedOption = { + name: "output-tags", + option: { + describe: "A list of tags to filter the compiled output to.", + type: "array", + coerce: (rawTags: string[] | null) => rawTags.map(tags => tags.split(",")).flat() + } +}; + +const outputIncludeDepsOption: INamedOption = { + name: "output-include-deps", + option: { + describe: "If set, dependencies of the selected actions are also included in the output.", + type: "boolean" + }, + check: (argv: yargs.Arguments) => { + if ( + argv[outputIncludeDepsOption.name] && + !(argv[outputActionsOption.name] || argv[outputTagsOption.name]) + ) { + throw new Error( + `The --${outputIncludeDepsOption.name} flag should only be supplied along with --${outputActionsOption.name} or --${outputTagsOption.name}.` + ); + } + } +}; + +const outputIncludeDependentsOption: INamedOption = { + name: "output-include-dependents", + option: { + describe: + "If set, dependents (downstream) of the selected actions are also included in the output.", + type: "boolean" + }, + check: (argv: yargs.Arguments) => { + if ( + argv[outputIncludeDependentsOption.name] && + !(argv[outputActionsOption.name] || argv[outputTagsOption.name]) + ) { + throw new Error( + `The --${outputIncludeDependentsOption.name} flag should only be supplied along with --${outputActionsOption.name} or --${outputTagsOption.name}.` + ); + } + } +}; + const credentialsOption: INamedOption = { name: "credentials", option: { @@ -393,10 +452,10 @@ export function runCli() { dotOutputOption, timeoutOption, quietCompileOption, - actionsOption, - tagsOption, - includeDepsOption, - includeDependentsOption, + outputActionsOption, + outputTagsOption, + outputIncludeDepsOption, + outputIncludeDependentsOption, { name: verboseOptionName, option: { @@ -440,14 +499,14 @@ export function runCli() { // a clean graph; if compilation produced errors we print the full graph // plus the errors, keeping graph-level errors as-is. const hasSelector = - argv[actionsOption.name]?.length > 0 || argv[tagsOption.name]?.length > 0; + argv[outputActionsOption.name]?.length > 0 || argv[outputTagsOption.name]?.length > 0; const outputGraph = hasSelector && !compiledGraphHasErrors(compiledGraph) ? prune(compiledGraph, { - actions: argv[actionsOption.name], - tags: argv[tagsOption.name], - includeDependencies: argv[includeDepsOption.name], - includeDependents: argv[includeDependentsOption.name] + actions: argv[outputActionsOption.name], + tags: argv[outputTagsOption.name], + includeDependencies: argv[outputIncludeDepsOption.name], + includeDependents: argv[outputIncludeDependentsOption.name] }) : compiledGraph; printCompiledGraph(outputGraph, outputType, argv[quietCompileOption.name]); diff --git a/cli/index_compile_test.ts b/cli/index_compile_test.ts index c59c7576d..17153aaed 100644 --- a/cli/index_compile_test.ts +++ b/cli/index_compile_test.ts @@ -423,25 +423,25 @@ suite("compile node selection", ({ afterEach }) => { expect(tableNames(result.stdout)).deep.equals(["downstream", "midstream", "upstream"]); }); - test("--actions filters output to the selected action", async () => { + test("--output-actions filters output to the selected action", async () => { const projectDir = await setupSelectionProject(); const result = await getProcessResult( - execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--actions", "midstream", "--json"]) + execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--output-actions", "midstream", "--json"]) ); expect(result.exitCode, result.stderr).equals(0); expect(tableNames(result.stdout)).deep.equals(["midstream"]); }); - test("--actions --include-deps pulls in upstream dependencies", async () => { + test("--output-actions --output-include-deps pulls in upstream dependencies", async () => { const projectDir = await setupSelectionProject(); const result = await getProcessResult( execFile(nodePath, [ cliEntryPointPath, "compile", projectDir, - "--actions", + "--output-actions", "midstream", - "--include-deps", + "--output-include-deps", "--json" ]) ); @@ -449,16 +449,16 @@ suite("compile node selection", ({ afterEach }) => { expect(tableNames(result.stdout)).deep.equals(["midstream", "upstream"]); }); - test("--actions --include-dependents pulls in downstream dependents", async () => { + test("--output-actions --output-include-dependents pulls in downstream dependents", async () => { const projectDir = await setupSelectionProject(); const result = await getProcessResult( execFile(nodePath, [ cliEntryPointPath, "compile", projectDir, - "--actions", + "--output-actions", "midstream", - "--include-dependents", + "--output-include-dependents", "--json" ]) ); @@ -466,10 +466,10 @@ suite("compile node selection", ({ afterEach }) => { expect(tableNames(result.stdout)).deep.equals(["downstream", "midstream"]); }); - test("--tags filters output to actions carrying the tag", async () => { + test("--output-tags filters output to actions carrying the tag", async () => { const projectDir = await setupSelectionProject(); const result = await getProcessResult( - execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--tags", "daily", "--json"]) + execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--output-tags", "daily", "--json"]) ); expect(result.exitCode, result.stderr).equals(0); expect(tableNames(result.stdout)).deep.equals(["upstream"]); @@ -478,19 +478,19 @@ suite("compile node selection", ({ afterEach }) => { test("selector matching nothing emits an empty graph and exits zero", async () => { const projectDir = await setupSelectionProject(); const result = await getProcessResult( - execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--actions", "nope", "--json"]) + execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--output-actions", "nope", "--json"]) ); expect(result.exitCode, result.stderr).equals(0); expect(tableNames(result.stdout)).deep.equals([]); }); - test("--include-deps without a selector is rejected", async () => { + test("--output-include-deps without a selector is rejected", async () => { const projectDir = await setupSelectionProject(); const result = await getProcessResult( - execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--include-deps", "--json"]) + execFile(nodePath, [cliEntryPointPath, "compile", projectDir, "--output-include-deps", "--json"]) ); expect(result.exitCode).not.equals(0); - expect(result.stderr).contains("--include-deps"); + expect(result.stderr).contains("--output-include-deps"); }); }); From 8746e00afa4f2a69ee09dd33d7db7bff1cc5a910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Luiz=20Braga=20Mallmann?= Date: Wed, 29 Jul 2026 21:22:19 -0300 Subject: [PATCH 3/4] refactor(cli): share coerce and check logic across selection flags Extract `splitCommas` for the comma/repeat-splitting coerce used by `actions`, `tags`, `output-actions` and `output-tags`, and `requiresSelection` for the include-deps/include-dependents checks, so the run/build and compile flag families no longer duplicate the same logic in two places. Claude-Session: https://claude.ai/code/session_01SDrEz3ahcaMu9Zc2pwwpUu --- cli/index.ts | 70 +++++++++++++++++++--------------------------------- 1 file changed, 26 insertions(+), 44 deletions(-) diff --git a/cli/index.ts b/cli/index.ts index ddc210db3..0f8caf477 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -80,12 +80,29 @@ const fullRefreshOption: INamedOption = { } }; +// Splits repeated and comma-separated values into a flat list, e.g. +// `--actions a,b --actions c` -> ["a", "b", "c"]. +const splitCommas = (raw: string[] | null) => raw.map(value => value.split(",")).flat(); + +// It would be nice to use yargs' "implies" to implement this, but it doesn't work for some reason. +const requiresSelection = ( + name: string, + actionsName: string, + tagsName: string +): INamedOption["check"] => (argv: yargs.Arguments) => { + if (argv[name] && !(argv[actionsName] || argv[tagsName])) { + throw new Error( + `The --${name} flag should only be supplied along with --${actionsName} or --${tagsName}.` + ); + } +}; + const actionsOption: INamedOption = { name: "actions", option: { describe: "A list of action names or patterns to run. Can include '*' wildcards.", type: "array", - coerce: (rawActions: string[] | null) => rawActions.map(actions => actions.split(",")).flat() + coerce: splitCommas } }; @@ -94,7 +111,7 @@ const tagsOption: INamedOption = { option: { describe: "A list of tags to filter the actions to run.", type: "array", - coerce: (rawTags: string[] | null) => rawTags.map(tags => tags.split(",")).flat() + coerce: splitCommas } }; @@ -104,14 +121,7 @@ const includeDepsOption: INamedOption = { describe: "If set, dependencies for selected actions will also be run.", type: "boolean" }, - // It would be nice to use yargs' "implies" to implement this, but it doesn't work for some reason. - check: (argv: yargs.Arguments) => { - if (argv[includeDepsOption.name] && !(argv[actionsOption.name] || argv[tagsOption.name])) { - throw new Error( - `The --${includeDepsOption.name} flag should only be supplied along with --${actionsOption.name} or --${tagsOption.name}.` - ); - } - } + check: requiresSelection("include-deps", "actions", "tags") }; const includeDependentsOption: INamedOption = { @@ -120,29 +130,19 @@ const includeDependentsOption: INamedOption = { describe: "If set, dependents (downstream) for selected actions will also be run.", type: "boolean" }, - // It would be nice to use yargs' "implies" to implement this, but it doesn't work for some reason. - check: (argv: yargs.Arguments) => { - if ( - argv[includeDependentsOption.name] && - !(argv[actionsOption.name] || argv[tagsOption.name]) - ) { - throw new Error( - `The --${includeDependentsOption.name} flag should only be supplied along with --${actionsOption.name} or --${tagsOption.name}.` - ); - } - } + check: requiresSelection("include-dependents", "actions", "tags") }; // `compile` reuses the same prune() filtering as run/build, but these flags only // filter the *printed output* -- the whole project still compiles. The `output-` -// prefix makes that distinction explicit (see PR #2212). +// prefix makes that distinction explicit. const outputActionsOption: INamedOption = { name: "output-actions", option: { describe: "A list of action names or patterns to filter the compiled output to. Can include '*' wildcards.", type: "array", - coerce: (rawActions: string[] | null) => rawActions.map(actions => actions.split(",")).flat() + coerce: splitCommas } }; @@ -151,7 +151,7 @@ const outputTagsOption: INamedOption = { option: { describe: "A list of tags to filter the compiled output to.", type: "array", - coerce: (rawTags: string[] | null) => rawTags.map(tags => tags.split(",")).flat() + coerce: splitCommas } }; @@ -161,16 +161,7 @@ const outputIncludeDepsOption: INamedOption = { describe: "If set, dependencies of the selected actions are also included in the output.", type: "boolean" }, - check: (argv: yargs.Arguments) => { - if ( - argv[outputIncludeDepsOption.name] && - !(argv[outputActionsOption.name] || argv[outputTagsOption.name]) - ) { - throw new Error( - `The --${outputIncludeDepsOption.name} flag should only be supplied along with --${outputActionsOption.name} or --${outputTagsOption.name}.` - ); - } - } + check: requiresSelection("output-include-deps", "output-actions", "output-tags") }; const outputIncludeDependentsOption: INamedOption = { @@ -180,16 +171,7 @@ const outputIncludeDependentsOption: INamedOption = { "If set, dependents (downstream) of the selected actions are also included in the output.", type: "boolean" }, - check: (argv: yargs.Arguments) => { - if ( - argv[outputIncludeDependentsOption.name] && - !(argv[outputActionsOption.name] || argv[outputTagsOption.name]) - ) { - throw new Error( - `The --${outputIncludeDependentsOption.name} flag should only be supplied along with --${outputActionsOption.name} or --${outputTagsOption.name}.` - ); - } - } + check: requiresSelection("output-include-dependents", "output-actions", "output-tags") }; const credentialsOption: INamedOption = { From 52c2021aef2182a3caff00b931d5d9bf011a629f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Luiz=20Braga=20Mallmann?= Date: Wed, 29 Jul 2026 21:41:07 -0300 Subject: [PATCH 4/4] fix(cli): prune targets and drop false wildcard claim from --output-actions Follow-up to the shared coerce/check refactor: * `requiresSelection` now takes the selector option objects rather than their names as string literals, so renaming `actions`/`output-actions` can't silently desync the check from the flag it guards. * `prune()` left `targets` untouched, so `compile --output-actions x` printed a single-table graph next to a `targets` list covering the whole project. Nothing downstream reads `targets`, but `compile` prints it, so it now gets filtered with the same included-action set. * `matchPatterns()` matches exactly on the action name or its fully-qualified `database.schema.name`; there is no glob support, so `--output-actions` no longer advertises `'*'` wildcards. Claude-Session: https://claude.ai/code/session_01PnfbDkrf1KoYR2hjp7DEyQ --- cli/api/commands/prune.ts | 5 +++++ cli/index.ts | 21 +++++++++++---------- cli/index_compile_test.ts | 4 ++++ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/cli/api/commands/prune.ts b/cli/api/commands/prune.ts index 71358ea0b..b92c1e94d 100644 --- a/cli/api/commands/prune.ts +++ b/cli/api/commands/prune.ts @@ -20,6 +20,11 @@ export function prune( ), operations: compiledGraph.operations.filter(action => includedActionNames.has(targetAsReadableString(action.target)) + ), + // `targets` is declarative output only (nothing downstream reads it), but + // `compile` prints it, so it has to agree with the filtered action lists. + targets: compiledGraph.targets?.filter(target => + includedActionNames.has(targetAsReadableString(target)) ) }; } diff --git a/cli/index.ts b/cli/index.ts index 0f8caf477..b3fb7e05c 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -87,12 +87,12 @@ const splitCommas = (raw: string[] | null) => raw.map(value => value.split(",")) // It would be nice to use yargs' "implies" to implement this, but it doesn't work for some reason. const requiresSelection = ( name: string, - actionsName: string, - tagsName: string + actions: INamedOption, + tags: INamedOption ): INamedOption["check"] => (argv: yargs.Arguments) => { - if (argv[name] && !(argv[actionsName] || argv[tagsName])) { + if (argv[name] && !(argv[actions.name] || argv[tags.name])) { throw new Error( - `The --${name} flag should only be supplied along with --${actionsName} or --${tagsName}.` + `The --${name} flag should only be supplied along with --${actions.name} or --${tags.name}.` ); } }; @@ -121,7 +121,7 @@ const includeDepsOption: INamedOption = { describe: "If set, dependencies for selected actions will also be run.", type: "boolean" }, - check: requiresSelection("include-deps", "actions", "tags") + check: requiresSelection("include-deps", actionsOption, tagsOption) }; const includeDependentsOption: INamedOption = { @@ -130,7 +130,7 @@ const includeDependentsOption: INamedOption = { describe: "If set, dependents (downstream) for selected actions will also be run.", type: "boolean" }, - check: requiresSelection("include-dependents", "actions", "tags") + check: requiresSelection("include-dependents", actionsOption, tagsOption) }; // `compile` reuses the same prune() filtering as run/build, but these flags only @@ -139,8 +139,9 @@ const includeDependentsOption: INamedOption = { const outputActionsOption: INamedOption = { name: "output-actions", option: { - describe: - "A list of action names or patterns to filter the compiled output to. Can include '*' wildcards.", + // No wildcard support: prune()'s matchPatterns() does exact matching on the + // action name or its fully-qualified `database.schema.name`. + describe: "A list of action names to filter the compiled output to.", type: "array", coerce: splitCommas } @@ -161,7 +162,7 @@ const outputIncludeDepsOption: INamedOption = { describe: "If set, dependencies of the selected actions are also included in the output.", type: "boolean" }, - check: requiresSelection("output-include-deps", "output-actions", "output-tags") + check: requiresSelection("output-include-deps", outputActionsOption, outputTagsOption) }; const outputIncludeDependentsOption: INamedOption = { @@ -171,7 +172,7 @@ const outputIncludeDependentsOption: INamedOption = { "If set, dependents (downstream) of the selected actions are also included in the output.", type: "boolean" }, - check: requiresSelection("output-include-dependents", "output-actions", "output-tags") + check: requiresSelection("output-include-dependents", outputActionsOption, outputTagsOption) }; const credentialsOption: INamedOption = { diff --git a/cli/index_compile_test.ts b/cli/index_compile_test.ts index 17153aaed..c27ac47eb 100644 --- a/cli/index_compile_test.ts +++ b/cli/index_compile_test.ts @@ -430,6 +430,10 @@ suite("compile node selection", ({ afterEach }) => { ); expect(result.exitCode, result.stderr).equals(0); expect(tableNames(result.stdout)).deep.equals(["midstream"]); + // `targets` is printed alongside the actions, so it must be pruned too. + expect(JSON.parse(result.stdout).targets.map((target: any) => target.name)).deep.equals([ + "midstream" + ]); }); test("--output-actions --output-include-deps pulls in upstream dependencies", async () => {