Skip to content

feat: exclude stacks from the selection with ! patterns - #1920

Open
badmintoncryer wants to merge 7 commits into
aws:mainfrom
badmintoncryer:fix/758-stack-selection-exclusion-patterns
Open

feat: exclude stacks from the selection with ! patterns#1920
badmintoncryer wants to merge 7 commits into
aws:mainfrom
badmintoncryer:fix/758-stack-selection-exclusion-patterns

Conversation

@badmintoncryer

@badmintoncryer badmintoncryer commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Fixes #1908
Resolves #758

Implements @mrgrain's snippet from #1909. (That PR could not be reopened after the force push, so this is a new one.)

picomatch.parse().negated splits the patterns into positives and negations, and the selection is OR over the positives, AND over the negations. Every pattern is compiled exactly as picomatch defines it - a negated matcher accepts the stacks that survive it - so nothing is ever stripped or rewritten, and !(A|B) stays an extglob.

$ cdk ls '!Prod/Canary' '!Prod/StackB'   # before: all 4 stacks
DevStack
Prod/StackA

Two changes from the snippet:

  • scan().negated reports true for !!Stack where picomatch matches it as a positive, and slicing the ! prefix off turns '!' into an empty pattern that picomatch refuses to compile. parse().negated is the flag the matcher itself uses, and compiling the raw pattern sidesteps the rest.
  • positives.length === 0 meaning "everything" would make patterns: [] select every stack, which select behavior: none in cloud-assembly.test.ts pins down. Now it needs at least one negation.

Also:

  • Patterns without ! are unchanged and no existing test needed changes. The new selection is always a subset of the old one, so the change only ever narrows: anyone leaning on the union bug, e.g. 'Prod/*' '!Prod/Canary', will stop deploying Canary. That is the point of the fix, but say the word if you'd rather have it warn for a release first.
  • The does not exist warning compared each raw pattern against the selection it produced. With subtraction in play that turns false ('Stack1' '!Stack1' would warn that Stack1 does not exist), so patterns are now checked against the assembly, negations with the ! off - which also gets destroy '!stack1' its casing suggestion.
  • Excluding a dependency of a selected stack does not drop it, --exclusively still does. Test + README.

toolkit-lib only, so deploy / diff / synth / destroy / ls all get this.

Checklist

  • This change contains a major version upgrade for a dependency and I confirm all breaking changes are addressed
    • Release notes for the new version:

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@codecov-commenter

codecov-commenter commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.33%. Comparing base (5b77563) to head (966fcf6).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1920   +/-   ##
=======================================
  Coverage   91.33%   91.33%           
=======================================
  Files          79       79           
  Lines       12164    12164           
  Branches     1721     1721           
=======================================
  Hits        11110    11110           
  Misses       1019     1019           
  Partials       35       35           
Flag Coverage Δ
suite.unit 91.33% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@badmintoncryer
badmintoncryer force-pushed the fix/758-stack-selection-exclusion-patterns branch from 8b81835 to b8146c7 Compare September 1, 2026 04:21
@badmintoncryer
badmintoncryer force-pushed the fix/758-stack-selection-exclusion-patterns branch from b8146c7 to 644a5b2 Compare September 1, 2026 04:28
@badmintoncryer
badmintoncryer force-pushed the fix/758-stack-selection-exclusion-patterns branch from 644a5b2 to b2fc522 Compare September 1, 2026 04:30
@badmintoncryer
badmintoncryer force-pushed the fix/758-stack-selection-exclusion-patterns branch from b2fc522 to 32e71ee Compare September 1, 2026 04:47
@badmintoncryer
badmintoncryer marked this pull request as ready for review September 1, 2026 05:21
@badmintoncryer
badmintoncryer force-pushed the fix/758-stack-selection-exclusion-patterns branch from 32e71ee to 735e910 Compare September 1, 2026 05:24
@badmintoncryer
badmintoncryer marked this pull request as draft September 1, 2026 06:50
Stack patterns are matched with picomatch, which reads a leading `!` as a
negation. Every pattern was matched on its own and the results unioned, so
`!A !B` asked for "not A" and "not B" and got back the union of the two,
which is every stack there is.

Patterns are now split into the ones that select and the ones that exclude:
the selection is the union of the former, minus everything the latter match.
picomatch's own `scan()` draws the line, so `!(A|B)` stays the extglob it is
and `!!A` stays a positive.
@badmintoncryer
badmintoncryer force-pushed the fix/758-stack-selection-exclusion-patterns branch from 735e910 to e94875c Compare September 1, 2026 07:03
@badmintoncryer badmintoncryer changed the title feat(toolkit-lib): exclude stacks from the selection with ! feat: exclude stacks from the selection with ! patterns Sep 1, 2026

@mrgrain mrgrain left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Not as easy as I hoped.

Comment thread packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/stack-assembly.ts Outdated
$ cdk deploy 'PipelineStack/Prod/**' '!PipelineStack/Prod/Canary'
```

The selection is the union of the other patterns, minus everything the exclusions match; `!(...)` is extglob syntax, not an exclusion. When you only pass exclusions, they apply to every stack in the app. Note that `--all` cannot be combined with patterns, so use `**` when you want to spell out the starting point. Stacks that a selected stack depends on are still added to the deployment even if you excluded them; pass `--exclusively` (`-e`) to keep them out.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"!(...) is extglob syntax, not an exclusion." this is probably correct, but not sure what it is supposed to mean 😅

"When you only pass exclusions, they apply to every stack in the app." similar here.

But don't worry about it. I'll adjust this before me merge.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much...

@badmintoncryer
badmintoncryer force-pushed the fix/758-stack-selection-exclusion-patterns branch from e94875c to f4cfbb5 Compare September 2, 2026 03:38
@badmintoncryer
badmintoncryer force-pushed the fix/758-stack-selection-exclusion-patterns branch from f4cfbb5 to 1535c67 Compare September 2, 2026 03:42
@badmintoncryer
badmintoncryer force-pushed the fix/758-stack-selection-exclusion-patterns branch from 1535c67 to ea350e8 Compare September 2, 2026 09:33
Negation status now comes from parse().negated, the flag matching itself
uses, instead of counting `!`s in the scan() prefix. Every pattern is
compiled raw - a negated matcher accepts the stacks that survive it - so
nothing is stripped any more. Slicing the prefix off broke on a bare `!`,
which left an empty pattern that picomatch refuses to compile.
@badmintoncryer
badmintoncryer force-pushed the fix/758-stack-selection-exclusion-patterns branch from ea350e8 to 0aedbf0 Compare September 2, 2026 09:37
@badmintoncryer
badmintoncryer marked this pull request as ready for review September 2, 2026 12:40

@mrgrain mrgrain left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some small performance optimizations please, otherwise great!

* stack but that one. No patterns at all still selects nothing.
*/
function matcherFor(patterns: string[]): (hierarchicalId: string) => boolean {
const matchers = patterns.map(pattern => ({ negates: picomatch.parse(pattern).negated, matches: picomatch(pattern) }));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like picomatch(pattern, undefined, true).state is the same result as picomatch.parse(pattern) so we can save a parse here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@badmintoncryer I think this is still missing?

Comment thread packages/@aws-cdk/toolkit-lib/lib/api/cloud-assembly/private/stack-assembly.ts Outdated
const positives: picomatch.Matcher[] = [];
const negatives: picomatch.Matcher[] = [];
for (const pattern of patterns) {
(picomatch.parse(pattern).negated ? negatives : positives).push(picomatch(pattern));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(picomatch.parse(pattern).negated ? negatives : positives).push(picomatch(pattern));
const parsed = picomatch(pattern, undefined, true);
(parsed.state.negated ? negatives : positives).push(parsed);

I think this saves a parse?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

aws-cdk: Add --except flag in CDK CLI (cli): only one stack exclusion pattern takes effect

3 participants