From 4526aaec6444e4dee5e0e93754de68feb6d1edc4 Mon Sep 17 00:00:00 2001 From: vincent-onebrief Date: Thu, 13 Nov 2025 09:51:40 -0500 Subject: [PATCH] feat!: respect component selector ordering Signed-off-by: vincent-onebrief --- site/src/content/docs/ref/components.mdx | 7 +++++ src/pkg/packager/filters/utils.go | 35 +++++++++++------------- src/pkg/packager/filters/utils_test.go | 5 ++-- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/site/src/content/docs/ref/components.mdx b/site/src/content/docs/ref/components.mdx index d5a62cf437..f45b94da70 100644 --- a/site/src/content/docs/ref/components.mdx +++ b/site/src/content/docs/ref/components.mdx @@ -313,6 +313,13 @@ If you have any `default` components in a package definition you can also exclud $ zarf package deploy ./path/to/package.tar.zst --components=optional-component-1,-default-component-1 ``` +For components that match multiple selectors, the last selector takes priority. + +```bash +# exclude all components except component-1 +$ zarf package deploy ./path/to/package.tar.zst --components=-*,component-1 +``` + ::: ## Extensions (Removed) diff --git a/src/pkg/packager/filters/utils.go b/src/pkg/packager/filters/utils.go index 6a95d00f70..57adeb1b9f 100644 --- a/src/pkg/packager/filters/utils.go +++ b/src/pkg/packager/filters/utils.go @@ -18,33 +18,30 @@ const ( ) func includedOrExcluded(componentName string, requestedComponentNames []string) (selectState, string, error) { - // Check if the component has a leading dash indicating it should be excluded - this is done first so that exclusions precede inclusions - for _, requestedComponent := range requestedComponentNames { - if strings.HasPrefix(requestedComponent, "-") { - // If the component glob matches one of the requested components, then return true - // This supports globbing with "path" in order to have the same behavior across OSes (if we ever allow namespaced components with /) - matched, err := path.Match(strings.TrimPrefix(requestedComponent, "-"), componentName) - if err != nil { - return unknown, "", err - } - if matched { - return excluded, requestedComponent, nil - } - } - } - // Check if the component matches a glob pattern and should be included + // In unmatched cases we don't know if we should include or exclude yet + var match string + var matchType = unknown + + // Check every component request, so last match get's priority + // Order is assumed to match user input for _, requestedComponent := range requestedComponentNames { // If the component glob matches one of the requested components, then return true // This supports globbing with "path" in order to have the same behavior across OSes (if we ever allow namespaced components with /) - matched, err := path.Match(requestedComponent, componentName) + matched, err := path.Match(strings.TrimPrefix(requestedComponent, "-"), componentName) if err != nil { return unknown, "", err } + if matched { - return included, requestedComponent, nil + match = requestedComponent + // Exclusions are requests with the leading "-" + if strings.HasPrefix(requestedComponent, "-") { + matchType = excluded + } else { + matchType = included + } } } - // All other cases we don't know if we should include or exclude yet - return unknown, "", nil + return matchType, match, nil } diff --git a/src/pkg/packager/filters/utils_test.go b/src/pkg/packager/filters/utils_test.go index 59fc2d1c96..a46091ff44 100644 --- a/src/pkg/packager/filters/utils_test.go +++ b/src/pkg/packager/filters/utils_test.go @@ -45,10 +45,9 @@ func Test_includedOrExcluded(t *testing.T) { name: "Test when component is excluded and included", componentName: "example", requestedComponentNames: []string{"-example", "example"}, - wantState: excluded, - wantRequestedComponent: "-example", + wantState: included, + wantRequestedComponent: "example", }, - // interesting case, excluded wins { name: "Test when component is included and excluded", componentName: "example",