diff --git a/api/commands/prune.ts b/api/commands/prune.ts index 4da4d39bc..9a59a9e37 100644 --- a/api/commands/prune.ts +++ b/api/commands/prune.ts @@ -47,6 +47,7 @@ function computeIncludedActionNames( const hasActionSelector = runConfig.actions?.length > 0; const hasTagSelector = runConfig.tags?.length > 0; + const hasIncludeAllTagsSelector = runConfig.includeAllTags?.valueOf(); // If no selectors, return all actions. if (!hasActionSelector && !hasTagSelector) { @@ -64,9 +65,18 @@ function computeIncludedActionNames( // Determine actions selected with --tag option and update applicable actions if (hasTagSelector) { - allActions - .filter(action => action.tags.some(tag => runConfig.tags.includes(tag))) - .forEach(action => includedActionNames.add(targetAsReadableString(action.target))); + // Keep actions wich include every tags in --tag option + if (hasIncludeAllTagsSelector) { + allActions + .filter(action => runConfig.tags.every(tag => action.tags.includes(tag))) + .forEach(action => includedActionNames.add(targetAsReadableString(action.target))); + } + // Keep actions with include at least one tag in --tag option + else { + allActions + .filter(action => action.tags.some(tag => runConfig.tags.includes(tag))) + .forEach(action => includedActionNames.add(targetAsReadableString(action.target))); + } } // Compute all transitive dependencies. diff --git a/cli/index.ts b/cli/index.ts index fc8f59d6a..5d82cec0a 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -98,6 +98,15 @@ const tagsOption: INamedOption = { } }; +const includeAllTagsOption: INamedOption = { + name: "include-all-tags", + option: { + describe: "If set, only actions that include every user-specified tags will be run", + type: "boolean", + default: false + } +} + const includeDepsOption: INamedOption = { name: "include-deps", option: { @@ -581,6 +590,7 @@ export function runCli() { fullRefreshOption, actionsOption, tagsOption, + includeAllTagsOption, includeDepsOption, includeDependentsOption, schemaSuffixOverrideOption, @@ -628,7 +638,8 @@ export function runCli() { actions: argv[actionsOption.name], includeDependencies: argv[includeDepsOption.name], includeDependents: argv[includeDependentsOption.name], - tags: argv[tagsOption.name] + tags: argv[tagsOption.name], + includeAllTags: argv[includeAllTagsOption.name] }, dbadapter ); diff --git a/protos/execution.proto b/protos/execution.proto index c7d1e2fad..cc644e603 100644 --- a/protos/execution.proto +++ b/protos/execution.proto @@ -10,6 +10,7 @@ option go_package = "github.com/dataform-co/dataform/protos/dataform"; message RunConfig { repeated string actions = 1; repeated string tags = 5; + bool include_all_tags = 10; bool include_dependencies = 3; bool include_dependents = 11; bool full_refresh = 2; diff --git a/tests/api/api.spec.ts b/tests/api/api.spec.ts index 42a3842f2..b571ee913 100644 --- a/tests/api/api.spec.ts +++ b/tests/api/api.spec.ts @@ -291,6 +291,11 @@ suite("@dataform/api", () => { target: { schema: "schema", name: "op_d" }, tags: ["tag3"], queries: ["create or replace view schema.someview as select 1 as test"] + }, + { + target: { schema: "schema", name: "op_e" }, + tags: ["tag1", "tag2"], + queries: ["create or replace view schema.someview as select 1 as test"] } ], tables: [ @@ -395,6 +400,23 @@ suite("@dataform/api", () => { expect(actionNames).includes("schema.tab_a"); }); + test("prune actions with --tags (with include all tags)", () => { + const prunedGraph = prune(TEST_GRAPH_WITH_TAGS, { + tags: ["tag1", "tag2"], + includeAllTags: true + }); + const actionNames = [ + ...prunedGraph.tables.map(action => targetAsReadableString(action.target)), + ...prunedGraph.operations.map(action => targetAsReadableString(action.target)) + ]; + expect(actionNames).includes("schema.op_e"); + expect(actionNames).includes("schema.tab_a"); + expect(actionNames).not.includes("schema.op_a"); + expect(actionNames).not.includes("schema.op_b"); + expect(actionNames).not.includes("schema.op_c"); + expect(actionNames).not.includes("schema.op_d"); + }); + test("prune actions with --actions with dependencies", () => { const prunedGraph = prune(TEST_GRAPH, { actions: ["schema.a"], includeDependencies: true }); const actionNames = [