From 6abab2d8e925ffdf442fe90dc0414cb7a0822a7d Mon Sep 17 00:00:00 2001 From: SurbhiAgarwal1 Date: Tue, 1 Sep 2026 15:51:36 +0530 Subject: [PATCH 1/3] refactor(printer): unify stateful context-aware printer (#4698) - Consolidate CLI and Porch klog printer logic into an exported Printer interface in pkg/printer. - Introduce stateful contextual scoping via WithField, WithFields, WithPackage, and WithFunction. - Provide structured lifecycle event printing (PrintRunning, PrintPass, PrintFail, PrintResult, PrintSummary). - Remove duplicate packagePrinter implementation in pkg/lib/kptops/render.go. - Add comprehensive unit tests verifying stateful field scoping and formatting. Signed-off-by: SurbhiAgarwal1 --- pkg/lib/kptops/render.go | 53 +----- pkg/lib/kptops/render_test.go | 14 +- pkg/printer/fake/fake.go | 19 +++ pkg/printer/printer.go | 293 +++++++++++++++++++++++++++++----- pkg/printer/printer_test.go | 61 +++++++ 5 files changed, 344 insertions(+), 96 deletions(-) diff --git a/pkg/lib/kptops/render.go b/pkg/lib/kptops/render.go index 120bfe4da6..8b8728b21c 100644 --- a/pkg/lib/kptops/render.go +++ b/pkg/lib/kptops/render.go @@ -16,16 +16,11 @@ package kptops import ( "context" - "fmt" - "io" - "os" fnresultv1 "github.com/kptdev/kpt/api/fnresult/v1" "github.com/kptdev/kpt/pkg/fn" - "github.com/kptdev/kpt/pkg/lib/pkg" "github.com/kptdev/kpt/pkg/lib/runneroptions" "github.com/kptdev/kpt/pkg/printer" - "k8s.io/klog/v2" "sigs.k8s.io/kustomize/kyaml/filesys" ) @@ -51,51 +46,5 @@ func (r *renderer) Render(ctx context.Context, pkg filesys.FileSystem, opts fn.R FileSystem: pkg, RunnerOptions: r.runnerOptions, } - return rr.Execute(printer.WithContext(ctx, &packagePrinter{})) -} - -type packagePrinter struct{} - -var _ printer.Printer = &packagePrinter{} - -const ( - packagePrefixFormat = "Package %q:" - logDepth = 2 -) - -func (p *packagePrinter) PrintPackage(pkg *pkg.Pkg, _ bool) { - p.printfDepth(logDepth, packagePrefixFormat, pkg.DisplayPath) -} - -func (p *packagePrinter) Printf(format string, args ...any) { - p.printfDepth(logDepth, format, args...) -} - -func (p *packagePrinter) printfDepth(depth int, format string, args ...any) { - klog.InfofDepth(depth, format, args...) -} - -func (p *packagePrinter) OptPrintf(opt *printer.Options, format string, args ...any) { - if opt == nil { - p.Printf(format, args...) - return - } - var prefix string - switch { - case opt.PkgDisplayName != "": - prefix = fmt.Sprintf(packagePrefixFormat, opt.PkgDisplayName) - case !opt.PkgDisplayPath.Empty(): - prefix = fmt.Sprintf(packagePrefixFormat, string(opt.PkgDisplayPath)) - case !opt.PkgPath.Empty(): - prefix = fmt.Sprintf(packagePrefixFormat, string(opt.PkgPath)) - } - p.printfDepth(logDepth, prefix+format, args...) -} - -func (p *packagePrinter) OutStream() io.Writer { - return os.Stdout -} - -func (p *packagePrinter) ErrStream() io.Writer { - return os.Stderr + return rr.Execute(printer.WithContext(ctx, printer.NewKlogPrinter())) } diff --git a/pkg/lib/kptops/render_test.go b/pkg/lib/kptops/render_test.go index 394c2cf906..e46502a172 100644 --- a/pkg/lib/kptops/render_test.go +++ b/pkg/lib/kptops/render_test.go @@ -163,7 +163,7 @@ func TestPackagePrinter(t *testing.T) { func TestPackagePrinterStub(t *testing.T) { t.Run("PrintPackage stub", func(t *testing.T) { - p := &packagePrinter{} + p := printer.NewKlogPrinter() testPkg := &pkg.Pkg{ DisplayPath: "test/path", } @@ -178,7 +178,7 @@ func TestPackagePrinterStub(t *testing.T) { }) t.Run("Printf stub", func(t *testing.T) { - p := &packagePrinter{} + p := printer.NewKlogPrinter() assert.NotPanics(t, func() { p.Printf("test message") @@ -190,7 +190,7 @@ func TestPackagePrinterStub(t *testing.T) { }) t.Run("OptPrintf stub with nil options", func(t *testing.T) { - p := &packagePrinter{} + p := printer.NewKlogPrinter() assert.NotPanics(t, func() { p.OptPrintf(nil, "test message") @@ -198,7 +198,7 @@ func TestPackagePrinterStub(t *testing.T) { }) t.Run("OptPrintf stub with options", func(t *testing.T) { - p := &packagePrinter{} + p := printer.NewKlogPrinter() opt := printer.NewOpt().DisplayName("my-package") assert.NotPanics(t, func() { @@ -207,7 +207,7 @@ func TestPackagePrinterStub(t *testing.T) { }) t.Run("OutStream stub", func(t *testing.T) { - p := &packagePrinter{} + p := printer.NewKlogPrinter() stream := p.OutStream() assert.NotNil(t, stream) @@ -215,7 +215,7 @@ func TestPackagePrinterStub(t *testing.T) { }) t.Run("ErrStream stub", func(t *testing.T) { - p := &packagePrinter{} + p := printer.NewKlogPrinter() stream := p.ErrStream() assert.NotNil(t, stream) @@ -236,7 +236,7 @@ func TestPrinterLoggingDepth(t *testing.T) { } expectedFile := filepath.Base(filename) - p := &packagePrinter{} + p := printer.NewKlogPrinter() tests := []struct { name string diff --git a/pkg/printer/fake/fake.go b/pkg/printer/fake/fake.go index 0821398061..105b7ec374 100644 --- a/pkg/printer/fake/fake.go +++ b/pkg/printer/fake/fake.go @@ -17,6 +17,7 @@ package fake import ( "context" "io" + "time" "github.com/kptdev/kpt/pkg/lib/pkg" "github.com/kptdev/kpt/pkg/printer" @@ -29,6 +30,24 @@ type Printer struct { errStream io.Writer } +func (np *Printer) WithField(string, string) printer.Printer { return np } + +func (np *Printer) WithFields(printer.ContextualFields) printer.Printer { return np } + +func (np *Printer) WithPackage(string) printer.Printer { return np } + +func (np *Printer) WithFunction(string, string) printer.Printer { return np } + +func (np *Printer) PrintRunning(string, int) {} + +func (np *Printer) PrintPass(string, time.Duration) {} + +func (np *Printer) PrintFail(string, time.Duration, error) {} + +func (np *Printer) PrintResult(string, string, string) {} + +func (np *Printer) PrintSummary(int, int, time.Duration) {} + func (np *Printer) PrintPackage(*pkg.Pkg, bool) {} func (np *Printer) OptPrintf(*printer.Options, string, ...any) {} diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go index 07e3d8e62c..1524dee7ed 100644 --- a/pkg/printer/printer.go +++ b/pkg/printer/printer.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package printer defines utilities to display kpt CLI output. +// Package printer defines utilities to display kpt CLI and Porch output. package printer import ( @@ -20,9 +20,15 @@ import ( "fmt" "io" "os" + "sort" + "strconv" + "strings" + "sync" + "time" kptfilev1 "github.com/kptdev/kpt/api/kptfile/v1" "github.com/kptdev/kpt/pkg/lib/pkg" + "k8s.io/klog/v2" ) // TruncateOutput defines should output be truncated @@ -30,15 +36,33 @@ var TruncateOutput bool const ( packagePrefixFormat = "Package %q:" + defaultLogDepth = 2 ) -// Printer defines capabilities to display content in kpt CLI. -// The main intention, at the moment, is to abstract away printing -// output in the CLI so that we can evolve the kpt CLI UX. +// ContextualFields holds key-value metadata pairs (e.g. image, tag, package, requestID, user). +type ContextualFields map[string]string + +// Printer defines capabilities to display content in kpt CLI and Porch. type Printer interface { + // Contextual scoping methods (returns a child Printer with updated key-value fields) + WithField(key, value string) Printer + WithFields(fields ContextualFields) Printer + WithPackage(pkgName string) Printer + WithFunction(image, tag string) Printer + + // Structured lifecycle events + PrintRunning(fnRef string, resourceCount int) + PrintPass(fnRef string, duration time.Duration) + PrintFail(fnRef string, duration time.Duration, err error) + PrintResult(severity, msg string, targetRef string) + PrintSummary(executedFnCnt, pkgCnt int, totalTime time.Duration) + + // Legacy printing methods PrintPackage(pkg *pkg.Pkg, leadingNewline bool) Printf(format string, args ...any) OptPrintf(opt *Options, format string, args ...any) + + // Stream accessors OutStream() io.Writer ErrStream() io.Writer } @@ -77,7 +101,7 @@ func (opt *Options) DisplayName(name string) *Options { return opt } -// New returns an instance of Printer. +// New returns an instance of stream-based Printer for kpt CLI. func New(outStream, errStream io.Writer) Printer { if outStream == nil { outStream = os.Stdout @@ -88,79 +112,274 @@ func New(outStream, errStream io.Writer) Printer { return &printer{ outStream: outStream, errStream: errStream, + fields: make(ContextualFields), } } -// printer implements default Printer to be used in kpt codebase. +// NewKlogPrinter returns a Printer that writes logs using klog.InfofDepth. +func NewKlogPrinter() Printer { + return &printer{ + outStream: os.Stdout, + errStream: os.Stderr, + logFn: func(depth int, format string, args ...any) { + klog.InfofDepth(depth, format, args...) + }, + fields: make(ContextualFields), + } +} + +// NewWithLogFunc returns a Printer that delegates log printing to a custom log function. +func NewWithLogFunc(logFn func(depth int, format string, args ...any)) Printer { + return &printer{ + outStream: os.Stdout, + errStream: os.Stderr, + logFn: logFn, + fields: make(ContextualFields), + } +} + +// printer implements Printer for kpt CLI and Porch. type printer struct { + mu sync.RWMutex outStream io.Writer errStream io.Writer + logFn func(depth int, format string, args ...any) + fields ContextualFields } -// The key type is unexported to prevent collisions with context keys defined in -// other packages. -type contextKey int +// clone creates a copy of the printer with cloned contextual fields. +func (pr *printer) clone() *printer { + pr.mu.RLock() + defer pr.mu.RUnlock() -// printerKey is the context key for the printer. Its value of zero is -// arbitrary. If this package defined other context keys, they would have -// different integer values. -const printerKey contextKey = 0 + newFields := make(ContextualFields, len(pr.fields)) + for k, v := range pr.fields { + newFields[k] = v + } + + return &printer{ + outStream: pr.outStream, + errStream: pr.errStream, + logFn: pr.logFn, + fields: newFields, + } +} -// OutStream returns the StdOut stream, this can be used by callers to print -// command output to stdout, do not print error/debug logs to this stream +// WithField returns a child Printer with the specified key-value field attached. +func (pr *printer) WithField(key, value string) Printer { + child := pr.clone() + if value != "" { + child.fields[key] = value + } else { + delete(child.fields, key) + } + return child +} + +// WithFields returns a child Printer with the provided key-value fields attached. +func (pr *printer) WithFields(fields ContextualFields) Printer { + child := pr.clone() + for k, v := range fields { + if v != "" { + child.fields[k] = v + } else { + delete(child.fields, k) + } + } + return child +} + +// WithPackage returns a child Printer with the package field attached. +func (pr *printer) WithPackage(pkgName string) Printer { + return pr.WithField("package", pkgName) +} + +// WithFunction returns a child Printer with image and tag fields attached. +func (pr *printer) WithFunction(image, tag string) Printer { + p := pr.WithField("image", image) + if tag != "" { + p = p.WithField("tag", tag) + } + return p +} + +// OutStream returns the StdOut stream. func (pr *printer) OutStream() io.Writer { return pr.outStream } -// ErrStream returns the StdErr stream, this can be used by callers to print -// command output to stderr, print only error/debug/info logs to this stream +// ErrStream returns the StdErr stream. func (pr *printer) ErrStream() io.Writer { return pr.errStream } -// PrintPackage prints the package display path to stderr +// formatFields returns sorted, formatted key-value pairs (e.g. image="set-labels" tag="latest"). +func (pr *printer) formatFields(extraFields ...ContextualFields) string { + pr.mu.RLock() + combined := make(ContextualFields, len(pr.fields)) + for k, v := range pr.fields { + combined[k] = v + } + pr.mu.RUnlock() + + for _, ef := range extraFields { + for k, v := range ef { + if v != "" { + combined[k] = v + } + } + } + + if len(combined) == 0 { + return "" + } + + keys := make([]string, 0, len(combined)) + for k := range combined { + keys = append(keys, k) + } + sort.Strings(keys) + + var sb strings.Builder + for i, k := range keys { + if i > 0 { + sb.WriteString(" ") + } + sb.WriteString(fmt.Sprintf("%s=%s", k, strconv.Quote(combined[k]))) + } + return sb.String() +} + +func (pr *printer) printInternal(format string, args ...any) { + if pr.logFn != nil { + pr.logFn(defaultLogDepth+1, format, args...) + } else { + fmt.Fprintf(pr.errStream, format, args...) + } +} + +// PrintRunning outputs a [RUNNING] lifecycle event. +func (pr *printer) PrintRunning(fnRef string, resourceCount int) { + extra := ContextualFields{} + if resourceCount > 0 { + extra["resourceCount"] = strconv.Itoa(resourceCount) + } + attrStr := pr.formatFields(extra) + + if attrStr != "" { + pr.printInternal("[RUNNING] %s\n", attrStr) + } else if resourceCount > 0 { + pr.printInternal("[RUNNING] %s on %d resource(s)\n", strconv.Quote(fnRef), resourceCount) + } else { + pr.printInternal("[RUNNING] %s\n", strconv.Quote(fnRef)) + } +} + +// PrintPass outputs a [PASS] lifecycle event. +func (pr *printer) PrintPass(fnRef string, duration time.Duration) { + extra := ContextualFields{} + if duration > 0 { + extra["time"] = duration.Truncate(time.Millisecond).String() + } + attrStr := pr.formatFields(extra) + + if attrStr != "" { + pr.printInternal("[PASS] %s\n", attrStr) + } else { + pr.printInternal("[PASS] %q in %v\n", fnRef, duration.Truncate(time.Millisecond)) + } +} + +// PrintFail outputs a [FAIL] lifecycle event. +func (pr *printer) PrintFail(fnRef string, duration time.Duration, err error) { + extra := ContextualFields{} + if duration > 0 { + extra["time"] = duration.Truncate(time.Millisecond).String() + } + if err != nil { + extra["error"] = err.Error() + } + attrStr := pr.formatFields(extra) + + if attrStr != "" { + pr.printInternal("[FAIL] %s\n", attrStr) + } else { + pr.printInternal("[FAIL] %q in %v\n", fnRef, duration.Truncate(time.Millisecond)) + } +} + +// PrintResult outputs a structured result item line. +func (pr *printer) PrintResult(severity, msg string, targetRef string) { + if targetRef != "" { + pr.printInternal(" [%s] %s: %s\n", severity, targetRef, msg) + } else { + pr.printInternal(" [%s]: %s\n", severity, msg) + } +} + +// PrintSummary outputs a pipeline execution summary line. +func (pr *printer) PrintSummary(executedFnCnt, pkgCnt int, totalTime time.Duration) { + extra := ContextualFields{} + if totalTime > 0 { + extra["time"] = totalTime.Truncate(time.Millisecond).String() + } + attrStr := pr.formatFields(extra) + + if attrStr != "" { + pr.printInternal("Successfully executed %d function(s) in %d package(s) %s\n", executedFnCnt, pkgCnt, attrStr) + } else { + pr.printInternal("Successfully executed %d function(s) in %d package(s).\n", executedFnCnt, pkgCnt) + } +} + +// PrintPackage prints the package display path. func (pr *printer) PrintPackage(p *pkg.Pkg, leadingNewline bool) { - if leadingNewline { + if leadingNewline && pr.logFn == nil { fmt.Fprint(pr.errStream, "\n") } - fmt.Fprintf(pr.errStream, "Package %q:\n", p.DisplayPath) + if pr.logFn != nil { + pr.logFn(defaultLogDepth+1, packagePrefixFormat, p.DisplayPath) + } else { + fmt.Fprintf(pr.errStream, "Package %q:\n", p.DisplayPath) + } } // Printf is the wrapper over fmt.Printf that displays the output. -// this will print messages to stderr stream func (pr *printer) Printf(format string, args ...any) { - fmt.Fprintf(pr.errStream, format, args...) + pr.printInternal(format, args...) } -// OptPrintf is the wrapper over fmt.Printf that displays the output according -// to the opt, this will print messages to stderr stream -// https://mehulkar.com/blog/2017/11/stdout-vs-stderr/ +// OptPrintf is the wrapper over fmt.Printf that displays output according to the options. func (pr *printer) OptPrintf(opt *Options, format string, args ...any) { if opt == nil { - fmt.Fprintf(pr.errStream, format, args...) + pr.printInternal(format, args...) return } - o := pr.errStream + + var prefix string switch { case opt.PkgDisplayName != "": - format = fmt.Sprintf(packagePrefixFormat, opt.PkgDisplayName) + format + prefix = fmt.Sprintf(packagePrefixFormat, opt.PkgDisplayName) case !opt.PkgDisplayPath.Empty(): - format = fmt.Sprintf(packagePrefixFormat, string(opt.PkgDisplayPath)) + format + prefix = fmt.Sprintf(packagePrefixFormat, string(opt.PkgDisplayPath)) case !opt.PkgPath.Empty(): - // try to print relative path of the pkg if we can else use abs path relPath, err := opt.PkgPath.RelativePath() if err != nil { relPath = string(opt.PkgPath) } - format = fmt.Sprintf(packagePrefixFormat, relPath) + format + prefix = fmt.Sprintf(packagePrefixFormat, relPath) } - fmt.Fprintf(o, format, args...) + + pr.printInternal(prefix+format, args...) } -// Helper functions to set and retrieve printer instance from a context. -// Defining them here avoids the context key collision. +// Context keys and helper functions -// FromContext returns printer instance associated with the context. +type contextKey int + +const printerKey contextKey = 0 + +// FromContextOrDie returns the Printer instance associated with the context. func FromContextOrDie(ctx context.Context) Printer { pr, ok := ctx.Value(printerKey).(Printer) if ok { @@ -169,8 +388,8 @@ func FromContextOrDie(ctx context.Context) Printer { panic("printer missing in context") } -// WithContext creates new context from the given parent context -// by setting the printer instance. +// WithContext creates a new context setting the printer instance. func WithContext(ctx context.Context, pr Printer) context.Context { return context.WithValue(ctx, printerKey, pr) } + diff --git a/pkg/printer/printer_test.go b/pkg/printer/printer_test.go index a3fee4e8f9..cb64233fa8 100644 --- a/pkg/printer/printer_test.go +++ b/pkg/printer/printer_test.go @@ -102,3 +102,64 @@ func TestPrintPackage_WithoutLeadingNewline(t *testing.T) { t.Errorf("Expected %q, got %q", expected, buf.String()) } } + +func TestPrinter_ContextualFieldsAndEvents(t *testing.T) { + t.Run("WithField and WithFields scoping", func(t *testing.T) { + var buf bytes.Buffer + pr := New(&buf, &buf) + + p1 := pr.WithField("package", "wordpress") + p2 := p1.WithFunction("set-labels", "latest").WithFields(ContextualFields{ + "requestID": "req-123", + "user": "admin", + }) + + p2.PrintRunning("set-labels", 2) + got := buf.String() + expected := "[RUNNING] image=\"set-labels\" package=\"wordpress\" requestID=\"req-123\" resourceCount=\"2\" tag=\"latest\" user=\"admin\"\n" + if got != expected { + t.Errorf("Expected %q, got %q", expected, got) + } + + // Verify original printer p1 is unmodified + buf.Reset() + p1.PrintRunning("set-labels", 0) + gotP1 := buf.String() + expectedP1 := "[RUNNING] package=\"wordpress\"\n" + if gotP1 != expectedP1 { + t.Errorf("Expected %q, got %q", expectedP1, gotP1) + } + }) + + t.Run("PrintPass and PrintFail", func(t *testing.T) { + var buf bytes.Buffer + pr := New(&buf, &buf).WithFunction("kubeconform", "latest").WithPackage("wordpress") + + pr.PrintPass("kubeconform", 250*1000*1000) // 250ms + gotPass := buf.String() + expectedPass := "[PASS] image=\"kubeconform\" package=\"wordpress\" tag=\"latest\" time=\"250ms\"\n" + if gotPass != expectedPass { + t.Errorf("Expected %q, got %q", expectedPass, gotPass) + } + + buf.Reset() + pr.PrintFail("kubeconform", 100*1000*1000, nil) + gotFail := buf.String() + expectedFail := "[FAIL] image=\"kubeconform\" package=\"wordpress\" tag=\"latest\" time=\"100ms\"\n" + if gotFail != expectedFail { + t.Errorf("Expected %q, got %q", expectedFail, gotFail) + } + }) + + t.Run("PrintSummary", func(t *testing.T) { + var buf bytes.Buffer + pr := New(&buf, &buf).WithField("user", "porch-controller") + + pr.PrintSummary(4, 2, 1170*1000*1000) + gotSummary := buf.String() + expectedSummary := "Successfully executed 4 function(s) in 2 package(s) time=\"1.17s\" user=\"porch-controller\"\n" + if gotSummary != expectedSummary { + t.Errorf("Expected %q, got %q", expectedSummary, gotSummary) + } + }) +} From c8b13300e3306498c5ad29005ec35538274674ce Mon Sep 17 00:00:00 2001 From: SurbhiAgarwal1 Date: Tue, 8 Sep 2026 21:36:31 +0530 Subject: [PATCH 2/3] refactor: absorb thirdparty packages into pkg and remove thirdparty folder (#4554) Signed-off-by: SurbhiAgarwal1 --- .github/copilot-code-review.yml | 2 -- .golangci.yml | 2 -- AGENTS.md | 1 - commands/fn/fncmd.go | 6 ++-- commands/pkg/pkgcmd.go | 4 +-- .../cmdconfig/commands/cmdcat/cmdcat.go | 2 +- .../cmdconfig/commands/cmdcat/cmdcat_test.go | 16 ++++++++-- .../cmdconfig/commands/cmdeval/cmdeval.go | 4 +-- .../commands/cmdeval/cmdeval_test.go | 2 +- .../cmdconfig/commands/cmdsink/cmdsink.go | 0 .../commands/cmdsink/cmdsink_test.go | 0 .../cmdconfig/commands/cmdsource/cmdsource.go | 2 +- .../commands/cmdsource/cmdsource_test.go | 10 +++++-- .../cmdconfig/commands/cmdtree/cmdtree.go | 2 +- .../commands/cmdtree/cmdtree_test.go | 9 ++++-- .../cmdconfig/commands/cmdtree/tree.go | 0 .../cmdconfig/commands/runner/runner.go | 0 .../cmdconfig/commands/runner/runner_test.go | 0 {thirdparty/kyaml => pkg/fn}/runfn/runfn.go | 0 .../kyaml => pkg/fn}/runfn/runfn_test.go | 0 .../java/java-configmap.resource.yaml | 0 .../java/java-deployment.resource.yaml | 0 .../testdata/java/java-service.resource.yaml | 0 sonar.properties | 4 +-- thirdparty/README.md | 29 ------------------- 25 files changed, 41 insertions(+), 54 deletions(-) rename {thirdparty => pkg}/cmdconfig/commands/cmdcat/cmdcat.go (99%) rename {thirdparty => pkg}/cmdconfig/commands/cmdcat/cmdcat_test.go (97%) rename {thirdparty => pkg}/cmdconfig/commands/cmdeval/cmdeval.go (99%) rename {thirdparty => pkg}/cmdconfig/commands/cmdeval/cmdeval_test.go (99%) rename {thirdparty => pkg}/cmdconfig/commands/cmdsink/cmdsink.go (100%) rename {thirdparty => pkg}/cmdconfig/commands/cmdsink/cmdsink_test.go (100%) rename {thirdparty => pkg}/cmdconfig/commands/cmdsource/cmdsource.go (98%) rename {thirdparty => pkg}/cmdconfig/commands/cmdsource/cmdsource_test.go (98%) rename {thirdparty => pkg}/cmdconfig/commands/cmdtree/cmdtree.go (98%) rename {thirdparty => pkg}/cmdconfig/commands/cmdtree/cmdtree_test.go (99%) rename {thirdparty => pkg}/cmdconfig/commands/cmdtree/tree.go (100%) rename {thirdparty => pkg}/cmdconfig/commands/runner/runner.go (100%) rename {thirdparty => pkg}/cmdconfig/commands/runner/runner_test.go (100%) rename {thirdparty/kyaml => pkg/fn}/runfn/runfn.go (100%) rename {thirdparty/kyaml => pkg/fn}/runfn/runfn_test.go (100%) rename {thirdparty/kyaml => pkg/fn}/runfn/test/testdata/java/java-configmap.resource.yaml (100%) rename {thirdparty/kyaml => pkg/fn}/runfn/test/testdata/java/java-deployment.resource.yaml (100%) rename {thirdparty/kyaml => pkg/fn}/runfn/test/testdata/java/java-service.resource.yaml (100%) delete mode 100644 thirdparty/README.md diff --git a/.github/copilot-code-review.yml b/.github/copilot-code-review.yml index 9a78cece84..8f6db0456f 100644 --- a/.github/copilot-code-review.yml +++ b/.github/copilot-code-review.yml @@ -47,8 +47,6 @@ review: instructions: "Do not review — generated by k8s code-generator" - path: "**/zz_generated*" instructions: "Do not review — generated by controller-tools" - - path: "thirdparty/**" - instructions: "Do not review — vendored upstream code" - path: "go.sum" instructions: "Do not review" - path: "go.mod" diff --git a/.golangci.yml b/.golangci.yml index 6765750cd1..ca2d6d2cb8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -66,7 +66,6 @@ linters: - gosec - funlen paths: - - thirdparty/ - third_party$ - builtin$ - examples$ @@ -77,7 +76,6 @@ formatters: exclusions: generated: lax paths: - - thirdparty/ - third_party$ - builtin$ - examples$ diff --git a/AGENTS.md b/AGENTS.md index 8e07ce4015..5f9feabbd5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -173,7 +173,6 @@ git config --global user.name "Your Name" * **release/**: Release automation (GoReleaser config, Homebrew formula generation) * **hack/**: Miscellaneous development utilities * **healthcheck/**: Separate module for health checking (Go (Go version is defined in [healthcheck/go.mod](./healthcheck/go.mod)), local Makefile) -* **thirdparty/**: Third-party code (excluded from linting) * **Formula/**: Homebrew package definition (generated by `go run ./release/formula/main.go VERSION`) ## Linting Rules & Style diff --git a/commands/fn/fncmd.go b/commands/fn/fncmd.go index 713916851a..3f1cb98d58 100644 --- a/commands/fn/fncmd.go +++ b/commands/fn/fncmd.go @@ -20,9 +20,9 @@ import ( "github.com/kptdev/kpt/commands/fn/doc" "github.com/kptdev/kpt/commands/fn/render" "github.com/kptdev/kpt/internal/docs/generated/fndocs" - "github.com/kptdev/kpt/thirdparty/cmdconfig/commands/cmdeval" - "github.com/kptdev/kpt/thirdparty/cmdconfig/commands/cmdsink" - "github.com/kptdev/kpt/thirdparty/cmdconfig/commands/cmdsource" + "github.com/kptdev/kpt/pkg/cmdconfig/commands/cmdeval" + "github.com/kptdev/kpt/pkg/cmdconfig/commands/cmdsink" + "github.com/kptdev/kpt/pkg/cmdconfig/commands/cmdsource" "github.com/spf13/cobra" ) diff --git a/commands/pkg/pkgcmd.go b/commands/pkg/pkgcmd.go index 6c3951e0f4..e8a623c6b1 100644 --- a/commands/pkg/pkgcmd.go +++ b/commands/pkg/pkgcmd.go @@ -22,8 +22,8 @@ import ( initialization "github.com/kptdev/kpt/commands/pkg/init" "github.com/kptdev/kpt/commands/pkg/update" "github.com/kptdev/kpt/internal/docs/generated/pkgdocs" - "github.com/kptdev/kpt/thirdparty/cmdconfig/commands/cmdcat" - "github.com/kptdev/kpt/thirdparty/cmdconfig/commands/cmdtree" + "github.com/kptdev/kpt/pkg/cmdconfig/commands/cmdcat" + "github.com/kptdev/kpt/pkg/cmdconfig/commands/cmdtree" "github.com/spf13/cobra" ) diff --git a/thirdparty/cmdconfig/commands/cmdcat/cmdcat.go b/pkg/cmdconfig/commands/cmdcat/cmdcat.go similarity index 99% rename from thirdparty/cmdconfig/commands/cmdcat/cmdcat.go rename to pkg/cmdconfig/commands/cmdcat/cmdcat.go index 2934116a73..5254ce6512 100644 --- a/thirdparty/cmdconfig/commands/cmdcat/cmdcat.go +++ b/pkg/cmdconfig/commands/cmdcat/cmdcat.go @@ -27,8 +27,8 @@ import ( kptfilev1 "github.com/kptdev/kpt/api/kptfile/v1" "github.com/kptdev/kpt/internal/docs/generated/pkgdocs" + "github.com/kptdev/kpt/pkg/cmdconfig/commands/runner" argsutil "github.com/kptdev/kpt/pkg/lib/util/args" - "github.com/kptdev/kpt/thirdparty/cmdconfig/commands/runner" "github.com/spf13/cobra" "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio/filters" diff --git a/thirdparty/cmdconfig/commands/cmdcat/cmdcat_test.go b/pkg/cmdconfig/commands/cmdcat/cmdcat_test.go similarity index 97% rename from thirdparty/cmdconfig/commands/cmdcat/cmdcat_test.go rename to pkg/cmdconfig/commands/cmdcat/cmdcat_test.go index cc67f83d0e..e6682c1cd9 100644 --- a/thirdparty/cmdconfig/commands/cmdcat/cmdcat_test.go +++ b/pkg/cmdconfig/commands/cmdcat/cmdcat_test.go @@ -434,7 +434,7 @@ func TestCmd_NonExistent(t *testing.T) { d := t.TempDir() _, err := runCat(t, filepath.Join(d, "nope.yaml")) assert.Error(t, err) - assert.Contains(t, err.Error(), "no such file or directory") + assert.True(t, os.IsNotExist(err) || strings.Contains(err.Error(), "no such file or directory") || strings.Contains(err.Error(), "cannot find the file"), "expected file not found error") } // TestCmd_KptfileArgDisplayed: passing the Kptfile directly should display @@ -826,11 +826,21 @@ metadata: name: secret `) // Symlink inside the package — should be skipped. - require.NoError(t, os.Symlink(filepath.Join(d, "external.yaml"), filepath.Join(real, "link.yaml"))) + if err := os.Symlink(filepath.Join(d, "external.yaml"), filepath.Join(real, "link.yaml")); err != nil { + if strings.Contains(err.Error(), "privilege is not held") { + t.Skip("skipping symlink test on Windows without symlink privileges") + } + require.NoError(t, err) + } // Symlink as the argument — should be resolved. link := filepath.Join(d, "pkg-link") - require.NoError(t, os.Symlink(real, link)) + if err := os.Symlink(real, link); err != nil { + if strings.Contains(err.Error(), "privilege is not held") { + t.Skip("skipping symlink test on Windows without symlink privileges") + } + require.NoError(t, err) + } got, err := runCat(t, link) require.NoError(t, err) diff --git a/thirdparty/cmdconfig/commands/cmdeval/cmdeval.go b/pkg/cmdconfig/commands/cmdeval/cmdeval.go similarity index 99% rename from thirdparty/cmdconfig/commands/cmdeval/cmdeval.go rename to pkg/cmdconfig/commands/cmdeval/cmdeval.go index 8888368186..778fcc7501 100644 --- a/thirdparty/cmdconfig/commands/cmdeval/cmdeval.go +++ b/pkg/cmdconfig/commands/cmdeval/cmdeval.go @@ -15,14 +15,14 @@ import ( "github.com/google/shlex" kptfilev1 "github.com/kptdev/kpt/api/kptfile/v1" docs "github.com/kptdev/kpt/internal/docs/generated/fndocs" + "github.com/kptdev/kpt/pkg/cmdconfig/commands/runner" + "github.com/kptdev/kpt/pkg/fn/runfn" "github.com/kptdev/kpt/pkg/kptfile/kptfileutil" "github.com/kptdev/kpt/pkg/lib/runneroptions" argsutil "github.com/kptdev/kpt/pkg/lib/util/args" "github.com/kptdev/kpt/pkg/lib/util/cmdutil" pathutil "github.com/kptdev/kpt/pkg/lib/util/path" "github.com/kptdev/kpt/pkg/printer" - "github.com/kptdev/kpt/thirdparty/cmdconfig/commands/runner" - "github.com/kptdev/kpt/thirdparty/kyaml/runfn" "github.com/spf13/cobra" "sigs.k8s.io/kustomize/kyaml/comments" "sigs.k8s.io/kustomize/kyaml/errors" diff --git a/thirdparty/cmdconfig/commands/cmdeval/cmdeval_test.go b/pkg/cmdconfig/commands/cmdeval/cmdeval_test.go similarity index 99% rename from thirdparty/cmdconfig/commands/cmdeval/cmdeval_test.go rename to pkg/cmdconfig/commands/cmdeval/cmdeval_test.go index 7e03b56886..75dae8f1f8 100644 --- a/thirdparty/cmdconfig/commands/cmdeval/cmdeval_test.go +++ b/pkg/cmdconfig/commands/cmdeval/cmdeval_test.go @@ -14,9 +14,9 @@ import ( "testing" "github.com/kptdev/kpt/internal/testutil" + "github.com/kptdev/kpt/pkg/fn/runfn" "github.com/kptdev/kpt/pkg/lib/runneroptions" "github.com/kptdev/kpt/pkg/printer/fake" - "github.com/kptdev/kpt/thirdparty/kyaml/runfn" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" "sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil" diff --git a/thirdparty/cmdconfig/commands/cmdsink/cmdsink.go b/pkg/cmdconfig/commands/cmdsink/cmdsink.go similarity index 100% rename from thirdparty/cmdconfig/commands/cmdsink/cmdsink.go rename to pkg/cmdconfig/commands/cmdsink/cmdsink.go diff --git a/thirdparty/cmdconfig/commands/cmdsink/cmdsink_test.go b/pkg/cmdconfig/commands/cmdsink/cmdsink_test.go similarity index 100% rename from thirdparty/cmdconfig/commands/cmdsink/cmdsink_test.go rename to pkg/cmdconfig/commands/cmdsink/cmdsink_test.go diff --git a/thirdparty/cmdconfig/commands/cmdsource/cmdsource.go b/pkg/cmdconfig/commands/cmdsource/cmdsource.go similarity index 98% rename from thirdparty/cmdconfig/commands/cmdsource/cmdsource.go rename to pkg/cmdconfig/commands/cmdsource/cmdsource.go index 354253ae1f..6437a8d272 100644 --- a/thirdparty/cmdconfig/commands/cmdsource/cmdsource.go +++ b/pkg/cmdconfig/commands/cmdsource/cmdsource.go @@ -10,11 +10,11 @@ import ( kptfilev1 "github.com/kptdev/kpt/api/kptfile/v1" "github.com/kptdev/kpt/internal/docs/generated/fndocs" + "github.com/kptdev/kpt/pkg/cmdconfig/commands/runner" "github.com/kptdev/kpt/pkg/lib/pkg" argsutil "github.com/kptdev/kpt/pkg/lib/util/args" "github.com/kptdev/kpt/pkg/lib/util/cmdutil" "github.com/kptdev/kpt/pkg/printer" - "github.com/kptdev/kpt/thirdparty/cmdconfig/commands/runner" "github.com/spf13/cobra" "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio/kioutil" diff --git a/thirdparty/cmdconfig/commands/cmdsource/cmdsource_test.go b/pkg/cmdconfig/commands/cmdsource/cmdsource_test.go similarity index 98% rename from thirdparty/cmdconfig/commands/cmdsource/cmdsource_test.go rename to pkg/cmdconfig/commands/cmdsource/cmdsource_test.go index 8348b9d583..8880ca7b24 100644 --- a/thirdparty/cmdconfig/commands/cmdsource/cmdsource_test.go +++ b/pkg/cmdconfig/commands/cmdsource/cmdsource_test.go @@ -7,6 +7,7 @@ import ( "bytes" "os" "path/filepath" + "strings" "testing" "github.com/kptdev/kpt/internal/testutil" @@ -497,8 +498,13 @@ func TestSourceCommand_Symlink(t *testing.T) { err = os.MkdirAll(filepath.Join(d, "foo"), 0700) assert.NoError(t, err) err = os.Symlink("foo", "foo-link") - if !assert.NoError(t, err) { - return + if err != nil { + if strings.Contains(err.Error(), "privilege is not held") { + t.Skip("skipping symlink test on Windows without symlink privileges") + } + if !assert.NoError(t, err) { + return + } } err = os.WriteFile(filepath.Join(d, "foo", "f1.yaml"), []byte(` kind: Deployment diff --git a/thirdparty/cmdconfig/commands/cmdtree/cmdtree.go b/pkg/cmdconfig/commands/cmdtree/cmdtree.go similarity index 98% rename from thirdparty/cmdconfig/commands/cmdtree/cmdtree.go rename to pkg/cmdconfig/commands/cmdtree/cmdtree.go index e9f6aa183b..18dd17a269 100644 --- a/thirdparty/cmdconfig/commands/cmdtree/cmdtree.go +++ b/pkg/cmdconfig/commands/cmdtree/cmdtree.go @@ -25,9 +25,9 @@ import ( kptfilev1 "github.com/kptdev/kpt/api/kptfile/v1" "github.com/kptdev/kpt/internal/docs/generated/pkgdocs" + "github.com/kptdev/kpt/pkg/cmdconfig/commands/runner" argsutil "github.com/kptdev/kpt/pkg/lib/util/args" "github.com/kptdev/kpt/pkg/printer" - "github.com/kptdev/kpt/thirdparty/cmdconfig/commands/runner" "github.com/spf13/cobra" "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio/filters" diff --git a/thirdparty/cmdconfig/commands/cmdtree/cmdtree_test.go b/pkg/cmdconfig/commands/cmdtree/cmdtree_test.go similarity index 99% rename from thirdparty/cmdconfig/commands/cmdtree/cmdtree_test.go rename to pkg/cmdconfig/commands/cmdtree/cmdtree_test.go index 869ef10e26..df196ab244 100644 --- a/thirdparty/cmdconfig/commands/cmdtree/cmdtree_test.go +++ b/pkg/cmdconfig/commands/cmdtree/cmdtree_test.go @@ -604,8 +604,13 @@ func TestTreeCommand_symlink(t *testing.T) { err = os.MkdirAll(filepath.Join(d, "foo"), 0700) assert.NoError(t, err) err = os.Symlink("foo", "foo-link") - if !assert.NoError(t, err) { - return + if err != nil { + if strings.Contains(err.Error(), "privilege is not held") { + t.Skip("skipping symlink test on Windows without symlink privileges") + } + if !assert.NoError(t, err) { + return + } } defer os.RemoveAll(d) err = os.WriteFile(filepath.Join(d, "foo", "f1.yaml"), []byte(` diff --git a/thirdparty/cmdconfig/commands/cmdtree/tree.go b/pkg/cmdconfig/commands/cmdtree/tree.go similarity index 100% rename from thirdparty/cmdconfig/commands/cmdtree/tree.go rename to pkg/cmdconfig/commands/cmdtree/tree.go diff --git a/thirdparty/cmdconfig/commands/runner/runner.go b/pkg/cmdconfig/commands/runner/runner.go similarity index 100% rename from thirdparty/cmdconfig/commands/runner/runner.go rename to pkg/cmdconfig/commands/runner/runner.go diff --git a/thirdparty/cmdconfig/commands/runner/runner_test.go b/pkg/cmdconfig/commands/runner/runner_test.go similarity index 100% rename from thirdparty/cmdconfig/commands/runner/runner_test.go rename to pkg/cmdconfig/commands/runner/runner_test.go diff --git a/thirdparty/kyaml/runfn/runfn.go b/pkg/fn/runfn/runfn.go similarity index 100% rename from thirdparty/kyaml/runfn/runfn.go rename to pkg/fn/runfn/runfn.go diff --git a/thirdparty/kyaml/runfn/runfn_test.go b/pkg/fn/runfn/runfn_test.go similarity index 100% rename from thirdparty/kyaml/runfn/runfn_test.go rename to pkg/fn/runfn/runfn_test.go diff --git a/thirdparty/kyaml/runfn/test/testdata/java/java-configmap.resource.yaml b/pkg/fn/runfn/test/testdata/java/java-configmap.resource.yaml similarity index 100% rename from thirdparty/kyaml/runfn/test/testdata/java/java-configmap.resource.yaml rename to pkg/fn/runfn/test/testdata/java/java-configmap.resource.yaml diff --git a/thirdparty/kyaml/runfn/test/testdata/java/java-deployment.resource.yaml b/pkg/fn/runfn/test/testdata/java/java-deployment.resource.yaml similarity index 100% rename from thirdparty/kyaml/runfn/test/testdata/java/java-deployment.resource.yaml rename to pkg/fn/runfn/test/testdata/java/java-deployment.resource.yaml diff --git a/thirdparty/kyaml/runfn/test/testdata/java/java-service.resource.yaml b/pkg/fn/runfn/test/testdata/java/java-service.resource.yaml similarity index 100% rename from thirdparty/kyaml/runfn/test/testdata/java/java-service.resource.yaml rename to pkg/fn/runfn/test/testdata/java/java-service.resource.yaml diff --git a/sonar.properties b/sonar.properties index d5fc3660fa..7c3d1ae6c0 100644 --- a/sonar.properties +++ b/sonar.properties @@ -6,11 +6,11 @@ sonar.organization=kptdev sonar.language=go # Path to your Go source code -# Includes all relevant source directories, excluding vendor and thirdparty +# Includes all relevant source directories, excluding vendor sonar.sources=commands,pkg,run,api/fnresult,api/kptfile,api/resourcegroup,api/schema # Exclude files if needed -sonar.exclusions=**/test/**, **/examples/*, **/scripts/*, **/*_test.go, **/testing*, **/generated/**, **/testdata/**, **/*zz_generated.*, vendor/**, thirdparty/**, .github/**, documentation/**, Formula/** +sonar.exclusions=**/test/**, **/examples/*, **/scripts/*, **/*_test.go, **/testing*, **/generated/**, **/testdata/**, **/*zz_generated.*, vendor/**, .github/**, documentation/**, Formula/** # To include test coverage reports sonar.test.inclusions=**/*_test.go diff --git a/thirdparty/README.md b/thirdparty/README.md deleted file mode 100644 index 7d1227a237..0000000000 --- a/thirdparty/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# What is `thirdparty`? - -This directory contains the files that are copied from 3rd-party projects and modified to fit kpt requirements. - -# What is in `thirdparty`? - -- `kyaml`: Files copied from [kyaml] v0.10.15 library - - `runfn`: KRM function runner -- `cmdconfig`: Files copied from [cmd/config] v0.9.9 library - - `commands`: Command files copied from [cmd/config] -- `cli-utils`: Files copied from [cli-utils] - - `status`: Command files copied from [cli-utils/cmd/status] v0.26.0 - - `apply`: apply library copied from [cli-utils/pkg/apply] v0.29.2 + the change in [this PR](https://github.com/kubernetes-sigs/cli-utils/pull/577) - -# Copyright and Licenses - -All files in this directory will keep their original copyright notices at the beginning of the files. - -All files in this directory will be under their original licenses. Licenses notices will be reserved. - -# Contribute to Upstream - -The modifications made in the 3rd-party files may be contributed to upstream. The contribution is determined case by case. - -[kyaml]: https://github.com/kubernetes-sigs/kustomize/tree/8d72528eb5c73df80b20aae0a5e584c056879387/kyaml -[cmd/config]: https://github.com/kubernetes-sigs/kustomize/tree/b9c36caa1c5c6ee64926021841ea441773d0767c/cmd/config -[cli-utils]: https://github.com/kubernetes-sigs/cli-utils -[cli-utils/cmd/status]: https://github.com/kubernetes-sigs/cli-utils/tree/v0.26.0/cmd/status -[cli-utils/pkg/apply]: https://github.com/kubernetes-sigs/cli-utils/tree/v0.29.2/pkg/apply From c48ada7f213939a2301ac413b6932fba6f438b45 Mon Sep 17 00:00:00 2001 From: Surbhi Agarwal Date: Wed, 9 Sep 2026 10:14:34 +0530 Subject: [PATCH 3/3] fix(lint): resolve golangci-lint errors in absorbed packages and printer Signed-off-by: Surbhi Agarwal --- pkg/cmdconfig/commands/cmdcat/cmdcat_test.go | 4 +++- pkg/cmdconfig/commands/cmdeval/cmdeval.go | 3 ++- pkg/cmdconfig/commands/cmdtree/tree.go | 7 ++----- pkg/printer/printer.go | 9 +++++---- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pkg/cmdconfig/commands/cmdcat/cmdcat_test.go b/pkg/cmdconfig/commands/cmdcat/cmdcat_test.go index e6682c1cd9..7dc629088a 100644 --- a/pkg/cmdconfig/commands/cmdcat/cmdcat_test.go +++ b/pkg/cmdconfig/commands/cmdcat/cmdcat_test.go @@ -434,7 +434,9 @@ func TestCmd_NonExistent(t *testing.T) { d := t.TempDir() _, err := runCat(t, filepath.Join(d, "nope.yaml")) assert.Error(t, err) - assert.True(t, os.IsNotExist(err) || strings.Contains(err.Error(), "no such file or directory") || strings.Contains(err.Error(), "cannot find the file"), "expected file not found error") + isNotFound := os.IsNotExist(err) || strings.Contains(err.Error(), "no such file or directory") || + strings.Contains(err.Error(), "cannot find the file") + assert.True(t, isNotFound, "expected file not found error") } // TestCmd_KptfileArgDisplayed: passing the Kptfile directly should display diff --git a/pkg/cmdconfig/commands/cmdeval/cmdeval.go b/pkg/cmdconfig/commands/cmdeval/cmdeval.go index 778fcc7501..7931e7fd23 100644 --- a/pkg/cmdconfig/commands/cmdeval/cmdeval.go +++ b/pkg/cmdconfig/commands/cmdeval/cmdeval.go @@ -89,7 +89,8 @@ func GetEvalFnRunner(ctx context.Context, parent string) *EvalFnRunner { }) r.Command.Flags().BoolVar( - &r.RunnerOptions.AllowWasm, "allow-alpha-wasm", false, "allow alpha wasm functions to be run. If true, you can specify a wasm image with --image flag or a path to a wasm file (must have the .wasm file extension) with --exec flag.") + &r.RunnerOptions.AllowWasm, "allow-alpha-wasm", false, + "allow alpha wasm functions to be run. If true, you can specify a wasm image with --image flag or a path to a wasm file (must have the .wasm file extension) with --exec flag.") // selector flags r.Command.Flags().StringVar( diff --git a/pkg/cmdconfig/commands/cmdtree/tree.go b/pkg/cmdconfig/commands/cmdtree/tree.go index 2327cf35e0..3bc9a98fde 100644 --- a/pkg/cmdconfig/commands/cmdtree/tree.go +++ b/pkg/cmdconfig/commands/cmdtree/tree.go @@ -473,11 +473,8 @@ func (p TreeWriter) getFields(leaf *yaml.RNode) (treeFields, error) { elem := &treeField{name: match} field.matchingElementsAndFields = append(field.matchingElementsAndFields, elem) - // iterate over collection of queried fields for the element - for i := range subFields { - // add to the list of fields for this element - elem.matchingElementsAndFields = append(elem.matchingElementsAndFields, subFields[i]) - } + // add to the list of fields for this element + elem.matchingElementsAndFields = append(elem.matchingElementsAndFields, subFields...) } // clear this cached data field.subFieldByMatch = nil diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go index 1524dee7ed..4487267df5 100644 --- a/pkg/printer/printer.go +++ b/pkg/printer/printer.go @@ -245,7 +245,7 @@ func (pr *printer) formatFields(extraFields ...ContextualFields) string { if i > 0 { sb.WriteString(" ") } - sb.WriteString(fmt.Sprintf("%s=%s", k, strconv.Quote(combined[k]))) + fmt.Fprintf(&sb, "%s=%s", k, strconv.Quote(combined[k])) } return sb.String() } @@ -266,11 +266,12 @@ func (pr *printer) PrintRunning(fnRef string, resourceCount int) { } attrStr := pr.formatFields(extra) - if attrStr != "" { + switch { + case attrStr != "": pr.printInternal("[RUNNING] %s\n", attrStr) - } else if resourceCount > 0 { + case resourceCount > 0: pr.printInternal("[RUNNING] %s on %d resource(s)\n", strconv.Quote(fnRef), resourceCount) - } else { + default: pr.printInternal("[RUNNING] %s\n", strconv.Quote(fnRef)) } }