From 847b0ae7a9b5d188f54de151fca27e4920943dfc Mon Sep 17 00:00:00 2001 From: Ruslan Shaydullin Date: Mon, 11 May 2026 01:49:09 +0500 Subject: [PATCH] Add --add-ignore-paths flag to artifact commands The existing --ignore-paths flag replaces the built-in default exclude list (VCS files and common ignored extensions), which forces users to re-specify every default when they only need to add one extra pattern. This change introduces --add-ignore-paths for push/build/diff artifact, which appends its values to whichever ignore set is currently in effect (defaults when --ignore-paths is omitted, or the user override when it is provided). --ignore-paths semantics are unchanged. Signed-off-by: Ruslan Shaydullin Assisted-by: claude-code/claude-opus-4-7 --- cmd/flux/build_artifact.go | 20 +++++++++++- cmd/flux/build_artifact_test.go | 54 +++++++++++++++++++++++++++++++++ cmd/flux/diff_artifact.go | 14 +++++---- cmd/flux/push_artifact.go | 4 ++- 4 files changed, 84 insertions(+), 8 deletions(-) diff --git a/cmd/flux/build_artifact.go b/cmd/flux/build_artifact.go index 1dd681f8..740f3ed0 100644 --- a/cmd/flux/build_artifact.go +++ b/cmd/flux/build_artifact.go @@ -52,17 +52,35 @@ type buildArtifactFlags struct { output string path string ignorePaths []string + addIgnorePaths []string resolveSymlinks bool } var excludeOCI = append(strings.Split(sourceignore.ExcludeVCS, ","), strings.Split(sourceignore.ExcludeExt, ",")...) +// composeIgnorePaths returns the concatenation of two ignore-path slices, +// preserving order. It is used by build, push and diff artifact commands +// to combine --ignore-paths (which replaces the defaults) with +// --add-ignore-paths (which appends to whatever ignore set is currently +// in effect). Duplicate patterns are passed through unchanged since the +// underlying gitignore matcher treats redundant patterns as no-ops. +func composeIgnorePaths(ignorePaths, addIgnorePaths []string) []string { + if len(ignorePaths) == 0 && len(addIgnorePaths) == 0 { + return nil + } + result := make([]string, 0, len(ignorePaths)+len(addIgnorePaths)) + result = append(result, ignorePaths...) + result = append(result, addIgnorePaths...) + return result +} + var buildArtifactArgs buildArtifactFlags func init() { buildArtifactCmd.Flags().StringVarP(&buildArtifactArgs.path, "path", "p", "", "Path to the directory where the Kubernetes manifests are located.") buildArtifactCmd.Flags().StringVarP(&buildArtifactArgs.output, "output", "o", "artifact.tgz", "Path to where the artifact tgz file should be written.") buildArtifactCmd.Flags().StringSliceVar(&buildArtifactArgs.ignorePaths, "ignore-paths", excludeOCI, "set paths to ignore in .gitignore format") + buildArtifactCmd.Flags().StringSliceVar(&buildArtifactArgs.addIgnorePaths, "add-ignore-paths", nil, "additional paths to ignore in .gitignore format (appended to --ignore-paths)") buildArtifactCmd.Flags().BoolVar(&buildArtifactArgs.resolveSymlinks, "resolve-symlinks", false, "resolve symlinks by copying their targets into the artifact") buildCmd.AddCommand(buildArtifactCmd) @@ -100,7 +118,7 @@ func buildArtifactCmdRun(cmd *cobra.Command, args []string) error { logger.Actionf("building artifact from %s", path) ociClient := oci.NewClient(oci.DefaultOptions()) - if err := ociClient.Build(buildArtifactArgs.output, path, buildArtifactArgs.ignorePaths); err != nil { + if err := ociClient.Build(buildArtifactArgs.output, path, composeIgnorePaths(buildArtifactArgs.ignorePaths, buildArtifactArgs.addIgnorePaths)); err != nil { return fmt.Errorf("building artifact failed, error: %w", err) } diff --git a/cmd/flux/build_artifact_test.go b/cmd/flux/build_artifact_test.go index 88ac278f..b1c09462 100644 --- a/cmd/flux/build_artifact_test.go +++ b/cmd/flux/build_artifact_test.go @@ -180,6 +180,60 @@ func Test_resolveSymlinks_cycle(t *testing.T) { g.Expect(os.IsNotExist(err)).To(BeTrue()) } +func Test_composeIgnorePaths(t *testing.T) { + tests := []struct { + name string + ignorePaths []string + addIgnorePaths []string + want []string + }{ + { + name: "both nil returns nil", + ignorePaths: nil, + addIgnorePaths: nil, + want: nil, + }, + { + name: "only ignore-paths passes through", + ignorePaths: []string{"foo", "bar"}, + addIgnorePaths: nil, + want: []string{"foo", "bar"}, + }, + { + name: "only add-ignore-paths passes through", + ignorePaths: nil, + addIgnorePaths: []string{"baz"}, + want: []string{"baz"}, + }, + { + name: "both provided, order preserved", + ignorePaths: []string{"foo", "bar"}, + addIgnorePaths: []string{"baz", "qux"}, + want: []string{"foo", "bar", "baz", "qux"}, + }, + { + name: "duplicates retained as-is", + ignorePaths: []string{"foo", "bar"}, + addIgnorePaths: []string{"foo"}, + want: []string{"foo", "bar", "foo"}, + }, + { + name: "defaults plus add", + ignorePaths: excludeOCI, + addIgnorePaths: []string{"my-secrets/"}, + want: append(append([]string{}, excludeOCI...), "my-secrets/"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + got := composeIgnorePaths(tt.ignorePaths, tt.addIgnorePaths) + g.Expect(got).To(Equal(tt.want)) + }) + } +} + func Test_resolveSymlinks_multipleLinksSameTarget(t *testing.T) { g := NewWithT(t) diff --git a/cmd/flux/diff_artifact.go b/cmd/flux/diff_artifact.go index b7293471..a76c1dc2 100644 --- a/cmd/flux/diff_artifact.go +++ b/cmd/flux/diff_artifact.go @@ -39,11 +39,12 @@ flux diff artifact oci://ghcr.io/stefanprodan/manifests:podinfo:6.2.0 --path=./k } type diffArtifactFlags struct { - path string - creds string - provider flags.SourceOCIProvider - ignorePaths []string - insecure bool + path string + creds string + provider flags.SourceOCIProvider + ignorePaths []string + addIgnorePaths []string + insecure bool } var diffArtifactArgs = newDiffArtifactArgs() @@ -59,6 +60,7 @@ func init() { diffArtifactCmd.Flags().StringVar(&diffArtifactArgs.creds, "creds", "", "credentials for OCI registry in the format [:] if --provider is generic") diffArtifactCmd.Flags().Var(&diffArtifactArgs.provider, "provider", sourceOCIRepositoryArgs.provider.Description()) diffArtifactCmd.Flags().StringSliceVar(&diffArtifactArgs.ignorePaths, "ignore-paths", excludeOCI, "set paths to ignore in .gitignore format") + diffArtifactCmd.Flags().StringSliceVar(&diffArtifactArgs.addIgnorePaths, "add-ignore-paths", nil, "additional paths to ignore in .gitignore format (appended to --ignore-paths)") diffArtifactCmd.Flags().BoolVar(&diffArtifactArgs.insecure, "insecure-registry", false, "allows the remote artifact to be pulled without TLS") diffCmd.AddCommand(diffArtifactCmd) } @@ -109,7 +111,7 @@ func diffArtifactCmdRun(cmd *cobra.Command, args []string) error { } } - if err := ociClient.Diff(ctx, url, diffArtifactArgs.path, diffArtifactArgs.ignorePaths); err != nil { + if err := ociClient.Diff(ctx, url, diffArtifactArgs.path, composeIgnorePaths(diffArtifactArgs.ignorePaths, diffArtifactArgs.addIgnorePaths)); err != nil { return err } diff --git a/cmd/flux/push_artifact.go b/cmd/flux/push_artifact.go index 237c2593..d764c5c1 100644 --- a/cmd/flux/push_artifact.go +++ b/cmd/flux/push_artifact.go @@ -109,6 +109,7 @@ type pushArtifactFlags struct { creds string provider flags.SourceOCIProvider ignorePaths []string + addIgnorePaths []string annotations []string output string debug bool @@ -132,6 +133,7 @@ func init() { pushArtifactCmd.Flags().StringVar(&pushArtifactArgs.creds, "creds", "", "credentials for OCI registry in the format [:] if --provider is generic") pushArtifactCmd.Flags().Var(&pushArtifactArgs.provider, "provider", pushArtifactArgs.provider.Description()) pushArtifactCmd.Flags().StringSliceVar(&pushArtifactArgs.ignorePaths, "ignore-paths", excludeOCI, "set paths to ignore in .gitignore format") + pushArtifactCmd.Flags().StringSliceVar(&pushArtifactArgs.addIgnorePaths, "add-ignore-paths", nil, "additional paths to ignore in .gitignore format (appended to --ignore-paths)") pushArtifactCmd.Flags().StringArrayVarP(&pushArtifactArgs.annotations, "annotations", "a", nil, "Set custom OCI annotations in the format '='") pushArtifactCmd.Flags().StringVarP(&pushArtifactArgs.output, "output", "o", "", "the format in which the artifact digest should be printed, can be 'json' or 'yaml'") @@ -286,7 +288,7 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error { ociClient := oci.NewClient(opts) digestURL, err := ociClient.Push(ctx, url, path, oci.WithPushMetadata(meta), - oci.WithPushIgnorePaths(pushArtifactArgs.ignorePaths...), + oci.WithPushIgnorePaths(composeIgnorePaths(pushArtifactArgs.ignorePaths, pushArtifactArgs.addIgnorePaths)...), ) if err != nil { return fmt.Errorf("pushing artifact failed: %w", err)