feat(task): support exclusion groups in task name wildcards#34506
Conversation
Adds a `(!a|b|c)` exclusion syntax (modeled after `concurrently`) to `deno task` wildcard patterns. For example, `deno task "test:*(!e2e|interactive)"` now matches `test:unit` and `test:integration` but skips `test:e2e` and `test:interactive`. Closes #29355
fibibot
left a comment
There was a problem hiding this comment.
lint debug linux-x86_64 is red on this head — clippy collapsible_if at cli/tools/task.rs:1238. Collapse the nested if let Some(m) = caps.get(i) { if exclusions.iter()… } into a let-chain: if let Some(m) = caps.get(i) && exclusions.iter().any(|e| e == m.as_str()). Thats the only blocker.
One question, non-blocking: switching the anchor from ^{pat} to ^{pat}$ changes matching for non-trailing wildcards beyond the exclusion feature — e.g. *:test previously matched a:test:b (prefix) and now requires the name to end in :test. If that tightening is intended, fine; if not, the $ isnt needed for exclusions since the greedy (.*) capture already reaches the end. Worth a one-line confirmation.
- nit:
test:*(!)parses to a single empty-string exclusion, so it would drop names whose*captured the empty string. Harmless but slightly surprising.
- Collapse the nested `if let Some(m) = caps.get(i) { if exclusions… }`
into a let-chain to fix the clippy `collapsible_if` lint that was
failing CI.
- Drop the trailing `$` anchor from the generated regex. The exclusion
feature only needs the capture group, and the original behavior
matched names that started with the pattern (so `*:test` still
matches `a:test:b`). Keeping the `$` would have been an unrelated
tightening of wildcard matching.
lunadogbot
left a comment
There was a problem hiding this comment.
Commit 613c5ca addresses both items from my prior REQUEST_CHANGES:
- Clippy
collapsible_if— fixed via the let-chainif let Some(m) = caps.get(i) && exclusions.iter().any(|e| e == m.as_str()).lint debug linux-x86_64is now passing. - Trailing
$anchor — dropped. Regex is back to^{pat}only, so the prefix-match semantics of*:testmatchinga:test:bare preserved and the exclusion change isn't piggybacking on an unrelated tightening of wildcard matching.
Feature scope is tight: trailing (!a|b|c) parsed off the end, captures from * checked against the exclusion list, exclusion-without-wildcard rejected with a clear error message. Tests cover the canonical case, single-token exclusion (*(!build)), sub-prefix matching (test:e2e:smoke vs test:e2e), and the no-wildcard error path.
The leftover nit (test:*(!) parses to a single empty-string exclusion) is unaddressed but was tagged as harmless and isn't worth blocking on.
CI is fully green.
LGTM.
…#34506) Adds a `(!a|b|c)` exclusion syntax to `deno task` wildcard patterns, modeled after [concurrently][cc]. For example, `deno task "test:*(!e2e|interactive)"` now matches `test:unit` and `test:integration` but skips `test:e2e` and `test:interactive`. The exclusion group is parsed off the end of the argument, and each listed value is matched against what a `*` in the pattern captured. A pattern without a `*` but with a `(!...)` group is rejected, since there is nothing to exclude from. This is the common case from denoland#29355; more elaborate forms (multiple exclusion groups, sub-wildcards inside the group) are intentionally not handled. Closes denoland#29355.
Adds a
(!a|b|c)exclusion syntax todeno taskwildcard patterns,modeled after concurrently. For example,
deno task "test:*(!e2e|interactive)"now matchestest:unitandtest:integrationbut skipstest:e2eandtest:interactive.The exclusion group is parsed off the end of the argument, and each
listed value is matched against what a
*in the pattern captured. Apattern without a
*but with a(!...)group is rejected, sincethere is nothing to exclude from.
This is the common case from #29355; more elaborate forms (multiple
exclusion groups, sub-wildcards inside the group) are intentionally
not handled.
Closes #29355.