diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 30c2e089..0df6c0be 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -31,7 +31,13 @@ jobs: run: df -h - uses: golangci/golangci-lint-action@v9 with: - version: v2.11.4 + version: v2.13.2 + - name: Check for outdated Go patterns (go fix modernizers) + run: | + # Fails (exit 1) when any `go fix` modernizer produces a diff, i.e. the + # code contains outdated Go patterns that `go fix ./...` would rewrite. + # Fix locally with: go fix ./... && make fmt + go fix -diff ./... tests: runs-on: ubuntu-latest diff --git a/.golangci.yaml b/.golangci.yaml index dc6d3add..4dd9fea8 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -49,6 +49,7 @@ linters: goconst: min-len: 3 min-occurrences: 8 + ignore-tests: true gocritic: settings: captLocal: diff --git a/AGENTS.md b/AGENTS.md index 70b5892a..0b226560 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ ### Essential Setup ```bash -# Check Go version (requires 1.26.2+) +# Check Go version (requires 1.27.1+) go version # Check Helm dependency (required at runtime) @@ -39,6 +39,10 @@ make fmt # Run golangci-lint golangci-lint run + +# Detect outdated Go patterns (fails if `go fix` would rewrite anything; +# enforced in CI) +make check-modernize ``` ### Testing Commands diff --git a/Dockerfile b/Dockerfile index 3443d23e..d9f6c114 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS builder +FROM --platform=$BUILDPLATFORM golang:1.27-alpine AS builder RUN apk add --no-cache make git WORKDIR /workspace/helmfile diff --git a/Dockerfile.debian-stable-slim b/Dockerfile.debian-stable-slim index a65e3906..95a26eba 100644 --- a/Dockerfile.debian-stable-slim +++ b/Dockerfile.debian-stable-slim @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS builder +FROM --platform=$BUILDPLATFORM golang:1.27-alpine AS builder RUN apk add --no-cache make git WORKDIR /workspace/helmfile diff --git a/Dockerfile.ubuntu b/Dockerfile.ubuntu index 77b3042d..ad37ef13 100644 --- a/Dockerfile.ubuntu +++ b/Dockerfile.ubuntu @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS builder +FROM --platform=$BUILDPLATFORM golang:1.27-alpine AS builder RUN apk add --no-cache make git WORKDIR /workspace/helmfile diff --git a/Makefile b/Makefile index 3e2fc9df..bb1389c3 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,12 @@ check: go vet ${PKGS} .PHONY: check +# check-modernize fails (exit 1) when any `go fix` modernizer would rewrite code, +# i.e. the codebase contains outdated Go patterns. Run `go fix ./...` to fix, then `make fmt`. +check-modernize: + go fix -diff ./... +.PHONY: check-modernize + build-test-tools: go build test/diff-yamls/diff-yamls.go curl --progress-bar --location https://github.com/homeport/dyff/releases/download/v1.5.6/dyff_1.5.6_linux_amd64.tar.gz | tar -xzf - -C `pwd` dyff diff --git a/cmd/root.go b/cmd/root.go index dd417d69..99f85745 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -26,8 +26,7 @@ var globalUsage = "Declaratively deploy your Kubernetes manifests, Kustomize con func toCLIError(g *config.GlobalImpl, err error) error { if err != nil { - var exitErr helmexec.ExitError - if stderrors.As(err, &exitErr) { + if exitErr, ok := stderrors.AsType[helmexec.ExitError](err); ok { return errors.NewExitError(exitErr.Error(), exitErr.ExitStatus()) } switch e := err.(type) { diff --git a/go.mod b/go.mod index 4595ad4e..cd5c1058 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/helmfile/helmfile -go 1.26.8 +go 1.27.1 require ( dario.cat/mergo v1.0.2 @@ -9,6 +9,7 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.33.4 github.com/aws/aws-sdk-go-v2/service/s3 v1.113.1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc + github.com/fatih/color v1.19.0 github.com/go-test/deep v1.1.1 github.com/gofrs/flock v0.13.1 github.com/golang/mock v1.6.0 @@ -20,6 +21,9 @@ require ( github.com/hashicorp/hcl/v2 v2.24.0 github.com/helmfile/chartify v0.28.2 github.com/helmfile/vals v0.46.1 + github.com/mattn/go-runewidth v0.0.30 + github.com/otiai10/copy v1.14.1 + github.com/pkg/errors v0.9.1 github.com/sashabaranov/go-openai v1.42.1 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 @@ -43,6 +47,7 @@ require ( go.yaml.in/yaml/v3 v3.0.5 golang.org/x/sync v0.23.0 golang.org/x/term v0.46.0 + google.golang.org/protobuf v1.36.12 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.22.0 helm.sh/helm/v4 v4.3.0 @@ -58,86 +63,20 @@ replace ( k8s.io/client-go => k8s.io/client-go v0.35.4 ) -require ( - cloud.google.com/go v0.123.0 // indirect - cloud.google.com/go/iam v1.12.0 // indirect - cloud.google.com/go/storage v1.64.0 // indirect - filippo.io/age v1.3.1 // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.7 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/a8m/envsubst v1.4.3 // indirect - github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect - github.com/blang/semver v3.5.1+incompatible // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/fatih/color v1.19.0 - github.com/fujiwara/tfstate-lookup v1.12.1 // indirect - github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/go-querystring v1.2.0 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/gax-go/v2 v2.24.0 // indirect - github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-slug v0.16.8 // indirect - github.com/hashicorp/go-sockaddr v1.0.7 // indirect - github.com/hashicorp/go-tfe v1.103.0 // indirect - github.com/hashicorp/golang-lru v1.0.2 // indirect - github.com/hashicorp/hcl v1.0.1-vault-7 // indirect - github.com/hashicorp/jsonapi v1.4.3-0.20250220162346-81a76b606f3e // indirect - github.com/hashicorp/vault/api v1.23.0 // indirect - github.com/huandu/xstrings v1.5.0 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/itchyny/gojq v0.12.19 // indirect - github.com/klauspost/compress v1.19.1 // indirect - github.com/lib/pq v1.12.3 // indirect - github.com/mattn/go-colorable v0.1.15 // indirect - github.com/mattn/go-runewidth v0.0.30 - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/otiai10/copy v1.14.1 - github.com/pkg/errors v0.9.1 - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/shopspring/decimal v1.4.0 // indirect - github.com/sirupsen/logrus v1.9.4 // indirect - github.com/spf13/cast v1.7.0 // indirect - github.com/ulikunitz/xz v0.5.15 // indirect - go.uber.org/atomic v1.9.0 // indirect - golang.org/x/net v0.58.0 // indirect - golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sys v0.48.0 // indirect - golang.org/x/text v0.41.0 // indirect - golang.org/x/time v0.15.0 // indirect - google.golang.org/api v0.296.0 // indirect - google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d // indirect - google.golang.org/grpc v1.83.2 // indirect - google.golang.org/protobuf v1.36.12 - gopkg.in/ini.v1 v1.67.3 // indirect - sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect -) - require ( al.essio.dev/pkg/shellescape v1.6.0 // indirect cel.dev/expr v0.25.2 // indirect + cloud.google.com/go v0.123.0 // indirect cloud.google.com/go/auth v0.23.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/iam v1.12.0 // indirect cloud.google.com/go/kms v1.33.0 // indirect cloud.google.com/go/longrunning v1.2.0 // indirect cloud.google.com/go/monitoring v1.30.0 // indirect cloud.google.com/go/secretmanager v1.21.0 // indirect + cloud.google.com/go/storage v1.64.0 // indirect + filippo.io/age v1.3.1 // indirect filippo.io/edwards25519 v1.2.0 // indirect filippo.io/hpke v0.4.0 // indirect github.com/1Password/connect-sdk-go v1.5.3 // indirect @@ -152,6 +91,12 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.7 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2 // indirect github.com/BurntSushi/toml v1.6.0 // indirect github.com/DelineaXPM/delinea-common v1.0.0 // indirect @@ -161,8 +106,10 @@ require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/ProtonMail/go-crypto v1.4.1 // indirect + github.com/a8m/envsubst v1.4.3 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/antchfx/jsonquery v1.3.7 // indirect @@ -196,6 +143,8 @@ require ( github.com/aws/smithy-go v1.28.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/blang/semver v3.5.1+incompatible // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect @@ -210,6 +159,7 @@ require ( github.com/cyberark/conjur-api-go v0.15.7 // indirect github.com/cyphar/filepath-securejoin v0.7.0 // indirect github.com/danieljoos/wincred v1.2.2 // indirect + github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dominikbraun/graph v0.23.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect @@ -223,6 +173,7 @@ require ( github.com/felixge/httpsnoop v1.1.0 // indirect github.com/fluxcd/cli-utils v1.2.2 // indirect github.com/fluxcd/flagger v1.36.1 // indirect + github.com/fujiwara/tfstate-lookup v1.12.1 // indirect github.com/fxamacker/cbor/v2 v2.9.1 // indirect github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e // indirect github.com/getsops/sops/v3 v3.13.2 // indirect @@ -259,39 +210,67 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-yaml v1.19.2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/btree v1.1.3 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-jsonnet v0.22.0 // indirect + github.com/google/go-querystring v1.2.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.20 // indirect + github.com/googleapis/gax-go/v2 v2.24.0 // indirect + github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect + github.com/hashicorp/go-slug v0.16.8 // indirect + github.com/hashicorp/go-sockaddr v1.0.7 // indirect + github.com/hashicorp/go-tfe v1.103.0 // indirect github.com/hashicorp/go-version v1.8.0 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl v1.0.1-vault-7 // indirect github.com/hashicorp/hcp-sdk-go v0.175.0 // indirect + github.com/hashicorp/jsonapi v1.4.3-0.20250220162346-81a76b606f3e // indirect + github.com/hashicorp/vault/api v1.23.0 // indirect github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.202 // indirect github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/infisical/go-sdk v0.8.0 // indirect + github.com/itchyny/gojq v0.12.19 // indirect github.com/itchyny/timefmt-go v0.1.8 // indirect github.com/jmoiron/sqlx v1.4.0 // indirect github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/klauspost/compress v1.19.1 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/lib/pq v1.12.3 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-colorable v0.1.15 // indirect github.com/mattn/go-isatty v0.0.22 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect @@ -308,6 +287,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.24.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.70.1 // indirect @@ -316,11 +296,15 @@ require ( github.com/rs/zerolog v1.26.1 // indirect github.com/rubenv/sql-migrate v1.8.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect github.com/samber/lo v1.39.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.37 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/sony/gobreaker v0.5.0 // indirect + github.com/spf13/cast v1.7.0 // indirect github.com/spiffe/go-spiffe/v2 v2.8.1 // indirect github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect github.com/tetratelabs/wazero v1.12.0 // indirect @@ -330,6 +314,7 @@ require ( github.com/tjfoc/gmsm v1.4.1 // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect + github.com/ulikunitz/xz v0.5.15 // indirect github.com/urfave/cli v1.22.17 // indirect github.com/werf/logboek v0.6.1 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -361,16 +346,26 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0 // indirect go.opentelemetry.io/otel/log v0.22.0 // indirect go.opentelemetry.io/otel/sdk/log v0.22.0 // indirect + go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.55.0 // indirect golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect golang.org/x/mod v0.38.0 // indirect + golang.org/x/net v0.58.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sys v0.48.0 // indirect + golang.org/x/text v0.41.0 // indirect + golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.48.0 // indirect + google.golang.org/api v0.296.0 // indirect + google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect + google.golang.org/grpc v1.83.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/gookit/color.v1 v1.1.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.3 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/api v0.37.0 // indirect k8s.io/apiextensions-apiserver v0.37.0 // indirect @@ -383,8 +378,10 @@ require ( k8s.io/utils v0.0.0-20260626114624-be93311217bd // indirect oras.land/oras-go/v2 v2.6.2 // indirect sigs.k8s.io/controller-runtime v0.24.1 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/kustomize/api v0.21.1 // indirect sigs.k8s.io/kustomize/kyaml v0.21.1 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.4.2 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/pkg/agent/doctor/report_test.go b/pkg/agent/doctor/report_test.go index 2ea5d46d..cc406653 100644 --- a/pkg/agent/doctor/report_test.go +++ b/pkg/agent/doctor/report_test.go @@ -224,7 +224,7 @@ func TestReportJSON_FieldOrderIsHumanReadable(t *testing.T) { // Cheap way: scan line-by-line, the first 7 non-space chars of each line // are `"key":`. keys := []string{} - for _, line := range strings.Split(out, "\n") { + for line := range strings.SplitSeq(out, "\n") { line = strings.TrimSpace(line) if !strings.HasPrefix(line, `"`) { continue diff --git a/pkg/agent/llm/openai.go b/pkg/agent/llm/openai.go index 8c70c53e..292d03cc 100644 --- a/pkg/agent/llm/openai.go +++ b/pkg/agent/llm/openai.go @@ -50,7 +50,10 @@ func (o *openaiClient) Analyze(ctx goContext.Context, diff string, extras Analyz req := openai.ChatCompletionRequest{ Model: o.cfg.Model, Temperature: o.cfg.Temperature, - MaxTokens: o.cfg.MaxTokens, + // SA1019: keep the deprecated MaxTokens field deliberately: `max_tokens` is the + // only form universally accepted by OpenAI-compatible backends (One-API, + // LiteLLM, Ollama shim); MaxCompletionTokens is not supported by all of them. + MaxTokens: o.cfg.MaxTokens, //nolint:staticcheck // SA1019 ResponseFormat: &openai.ChatCompletionResponseFormat{ Type: openai.ChatCompletionResponseFormatTypeJSONObject, }, @@ -166,8 +169,8 @@ func stripJSONCodeFence(s string) string { // Case 2: fenced JSON. if strings.HasPrefix(s, "```") { rest := s[3:] - if nl := strings.IndexByte(rest, '\n'); nl >= 0 { - s = rest[nl+1:] + if _, after, ok := strings.Cut(rest, "\n"); ok { + s = after } else { s = rest } diff --git a/pkg/agent/llm/types_test.go b/pkg/agent/llm/types_test.go index 741fb217..5ac46626 100644 --- a/pkg/agent/llm/types_test.go +++ b/pkg/agent/llm/types_test.go @@ -141,7 +141,7 @@ func TestMockClient_Analyze_PropagatesErr(t *testing.T) { func TestMockClient_ConcurrentSafe(t *testing.T) { c := NewMockClient(Analysis{Summary: "ok"}) var wg sync.WaitGroup - for i := 0; i < 20; i++ { + for range 20 { wg.Add(2) go func() { defer wg.Done() diff --git a/pkg/app/app.go b/pkg/app/app.go index 5e715f6d..07ced72c 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path/filepath" + "slices" "sort" "strings" "sync" @@ -1744,9 +1745,7 @@ func (a *App) findDesiredStateFiles(specifiedPath string, opts LoadOpts) ([]stri return files[j] < files[i] }) } else { - sort.Slice(files, func(i, j int) bool { - return files[i] < files[j] - }) + slices.Sort(files) } a.Logger.Debugf("found %d helmfile state files in %s: %s", len(ymlFiles)+len(gotmplFiles), helmfileDir, strings.Join(files, ", ")) diff --git a/pkg/app/app_parallel_test.go b/pkg/app/app_parallel_test.go index 8cc6b9dc..90934d5f 100644 --- a/pkg/app/app_parallel_test.go +++ b/pkg/app/app_parallel_test.go @@ -38,7 +38,7 @@ releases: // Run ListReleases multiple times to verify consistent ordering var outputs []string - for i := 0; i < 5; i++ { + for i := range 5 { var buffer bytes.Buffer syncWriter := testhelper.NewSyncWriter(&buffer) logger := helmexec.NewLogger(syncWriter, "debug") diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index 78f0cda8..6a43e301 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -5069,9 +5069,7 @@ func TestGetArgs(t *testing.T) { for _, test := range tests { Helmdefaults := state.HelmSpec{KubeContext: "test", Args: test.defaultArgs} testState := &state.HelmState{ - ReleaseSetSpec: state.ReleaseSetSpec{ - HelmDefaults: Helmdefaults, - }, + HelmDefaults: Helmdefaults, } receivedArgs := GetArgs(test.args, testState) diff --git a/pkg/app/context_test.go b/pkg/app/context_test.go index d30bb2d5..000ba975 100644 --- a/pkg/app/context_test.go +++ b/pkg/app/context_test.go @@ -19,11 +19,11 @@ func TestContextConcurrentAccess(t *testing.T) { wg.Add(numGoroutines) // Launch multiple goroutines that concurrently update the repos map - for i := 0; i < numGoroutines; i++ { + for i := range numGoroutines { go func(goroutineID int) { defer wg.Done() - for j := 0; j < numReposPerGoroutine; j++ { + for j := range numReposPerGoroutine { repoKey := "repo-" + string(rune('0'+goroutineID)) + "-" + string(rune('0'+j)) ctx.mu.Lock() @@ -129,10 +129,10 @@ func TestContextConcurrentReadWrite(t *testing.T) { var wg sync.WaitGroup // Launch multiple goroutines for each repo - for i := 0; i < numRepos; i++ { + for i := range numRepos { repoKey := "repo-" + string(rune('0'+i)) + "=https://example.com" - for j := 0; j < numGoroutinesPerRepo; j++ { + for range numGoroutinesPerRepo { wg.Add(1) go func(key string) { defer wg.Done() diff --git a/pkg/app/doctor.go b/pkg/app/doctor.go index 19dd9c1f..606ae7f9 100644 --- a/pkg/app/doctor.go +++ b/pkg/app/doctor.go @@ -257,8 +257,7 @@ func isDetectedChanges(err error) bool { if err == nil { return false } - var appErr *Error - if errors.As(err, &appErr) { + if appErr, ok := errors.AsType[*Error](err); ok { if appErr.code != nil && *appErr.code == 2 { return true } diff --git a/pkg/app/init_test.go b/pkg/app/init_test.go index 2b3c88ed..bf632a3f 100644 --- a/pkg/app/init_test.go +++ b/pkg/app/init_test.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "regexp" + "slices" "strings" "testing" @@ -143,10 +144,8 @@ func createPluginYAML(t *testing.T, pluginsDir, pluginDirName, name, version str func newHelmPluginMockRunner(pluginErr error) *initMockRunner { return &initMockRunner{ executeFunc: func(cmd string, args []string, env map[string]string, enableLiveOutput bool) ([]byte, error) { - for _, a := range args { - if a == "--short" { - return []byte("v3.18.6"), nil - } + if slices.Contains(args, "--short") { + return []byte("v3.18.6"), nil } // Fail any "helm plugin ..." subcommand (install, update, etc.) if len(args) > 0 && args[0] == "plugin" { @@ -168,10 +167,8 @@ func TestCheckHelmPlugins_InstallErrorButPluginPresent(t *testing.T) { // plugin.yaml on disk and then returns an error. runner := &initMockRunner{ executeFunc: func(cmd string, args []string, env map[string]string, enableLiveOutput bool) ([]byte, error) { - for _, a := range args { - if a == "--short" { - return []byte("v3.18.6"), nil - } + if slices.Contains(args, "--short") { + return []byte("v3.18.6"), nil } if len(args) > 0 && args[0] == "plugin" && len(args) >= 3 && args[1] == "install" { // Find which plugin is being installed by matching the repo URL. @@ -228,10 +225,8 @@ func TestCheckHelmPlugins_UpdateUsesUninstallReinstall(t *testing.T) { // that writes the required version to disk. runner := &initMockRunner{ executeFunc: func(cmd string, args []string, env map[string]string, enableLiveOutput bool) ([]byte, error) { - for _, a := range args { - if a == "--short" { - return []byte("v3.18.6"), nil - } + if slices.Contains(args, "--short") { + return []byte("v3.18.6"), nil } if len(args) >= 2 && args[0] == "plugin" { switch args[1] { @@ -309,10 +304,8 @@ func TestCheckHelmPlugins_UpdateErrorButPluginAtRequiredVersion(t *testing.T) { // verifies the version and warns instead of returning an error. runner := &initMockRunner{ executeFunc: func(cmd string, args []string, env map[string]string, enableLiveOutput bool) ([]byte, error) { - for _, a := range args { - if a == "--short" { - return []byte("v3.18.6"), nil - } + if slices.Contains(args, "--short") { + return []byte("v3.18.6"), nil } if len(args) >= 2 && args[0] == "plugin" { switch args[1] { diff --git a/pkg/app/run.go b/pkg/app/run.go index 40eea495..3fe77398 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -181,8 +181,7 @@ func (r *Run) WithPreparedCharts(helmfileCommand string, opts state.ChartPrepare // merge the preparation and cleanup errors into a single error output var merged []error if prepareErr != nil { - var me *MultiError - if errors.As(prepareErr, &me) { + if me, ok := errors.AsType[*MultiError](prepareErr); ok { merged = append(merged, me.Errors...) } else { merged = append(merged, prepareErr) diff --git a/pkg/app/snapshot_test.go b/pkg/app/snapshot_test.go index 29c52ab8..b6b3e0a0 100644 --- a/pkg/app/snapshot_test.go +++ b/pkg/app/snapshot_test.go @@ -26,15 +26,13 @@ func runWithLogCapture(t *testing.T, logLevel string, f func(*testing.T, *zap.Su logFlushed := &sync.WaitGroup{} // Ensure all the log is consumed into `bs` by calling `logWriter.Close()` followed by `logFlushed.Wait()` - logFlushed.Add(1) - go func() { + logFlushed.Go(func() { scanner := bufio.NewScanner(logReader) for scanner.Scan() { bs.Write(scanner.Bytes()) bs.WriteString("\n") } - logFlushed.Done() - }() + }) defer func() { // This is here to avoid data-trace on bytes buffer `bs` to capture logs @@ -76,7 +74,7 @@ func assertEqualsToSnapshot(t *testing.T, name string, data string) { "Snapshot file %q does not exist. Rerun this test with `HELMFILE_UPDATE_SNAPSHOT=1 go test -v -run %s %s` to create the snapshot", snapshotFileName, t.Name(), - reflect.TypeOf(thisPkgLocator{}).PkgPath(), + reflect.TypeFor[thisPkgLocator]().PkgPath(), ) } @@ -87,7 +85,7 @@ func assertEqualsToSnapshot(t *testing.T, name string, data string) { t.Errorf( "If you think this is due to the snapshot file being outdated, rerun this test with `HELMFILE_UPDATE_SNAPSHOT=1 go test -v -run %s %s` to update the snapshot", t.Name(), - reflect.TypeOf(thisPkgLocator{}).PkgPath(), + reflect.TypeFor[thisPkgLocator]().PkgPath(), ) } } diff --git a/pkg/event/bus.go b/pkg/event/bus.go index 5e022e63..2182c7f2 100644 --- a/pkg/event/bus.go +++ b/pkg/event/bus.go @@ -3,6 +3,7 @@ package event import ( goContext "context" "fmt" + "maps" "os" "strconv" "strings" @@ -149,9 +150,7 @@ func (bus *Bus) runHook(hook Hook, evt string, evtErr error, context map[string] Error: evtErr, }, } - for k, v := range context { - data[k] = v - } + maps.Copy(data, context) render := tmpl.NewTextRenderer(bus.Fs, bus.BasePath, data) bus.Logger.Debugf("hook[%s]: triggered by event \"%s\"\n", name, evt) diff --git a/pkg/event/bus_test.go b/pkg/event/bus_test.go index 9cf92798..65284e0f 100644 --- a/pkg/event/bus_test.go +++ b/pkg/event/bus_test.go @@ -347,8 +347,7 @@ func TestTriggerCleanupEventWithNilError(t *testing.T) { } func TestBusDefaultRunnerUsesCtxWhenSet(t *testing.T) { - ctx, cancel := goContext.WithCancel(goContext.Background()) - defer cancel() + ctx := t.Context() bus := &Bus{Ctx: ctx, Logger: zap.NewNop().Sugar()} diff --git a/pkg/hcllang/hcl_loader.go b/pkg/hcllang/hcl_loader.go index 3cd712bb..0f688681 100644 --- a/pkg/hcllang/hcl_loader.go +++ b/pkg/hcllang/hcl_loader.go @@ -3,6 +3,7 @@ package hcllang import ( nativejson "encoding/json" "fmt" + "maps" "slices" "strings" @@ -49,9 +50,7 @@ func ctyMergeValues(a, b cty.Value) cty.Value { (a.Type().IsMapType() && b.Type().IsMapType()) { mergedAttrs := make(map[string]cty.Value) // Start with all attrs from a. - for name, av := range a.AsValueMap() { - mergedAttrs[name] = av - } + maps.Copy(mergedAttrs, a.AsValueMap()) // Overlay attrs from b. for name, bv := range b.AsValueMap() { if av, ok := mergedAttrs[name]; ok { diff --git a/pkg/helmexec/exec.go b/pkg/helmexec/exec.go index e747a43e..1593515b 100644 --- a/pkg/helmexec/exec.go +++ b/pkg/helmexec/exec.go @@ -306,14 +306,8 @@ func (helm *execer) retryRepoOp(name string, op func() ([]byte, error)) ([]byte, } // Cap the shift exponent at 5 (2^5 = 32x already exceeds the 30x cap) // so very large --repo-retries values can't overflow time.Duration. - shift := attempt - if shift > 5 { - shift = 5 - } - backoff := repoRetryBaseBackoff * time.Duration(1< 30*repoRetryBaseBackoff { - backoff = 30 * repoRetryBaseBackoff - } + shift := min(attempt, 5) + backoff := min(repoRetryBaseBackoff*time.Duration(1< "--dry-run") flagName := flag - if idx := strings.Index(flag, "="); idx != -1 { - flagName = flag[:idx] + if before, _, ok := strings.Cut(flag, "="); ok { + flagName = before } // Check if this flag or any prefix of it is supported @@ -841,8 +830,8 @@ func (helm *execer) TemplateRelease(name string, chart string, flags ...string) i++ continue } - if strings.HasPrefix(flags[i], "--output-dir=") { - outputDir = strings.TrimPrefix(flags[i], "--output-dir=") + if after, ok := strings.CutPrefix(flags[i], "--output-dir="); ok { + outputDir = after continue } filteredFlags = append(filteredFlags, flags[i]) diff --git a/pkg/helmexec/exec_flag_filtering_test.go b/pkg/helmexec/exec_flag_filtering_test.go index 28ac10da..e18e7c26 100644 --- a/pkg/helmexec/exec_flag_filtering_test.go +++ b/pkg/helmexec/exec_flag_filtering_test.go @@ -56,8 +56,7 @@ func TestFilterDependencyFlags_AllGlobalFlags(t *testing.T) { } var expectedFlags []string - for i := 0; i < envType.NumField(); i++ { - field := envType.Field(i) + for field := range envType.Fields() { if field.IsExported() { flagName := "--" + toKebabCase(field.Name) expectedFlags = append(expectedFlags, flagName) @@ -106,8 +105,7 @@ func TestFilterDependencyFlags_AllDependencyFlags(t *testing.T) { } var expectedFlags []string - for i := 0; i < depType.NumField(); i++ { - field := depType.Field(i) + for field := range depType.Fields() { if field.IsExported() { flagName := "--" + toKebabCase(field.Name) expectedFlags = append(expectedFlags, flagName) diff --git a/pkg/helmexec/exit_error.go b/pkg/helmexec/exit_error.go index 21aa37b1..b6a18c2b 100644 --- a/pkg/helmexec/exit_error.go +++ b/pkg/helmexec/exit_error.go @@ -6,11 +6,11 @@ import ( ) func newExitError(path string, args []string, exitStatus int, err error, stderr, combined string, stripArgsValuesOnExitError bool) ExitError { - var out string + var out strings.Builder - out += fmt.Sprintf("PATH:\n%s", Indent(path, " ")) + fmt.Fprintf(&out, "PATH:\n%s", Indent(path, " ")) - out += "\n\nARGS:" + out.WriteString("\n\nARGS:") // The legacy profile is byte-identical to the historical inline logic; // the goldens in exit_error_test.go pin its exact output. redacted := RedactArgs(args, RedactionLegacy) @@ -18,23 +18,23 @@ func newExitError(path string, args []string, exitStatus int, err error, stderr, redacted = args } for i, a := range redacted { - out += fmt.Sprintf("\n%s", Indent(fmt.Sprintf("%d: %s (%d bytes)", i, a, len(a)), " ")) + fmt.Fprintf(&out, "\n%s", Indent(fmt.Sprintf("%d: %s (%d bytes)", i, a, len(a)), " ")) } - out += fmt.Sprintf("\n\nERROR:\n%s", Indent(err.Error(), " ")) + fmt.Fprintf(&out, "\n\nERROR:\n%s", Indent(err.Error(), " ")) - out += fmt.Sprintf("\n\nEXIT STATUS\n%s", Indent(fmt.Sprintf("%d", exitStatus), " ")) + fmt.Fprintf(&out, "\n\nEXIT STATUS\n%s", Indent(fmt.Sprintf("%d", exitStatus), " ")) if len(stderr) > 0 { - out += fmt.Sprintf("\n\nSTDERR:\n%s", Indent(stderr, " ")) + fmt.Fprintf(&out, "\n\nSTDERR:\n%s", Indent(stderr, " ")) } if len(combined) > 0 { - out += fmt.Sprintf("\n\nCOMBINED OUTPUT:\n%s", Indent(combined, " ")) + fmt.Fprintf(&out, "\n\nCOMBINED OUTPUT:\n%s", Indent(combined, " ")) } return ExitError{ - Message: fmt.Sprintf("command %q exited with non-zero status:\n\n%s", path, out), + Message: fmt.Sprintf("command %q exited with non-zero status:\n\n%s", path, out.String()), Code: exitStatus, } } diff --git a/pkg/helmexec/log.go b/pkg/helmexec/log.go index cf6dd5a9..286e486e 100644 --- a/pkg/helmexec/log.go +++ b/pkg/helmexec/log.go @@ -23,7 +23,7 @@ type logWriter struct { } func (w *logWriter) Write(p []byte) (int, error) { - for _, line := range strings.Split(string(p), "\n") { + for line := range strings.SplitSeq(string(p), "\n") { w.log.Debugf("%s%s", w.prefix, strings.TrimSpace(line)) } return len(p), nil diff --git a/pkg/helmexec/redact.go b/pkg/helmexec/redact.go index 6f456b8f..e74ef769 100644 --- a/pkg/helmexec/redact.go +++ b/pkg/helmexec/redact.go @@ -1,6 +1,9 @@ package helmexec -import "strings" +import ( + "slices" + "strings" +) // RedactionProfile selects how aggressively RedactArgs masks secret-bearing // command-line arguments. @@ -74,12 +77,7 @@ func RedactArgs(args []string, profile RedactionProfile) []string { } func isStrictNextArgFlag(arg string) bool { - for _, flag := range strictNextArgFlags { - if arg == flag { - return true - } - } - return false + return slices.Contains(strictNextArgFlags, arg) } // strictInlineFlag returns the flag name when arg is a single-argument secret diff --git a/pkg/helmexec/runner.go b/pkg/helmexec/runner.go index 6fdb59e4..1bb8133c 100644 --- a/pkg/helmexec/runner.go +++ b/pkg/helmexec/runner.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io" + "maps" "os" "os/exec" "path/filepath" @@ -211,9 +212,7 @@ func LiveOutput(ctx context.Context, c *exec.Cmd, stripArgsValuesOnExitError boo func mergeEnv(orig []string, new map[string]string) []string { wanted := env2map(orig) - for k, v := range new { - wanted[k] = v - } + maps.Copy(wanted, new) return map2env(wanted) } diff --git a/pkg/helmexec/span.go b/pkg/helmexec/span.go index 9414b8d1..6cda8ae9 100644 --- a/pkg/helmexec/span.go +++ b/pkg/helmexec/span.go @@ -158,8 +158,7 @@ func finishExecSpan(span trace.Span, isHelm bool, args []string, start time.Time if err == nil { return } - var exitErr ExitError - if errors.As(err, &exitErr) { + if exitErr, ok := errors.AsType[ExitError](err); ok { span.SetAttributes(attribute.Int("exec.exit_code", exitErr.ExitStatus())) } // The raw error may embed command arguments and subprocess output; keep diff --git a/pkg/helmexec/span_test.go b/pkg/helmexec/span_test.go index 28a08bef..4b9999e4 100644 --- a/pkg/helmexec/span_test.go +++ b/pkg/helmexec/span_test.go @@ -105,8 +105,7 @@ func spanAttrStrings(t *testing.T, span *v1.Span, key string) []string { // subprocess spans nest under per-release spans WITHOUT overriding the // runner's own cancellation context (the kubedog safety valve). func TestSpanAttachedContext(t *testing.T) { - runnerCtx, valveCancel := context.WithCancel(context.Background()) - defer valveCancel() + runnerCtx := t.Context() spanCtx, span := noop.NewTracerProvider().Tracer("test").Start(context.Background(), "release") diff --git a/pkg/kubedog/printer.go b/pkg/kubedog/printer.go index 593f1bc1..e800beb4 100644 --- a/pkg/kubedog/printer.go +++ b/pkg/kubedog/printer.go @@ -3,6 +3,7 @@ package kubedog import ( "context" "fmt" + "maps" "sort" "strings" "sync" @@ -60,7 +61,7 @@ func HeaderDividerStyled(title string, useColor bool) string { // trailing padding on left-aligned last columns. func TableVisualWidth(tableStr string) int { max := 0 - for _, line := range strings.Split(tableStr, "\n") { + for line := range strings.SplitSeq(tableStr, "\n") { line = strings.TrimRight(line, " ") w := runewidth.StringWidth(line) if w > max { @@ -136,9 +137,7 @@ func (g *gateStatuses) snapshot() map[string]string { g.mu.RLock() defer g.mu.RUnlock() out := make(map[string]string, len(g.m)) - for k, v := range g.m { - out[k] = v - } + maps.Copy(out, g.m) return out } @@ -327,8 +326,8 @@ func (p *progressPrinter) statusColor(status, parentKind string) string { } head := status - if idx := strings.Index(status, " "); idx >= 0 { - head = status[:idx] + if before, _, ok := strings.Cut(status, " "); ok { + head = before } switch head { case "ready": diff --git a/pkg/kubedog/watchdog.go b/pkg/kubedog/watchdog.go index 1d7e0602..82878e30 100644 --- a/pkg/kubedog/watchdog.go +++ b/pkg/kubedog/watchdog.go @@ -3,6 +3,7 @@ package kubedog import ( "context" "fmt" + "slices" "time" "github.com/werf/kubedog/pkg/trackers/dyntracker/statestore" @@ -167,10 +168,8 @@ func (t *Tracker) scanForMissedFailures(ctx context.Context, taskStore *kdutil.C // "RS UIDs owned by this Deployment" so we only LIST RSes when we actually // need to disambiguate. func podOwnedBy(pod *unstructured.Unstructured, workloadUID types.UID, workloadKind string, deploymentRSUIDs func() map[string]struct{}) bool { - for _, uid := range podOwnerUIDs(pod) { - if uid == workloadUID { - return true - } + if slices.Contains(podOwnerUIDs(pod), workloadUID) { + return true } if workloadKind != "deploy" { return false @@ -206,11 +205,8 @@ func (t *Tracker) rsUIDsOwnedBy(ctx context.Context, namespace string, workloadU out := map[string]struct{}{} for i := range rsList.Items { rs := &rsList.Items[i] - for _, uid := range podOwnerUIDs(rs) { - if uid == workloadUID { - out[string(rs.GetUID())] = struct{}{} - break - } + if slices.Contains(podOwnerUIDs(rs), workloadUID) { + out[string(rs.GetUID())] = struct{}{} } } return out diff --git a/pkg/maputil/maputil.go b/pkg/maputil/maputil.go index 47192035..86ada320 100644 --- a/pkg/maputil/maputil.go +++ b/pkg/maputil/maputil.go @@ -2,6 +2,7 @@ package maputil import ( "fmt" + "maps" "strconv" "strings" ) @@ -263,16 +264,14 @@ func typedVal(val string, st bool) any { } // MergeMaps merges two maps with special handling for nested maps and arrays. -func MergeMaps(a, b map[string]interface{}, opts ...MergeOptions) map[string]interface{} { +func MergeMaps(a, b map[string]any, opts ...MergeOptions) map[string]any { arrayStrategy := ArrayMergeStrategySparse if len(opts) > 0 { arrayStrategy = opts[0].ArrayStrategy } - out := make(map[string]interface{}, len(a)) - for k, v := range a { - out[k] = v - } + out := make(map[string]any, len(a)) + maps.Copy(out, a) for k, v := range b { if v == nil { // If key doesn't exist in base, add nil (issue #1154). @@ -282,9 +281,9 @@ func MergeMaps(a, b map[string]interface{}, opts ...MergeOptions) map[string]int } continue } - if v, ok := v.(map[string]interface{}); ok { + if v, ok := v.(map[string]any); ok { if bv, ok := out[k]; ok { - if bv, ok := bv.(map[string]interface{}); ok { + if bv, ok := bv.(map[string]any); ok { out[k] = MergeMaps(bv, v, opts...) continue } @@ -365,15 +364,12 @@ func mergeSlices(base, override []any, strategy ArrayMergeStrategy) []any { } // Merge element-by-element (for ArrayMergeStrategyMerge or sparse arrays) - maxLen := len(base) - if len(override) > maxLen { - maxLen = len(override) - } + maxLen := max(len(override), len(base)) - result := make([]interface{}, maxLen) + result := make([]any, maxLen) copy(result, base) - for i := 0; i < len(override); i++ { + for i := range override { overrideVal := override[i] if overrideVal == nil { continue diff --git a/pkg/maputil/maputil_test.go b/pkg/maputil/maputil_test.go index 2558988a..8e135b41 100644 --- a/pkg/maputil/maputil_test.go +++ b/pkg/maputil/maputil_test.go @@ -195,27 +195,27 @@ func typedValueTest(t *testing.T, input string, expectedWhenNoStr any) { } func TestMapUtil_MergeMaps(t *testing.T) { - map1 := map[string]interface{}{ + map1 := map[string]any{ "debug": true, } - map2 := map[string]interface{}{ + map2 := map[string]any{ "logLevel": "info", "replicaCount": 3, } - map3 := map[string]interface{}{ + map3 := map[string]any{ "logLevel": "info", "replicaCount": map[string]any{ "app1": 3, "awesome": 4, }, } - map4 := map[string]interface{}{ + map4 := map[string]any{ "logLevel": "info", "replicaCount": map[string]any{ "app1": 3, }, } - map5 := map[string]interface{}{ + map5 := map[string]any{ "logLevel": "error", "replicaCount": nil, } @@ -239,7 +239,7 @@ func TestMapUtil_MergeMaps(t *testing.T) { } testMap = MergeMaps(map1, map3) - expectedMap := map[string]interface{}{ + expectedMap := map[string]any{ "debug": true, "logLevel": "info", "replicaCount": map[string]any{ @@ -253,7 +253,7 @@ func TestMapUtil_MergeMaps(t *testing.T) { } testMap = MergeMaps(map3, map5) - expectedMap = map[string]interface{}{ + expectedMap = map[string]any{ "logLevel": "error", "replicaCount": map[string]any{ "app1": 3, @@ -478,7 +478,7 @@ func TestMapUtil_Issue2281_EmptyMapScenario(t *testing.T) { func TestMapUtil_Issue2281_MergeArrays(t *testing.T) { t.Run("merging sparse arrays should preserve elements from base that aren't in override", func(t *testing.T) { // Base values from helmfile - base := map[string]interface{}{ + base := map[string]any{ "top": map[string]any{ "array": []any{"thing1", "thing2"}, }, @@ -486,7 +486,7 @@ func TestMapUtil_Issue2281_MergeArrays(t *testing.T) { // Override values from --state-values-set top.array[1]=cmdlinething1 // This creates a sparse array with nil at index 0 - override := map[string]interface{}{ + override := map[string]any{ "top": map[string]any{ "array": []any{nil, "cmdlinething1"}, }, @@ -506,7 +506,7 @@ func TestMapUtil_Issue2281_MergeArrays(t *testing.T) { t.Run("complete arrays without nils should replace entirely (layer behavior)", func(t *testing.T) { // Base values from helmfile - base := map[string]interface{}{ + base := map[string]any{ "top": map[string]any{ "array": []any{"thing1", "thing2", "thing3"}, }, @@ -514,7 +514,7 @@ func TestMapUtil_Issue2281_MergeArrays(t *testing.T) { // Override values from environment YAML (complete array, no nils) // This should REPLACE the base array entirely - override := map[string]interface{}{ + override := map[string]any{ "top": map[string]any{ "array": []any{"override1"}, }, @@ -533,7 +533,7 @@ func TestMapUtil_Issue2281_MergeArrays(t *testing.T) { t.Run("merging complex arrays should preserve non-overridden elements and fields", func(t *testing.T) { // Base values from helmfile - base := map[string]interface{}{ + base := map[string]any{ "top": map[string]any{ "complexArray": []any{ map[string]any{ @@ -549,7 +549,7 @@ func TestMapUtil_Issue2281_MergeArrays(t *testing.T) { } // Override values from --state-values-set top.complexArray[1].anotherThing=cmdline - override := map[string]interface{}{ + override := map[string]any{ "top": map[string]any{ "complexArray": []any{ nil, @@ -588,7 +588,7 @@ func TestMapUtil_Issue2281_MergeArrays(t *testing.T) { t.Run("complete issue #2281 scenario with MergeMaps - sparse arrays", func(t *testing.T) { // Base values from helmfile - base := map[string]interface{}{ + base := map[string]any{ "top": map[string]any{ "array": []any{"thing1", "thing2"}, "complexArray": []any{ @@ -607,7 +607,7 @@ func TestMapUtil_Issue2281_MergeArrays(t *testing.T) { // Override values from: // --state-values-set top.array[1]=cmdlinething1 (creates sparse array with nil at 0) // --state-values-set top.complexArray[1].anotherThing=cmdline - override := map[string]interface{}{ + override := map[string]any{ "top": map[string]any{ "array": []any{nil, "cmdlinething1"}, // Sparse array - nil at index 0 "complexArray": []any{ @@ -651,10 +651,10 @@ func TestMapUtil_Issue2281_MergeArrays(t *testing.T) { func TestMergeMaps_ArrayStrategies(t *testing.T) { t.Run("ArrayMergeStrategyReplace should replace arrays entirely - fixes #2353", func(t *testing.T) { // This simulates layer value overriding where outer layer array should replace inner - base := map[string]interface{}{ + base := map[string]any{ "array": []any{"inner1", "inner2", "inner3"}, } - override := map[string]interface{}{ + override := map[string]any{ "array": []any{"outer1", "outer2"}, } @@ -671,10 +671,10 @@ func TestMergeMaps_ArrayStrategies(t *testing.T) { t.Run("ArrayMergeStrategySparse (default) should merge element-by-element - preserves #2281 fix", func(t *testing.T) { // This simulates --state-values-set which creates sparse arrays - base := map[string]interface{}{ + base := map[string]any{ "array": []any{"base1", "base2", "base3"}, } - override := map[string]interface{}{ + override := map[string]any{ "array": []any{nil, "override2"}, // Has nil = sparse array from CLI } @@ -691,10 +691,10 @@ func TestMergeMaps_ArrayStrategies(t *testing.T) { t.Run("Auto-detect: complete array (no nils) replaces base entirely", func(t *testing.T) { // Array without nils is detected as "complete" (layer value) and replaces entirely - base := map[string]interface{}{ + base := map[string]any{ "array": []any{"base1", "base2", "base3"}, } - override := map[string]interface{}{ + override := map[string]any{ "array": []any{"override1"}, // Single element, no nils } @@ -711,10 +711,10 @@ func TestMergeMaps_ArrayStrategies(t *testing.T) { t.Run("Auto-detect: sparse array (with nils) preserves base at nil indices", func(t *testing.T) { // Array with nils is detected as "sparse" (CLI value) and merges element-by-element - base := map[string]interface{}{ + base := map[string]any{ "array": []any{"base1", "base2", "base3"}, } - override := map[string]interface{}{ + override := map[string]any{ "array": []any{nil, nil, "override3"}, // Has nils at indices 0, 1 } @@ -730,13 +730,13 @@ func TestMergeMaps_ArrayStrategies(t *testing.T) { }) t.Run("nested maps in sparse arrays should merge recursively", func(t *testing.T) { - base := map[string]interface{}{ + base := map[string]any{ "complexArray": []any{ map[string]any{"field1": "a", "field2": "b"}, map[string]any{"field1": "c", "field2": "d"}, }, } - override := map[string]interface{}{ + override := map[string]any{ "complexArray": []any{ nil, // Skip index 0 map[string]any{"field2": "override"}, // Only override field2 at index 1 @@ -761,14 +761,14 @@ func TestMergeMaps_ArrayStrategies(t *testing.T) { t.Run("Replace strategy: array of maps replaced entirely - layer scenario #2353", func(t *testing.T) { // This is the key scenario from #2353: outer layer defining complete array of objects - base := map[string]interface{}{ + base := map[string]any{ "releases": []any{ map[string]any{"name": "inner-release-1", "chart": "inner-chart-1"}, map[string]any{"name": "inner-release-2", "chart": "inner-chart-2"}, map[string]any{"name": "inner-release-3", "chart": "inner-chart-3"}, }, } - override := map[string]interface{}{ + override := map[string]any{ "releases": []any{ map[string]any{"name": "outer-release-1", "chart": "outer-chart-1"}, map[string]any{"name": "outer-release-2", "chart": "outer-chart-2"}, @@ -796,10 +796,10 @@ func TestMergeMaps_ArrayStrategies(t *testing.T) { }) t.Run("Replace strategy: empty override array replaces base", func(t *testing.T) { - base := map[string]interface{}{ + base := map[string]any{ "array": []any{"a", "b", "c"}, } - override := map[string]interface{}{ + override := map[string]any{ "array": []any{}, // Empty array } @@ -814,10 +814,10 @@ func TestMergeMaps_ArrayStrategies(t *testing.T) { }) t.Run("Auto-detect: empty override replaces base (no nils means complete)", func(t *testing.T) { - base := map[string]interface{}{ + base := map[string]any{ "array": []any{"a", "b", "c"}, } - override := map[string]interface{}{ + override := map[string]any{ "array": []any{}, // Empty array - has no nils, detected as complete } @@ -833,14 +833,14 @@ func TestMergeMaps_ArrayStrategies(t *testing.T) { }) t.Run("strategies propagate to nested maps", func(t *testing.T) { - base := map[string]interface{}{ + base := map[string]any{ "outer": map[string]any{ "inner": []any{"a", "b", "c"}, }, } // With Replace strategy - explicit replacement - overrideComplete := map[string]interface{}{ + overrideComplete := map[string]any{ "outer": map[string]any{ "inner": []any{"x", "y"}, // Complete array (no nils) }, @@ -856,7 +856,7 @@ func TestMergeMaps_ArrayStrategies(t *testing.T) { } // With auto-detection and sparse array (has nils) - overrideSparse := map[string]interface{}{ + overrideSparse := map[string]any{ "outer": map[string]any{ "inner": []any{"x", "y", nil}, // Sparse array - has nil at index 2 }, @@ -875,10 +875,10 @@ func TestMergeMaps_ArrayStrategies(t *testing.T) { t.Run("ArrayMergeStrategyMerge always merges element-by-element (CLI index 0 case)", func(t *testing.T) { // This tests the CLI scenario: --state-values-set array[0]=value // Creates array ["value"] with NO nils, but should still merge - base := map[string]interface{}{ + base := map[string]any{ "array": []any{"base0", "base1", "base2"}, } - override := map[string]interface{}{ + override := map[string]any{ "array": []any{"override0"}, // Single element, no nils - from CLI index 0 } diff --git a/pkg/plugins/vals.go b/pkg/plugins/vals.go index d73169b8..79e2d1f3 100644 --- a/pkg/plugins/vals.go +++ b/pkg/plugins/vals.go @@ -6,6 +6,7 @@ import ( "io" "os" "regexp" + "slices" "strconv" "strings" "sync" @@ -104,16 +105,12 @@ func containsRefPlus(v any) bool { } } case []any: - for _, v := range val { - if containsRefPlus(v) { - return true - } + if slices.ContainsFunc(val, containsRefPlus) { + return true } case []string: - for _, s := range val { - if refPlusRegexp.MatchString(s) { - return true - } + if slices.ContainsFunc(val, refPlusRegexp.MatchString) { + return true } } return false diff --git a/pkg/policy/checker.go b/pkg/policy/checker.go index a609b8fa..4d8d43ac 100644 --- a/pkg/policy/checker.go +++ b/pkg/policy/checker.go @@ -32,7 +32,7 @@ func forbidEnvironmentsWithReleases(filePath string, content []byte) (bool, erro return true, fmt.Errorf("no top-level config keys are found in %s", filePath) } result := []string{} - resultKeys := map[string]interface{}{} + resultKeys := map[string]any{} for _, k := range topKeys { if slices.Contains([]string{"environments", "releases", "---"}, k) { if _, ok := resultKeys[k]; !ok { @@ -79,9 +79,9 @@ func isTopOrderKey(key string) bool { // TopKeys returns the top-level config keys. func TopKeys(helmfileContent []byte, hasSeparator bool) []string { var topKeys []string - clines := bytes.Split(helmfileContent, []byte("\n")) + clines := bytes.SplitSeq(helmfileContent, []byte("\n")) - for _, line := range clines { + for line := range clines { lineStr := strings.TrimRightFunc(string(line), unicode.IsSpace) if lineStr == "" { continue // Skip empty lines @@ -91,7 +91,7 @@ func TopKeys(helmfileContent []byte, hasSeparator bool) []string { } if topConfigKeysRegex.MatchString(lineStr) { - topKey := strings.SplitN(lineStr, ":", 2)[0] + topKey, _, _ := strings.Cut(lineStr, ":") topKeys = append(topKeys, topKey) } } diff --git a/pkg/state/chart_dependencies_rewrite_test.go b/pkg/state/chart_dependencies_rewrite_test.go index be1c51dc..1e6abbf5 100644 --- a/pkg/state/chart_dependencies_rewrite_test.go +++ b/pkg/state/chart_dependencies_rewrite_test.go @@ -549,7 +549,7 @@ dependencies: errCh := make(chan error, numGoroutines) ready := make(chan struct{}) - for i := 0; i < numGoroutines; i++ { + for range numGoroutines { wg.Add(1) readyWg.Add(1) go func() { diff --git a/pkg/state/chart_dependency.go b/pkg/state/chart_dependency.go index 242da4de..825f0660 100644 --- a/pkg/state/chart_dependency.go +++ b/pkg/state/chart_dependency.go @@ -260,8 +260,8 @@ func chartDependenciesAlias(namespace, releaseName string) string { // the chart path when helm processes OCI references during dependency update. // See issue #954. func ociDependencyChartName(chart string) string { - if idx := strings.LastIndex(chart, "/"); idx >= 0 { - return chart[idx+1:] + if _, name, found := strings.CutLast(chart, "/"); found { + return name } return chart } @@ -273,10 +273,11 @@ func ociDependencyChartName(chart string) string { // chart="path_with_underscores/example", baseURL="oci://registry.example.com" // → "oci://registry.example.com/path_with_underscores" func ociDependencyRepoURL(chart, ociBaseURL string) string { - if idx := strings.LastIndex(chart, "/"); idx >= 0 { - return strings.TrimSuffix(ociBaseURL, "/") + "/" + chart[:idx] + pathPrefix, _, found := strings.CutLast(chart, "/") + if !found { + return ociBaseURL } - return ociBaseURL + return strings.TrimSuffix(ociBaseURL, "/") + "/" + pathPrefix } func getUnresolvedDependenciess(st *HelmState) (string, *UnresolvedDependencies) { diff --git a/pkg/state/chart_dependency_test.go b/pkg/state/chart_dependency_test.go index ed558d6d..db63a784 100644 --- a/pkg/state/chart_dependency_test.go +++ b/pkg/state/chart_dependency_test.go @@ -80,27 +80,25 @@ func TestGetUnresolvedDependenciess(t *testing.T) { name: "oci chart with path prefix and underscores (issue #954)", helmState: &HelmState{ FilePath: "helmfile.yaml", - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{ - { - Name: "example", - Chart: "myrepo/path_with_underscores/example", - Version: "1.0.0", - Namespace: "myns", - }, - { - Name: "another", - Chart: "myrepo/another_path/chart", - Version: "2.0.0", - Namespace: "myns", - }, + Releases: []ReleaseSpec{ + { + Name: "example", + Chart: "myrepo/path_with_underscores/example", + Version: "1.0.0", + Namespace: "myns", }, - Repositories: []RepositorySpec{ - { - Name: "myrepo", - URL: "harbor.custom.com", - OCI: true, - }, + { + Name: "another", + Chart: "myrepo/another_path/chart", + Version: "2.0.0", + Namespace: "myns", + }, + }, + Repositories: []RepositorySpec{ + { + Name: "myrepo", + URL: "harbor.custom.com", + OCI: true, }, }, }, @@ -130,34 +128,32 @@ func TestGetUnresolvedDependenciess(t *testing.T) { name: "oci chart without path prefix (unchanged behavior)", helmState: &HelmState{ FilePath: "helmfile.yaml", - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{ - { - Name: "foo", - Chart: "chartsa/abc", - Version: "0.1.0", - Namespace: "ns1", - }, - { - Name: "empty", - Chart: "chartsb/empty", - Namespace: "ns2", - }, - { - Name: "empty", - Chart: "chartsb/empty", - }, + Releases: []ReleaseSpec{ + { + Name: "foo", + Chart: "chartsa/abc", + Version: "0.1.0", + Namespace: "ns1", }, - Repositories: []RepositorySpec{ - { - Name: "chartsa", - URL: "localhost:5000/aaa", - OCI: true, - }, - { - Name: "chartsb", - URL: "localhost:5000/bbb", - }, + { + Name: "empty", + Chart: "chartsb/empty", + Namespace: "ns2", + }, + { + Name: "empty", + Chart: "chartsb/empty", + }, + }, + Repositories: []RepositorySpec{ + { + Name: "chartsa", + URL: "localhost:5000/aaa", + OCI: true, + }, + { + Name: "chartsb", + URL: "localhost:5000/bbb", }, }, }, @@ -191,31 +187,29 @@ func TestGetUnresolvedDependenciess(t *testing.T) { name: "duplicate charts are differentiated by alias", helmState: &HelmState{ FilePath: "helmfile.yaml", - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{ - { - Name: "foo", - Chart: "myrepo/abc", - Version: "> 0.2.0", - Namespace: "ns1", - }, - { - Name: "bar", - Chart: "myrepo/abc", - Version: "0.1.0", - Namespace: "ns2", - }, - { - Name: "baz", - Chart: "myrepo/abc", - Version: "0.3.0", - }, + Releases: []ReleaseSpec{ + { + Name: "foo", + Chart: "myrepo/abc", + Version: "> 0.2.0", + Namespace: "ns1", }, - Repositories: []RepositorySpec{ - { - Name: "myrepo", - URL: "localhost:5000/aaa", - }, + { + Name: "bar", + Chart: "myrepo/abc", + Version: "0.1.0", + Namespace: "ns2", + }, + { + Name: "baz", + Chart: "myrepo/abc", + Version: "0.3.0", + }, + }, + Repositories: []RepositorySpec{ + { + Name: "myrepo", + URL: "localhost:5000/aaa", }, }, }, diff --git a/pkg/state/create.go b/pkg/state/create.go index 7384596a..9e02a5ae 100644 --- a/pkg/state/create.go +++ b/pkg/state/create.go @@ -538,7 +538,7 @@ func (c *StateCreator) scatterGatherEnvSecretFiles(st *HelmState, envSecretFiles func() { sortedSecrets := make([]secretResult, inputsSize) - for i := 0; i < inputsSize; i++ { + for range inputsSize { result := <-results sortedSecrets[result.id] = result } diff --git a/pkg/state/helmx_test.go b/pkg/state/helmx_test.go index 6e35b0cb..d4eb5382 100644 --- a/pkg/state/helmx_test.go +++ b/pkg/state/helmx_test.go @@ -648,10 +648,6 @@ func TestGetReleaseHardTimeout(t *testing.T) { } } -func strPtr(s string) *string { - return &s -} - func TestAppendServerSideFlagsForUpgrade(t *testing.T) { type args struct { flags []string @@ -691,7 +687,7 @@ func TestAppendServerSideFlagsForUpgrade(t *testing.T) { args: args{ flags: []string{}, helm: testutil.NewHelmExec(true), - release: &ReleaseSpec{ServerSide: strPtr("false")}, + release: &ReleaseSpec{ServerSide: new("false")}, opt: &SyncOpts{}, expected: []string{"--server-side", "false"}, }, @@ -701,7 +697,7 @@ func TestAppendServerSideFlagsForUpgrade(t *testing.T) { args: args{ flags: []string{}, helm: testutil.NewHelmExec(true), - helmSpec: HelmSpec{ServerSide: strPtr("auto")}, + helmSpec: HelmSpec{ServerSide: new("auto")}, release: &ReleaseSpec{}, opt: &SyncOpts{}, expected: []string{"--server-side", "auto"}, @@ -712,7 +708,7 @@ func TestAppendServerSideFlagsForUpgrade(t *testing.T) { args: args{ flags: []string{}, helm: testutil.NewHelmExec(true), - release: &ReleaseSpec{ServerSide: strPtr("true")}, + release: &ReleaseSpec{ServerSide: new("true")}, opt: &SyncOpts{ServerSide: "false"}, expected: []string{"--server-side", "true"}, }, @@ -722,7 +718,7 @@ func TestAppendServerSideFlagsForUpgrade(t *testing.T) { args: args{ flags: []string{}, helm: testutil.NewHelmExec(true), - helmSpec: HelmSpec{ServerSide: strPtr("false")}, + helmSpec: HelmSpec{ServerSide: new("false")}, release: &ReleaseSpec{}, opt: &SyncOpts{ServerSide: "true"}, expected: []string{"--server-side", "true"}, @@ -743,7 +739,7 @@ func TestAppendServerSideFlagsForUpgrade(t *testing.T) { args: args{ flags: []string{}, helm: testutil.NewVersionHelmExec("3.17.0"), - release: &ReleaseSpec{ServerSide: strPtr("true")}, + release: &ReleaseSpec{ServerSide: new("true")}, opt: &SyncOpts{}, expected: []string{}, wantErr: true, @@ -754,7 +750,7 @@ func TestAppendServerSideFlagsForUpgrade(t *testing.T) { args: args{ flags: []string{}, helm: testutil.NewVersionHelmExec("3.17.0"), - helmSpec: HelmSpec{ServerSide: strPtr("true")}, + helmSpec: HelmSpec{ServerSide: new("true")}, release: &ReleaseSpec{}, opt: &SyncOpts{}, expected: []string{}, @@ -777,7 +773,7 @@ func TestAppendServerSideFlagsForUpgrade(t *testing.T) { args: args{ flags: []string{}, helm: testutil.NewHelmExec(true), - release: &ReleaseSpec{ServerSide: strPtr("")}, + release: &ReleaseSpec{ServerSide: new("")}, opt: &SyncOpts{ServerSide: "auto"}, expected: []string{"--server-side", "auto"}, }, diff --git a/pkg/state/inherited.go b/pkg/state/inherited.go index c77a4f87..02e3cf93 100644 --- a/pkg/state/inherited.go +++ b/pkg/state/inherited.go @@ -2,6 +2,7 @@ package state import ( "fmt" + "slices" "strings" "dario.cat/mergo" @@ -36,12 +37,7 @@ func AllowedInherits() []string { // IsValidInherit reports whether key is an allowed inherits: entry. func IsValidInherit(key string) bool { - for _, k := range allowedInherits { - if k == key { - return true - } - } - return false + return slices.Contains(allowedInherits, key) } // InheritedConfig carries parent-helmfile config to a sub-helmfile. Only the diff --git a/pkg/state/inherited_test.go b/pkg/state/inherited_test.go index 0818802f..aaa709bf 100644 --- a/pkg/state/inherited_test.go +++ b/pkg/state/inherited_test.go @@ -13,15 +13,14 @@ import ( ) func TestBuildInheritedConfig_OnlyRequestedFields(t *testing.T) { - st := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ + st := &HelmState{ Repositories: []RepositorySpec{{Name: "a"}, {Name: "b"}}, HelmDefaults: HelmSpec{Timeout: 300, Atomic: true}, CommonLabels: map[string]string{"team": "platform"}, ApiVersions: []string{"v1"}, KubeVersion: "1.30.0", Templates: map[string]TemplateSpec{"t": {}}, - Env: environment.Environment{Name: "prod", Values: map[string]any{"k": "v"}}, - }} + Env: environment.Environment{Name: "prod", Values: map[string]any{"k": "v"}}} t.Run("repositories only", func(t *testing.T) { in, err := st.BuildInheritedConfig([]string{"repositories"}) @@ -65,13 +64,12 @@ func TestBuildInheritedConfig_OnlyRequestedFields(t *testing.T) { // does not alias the parent's slices/maps — mutating the copy must not affect // the parent state. This guards against the cross-state coupling noted in review. func TestBuildInheritedConfig_PureFieldsAreDeepCopied(t *testing.T) { - st := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ + st := &HelmState{ Repositories: []RepositorySpec{{Name: "a"}, {Name: "b"}}, HelmDefaults: HelmSpec{Timeout: 300, Args: []string{"--parent-arg"}}, CommonLabels: map[string]string{"team": "platform"}, ApiVersions: []string{"v1"}, - Templates: map[string]TemplateSpec{"base": {ReleaseSpec: ReleaseSpec{Namespace: "parent-ns"}}}, - }} + Templates: map[string]TemplateSpec{"base": {ReleaseSpec: ReleaseSpec{Namespace: "parent-ns"}}}} in, err := st.BuildInheritedConfig([]string{ "repositories", "helmDefaults", "commonLabels", "apiVersions", "templates", }) @@ -96,21 +94,19 @@ func TestBuildInheritedConfig_PureFieldsAreDeepCopied(t *testing.T) { } func TestMergeInherited_NilIsNoop(t *testing.T) { - st := &HelmState{ReleaseSetSpec: ReleaseSetSpec{Repositories: []RepositorySpec{{Name: "a"}}}} + st := &HelmState{Repositories: []RepositorySpec{{Name: "a"}}} require.NoError(t, st.MergeInherited(nil)) assert.Equal(t, []RepositorySpec{{Name: "a"}}, st.Repositories) } func TestMergeInherited_RepositoriesAppendsAndDedupsChildWins(t *testing.T) { - parent := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ - Repositories: []RepositorySpec{{Name: "shared", URL: "parent-url"}, {Name: "only-parent"}}, - }} + parent := &HelmState{ + Repositories: []RepositorySpec{{Name: "shared", URL: "parent-url"}, {Name: "only-parent"}}} in, err := parent.BuildInheritedConfig([]string{"repositories"}) require.NoError(t, err) - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ - Repositories: []RepositorySpec{{Name: "shared", URL: "child-url"}, {Name: "only-child"}}, - }} + child := &HelmState{ + Repositories: []RepositorySpec{{Name: "shared", URL: "child-url"}, {Name: "only-child"}}} require.NoError(t, child.MergeInherited(in)) names := repoNames(child.Repositories) @@ -124,9 +120,8 @@ func TestMergeInherited_RepositoriesAppendsAndDedupsChildWins(t *testing.T) { } func TestMergeInherited_HelmDefaultsParentFillsChildGaps(t *testing.T) { - parent := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: HelmSpec{Timeout: 300, Atomic: true}, - }} + parent := &HelmState{ + HelmDefaults: HelmSpec{Timeout: 300, Atomic: true}} in, err := parent.BuildInheritedConfig([]string{"helmDefaults"}) require.NoError(t, err) @@ -138,7 +133,7 @@ func TestMergeInherited_HelmDefaultsParentFillsChildGaps(t *testing.T) { }) t.Run("child sets a non-zero field, parent fills the rest", func(t *testing.T) { - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{HelmDefaults: HelmSpec{Wait: true}}} + child := &HelmState{HelmDefaults: HelmSpec{Wait: true}} require.NoError(t, child.MergeInherited(in)) assert.Equal(t, 300, child.HelmDefaults.Timeout, "parent fills child gap") assert.True(t, child.HelmDefaults.Atomic, "parent fills child gap") @@ -147,15 +142,13 @@ func TestMergeInherited_HelmDefaultsParentFillsChildGaps(t *testing.T) { } func TestMergeInherited_CommonLabelsUnionChildWins(t *testing.T) { - parent := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ - CommonLabels: map[string]string{"team": "platform", "shared": "parent"}, - }} + parent := &HelmState{ + CommonLabels: map[string]string{"team": "platform", "shared": "parent"}} in, err := parent.BuildInheritedConfig([]string{"commonLabels"}) require.NoError(t, err) - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ - CommonLabels: map[string]string{"shared": "child", "local": "c"}, - }} + child := &HelmState{ + CommonLabels: map[string]string{"shared": "child", "local": "c"}} require.NoError(t, child.MergeInherited(in)) assert.Equal(t, "platform", child.CommonLabels["team"], "parent-only key added") @@ -164,15 +157,13 @@ func TestMergeInherited_CommonLabelsUnionChildWins(t *testing.T) { } func TestMergeInherited_TemplatesUnionChildWins(t *testing.T) { - parent := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ - Templates: map[string]TemplateSpec{"base": {ReleaseSpec: ReleaseSpec{Namespace: "a"}}, "shared": {ReleaseSpec: ReleaseSpec{Namespace: "p"}}}, - }} + parent := &HelmState{ + Templates: map[string]TemplateSpec{"base": {ReleaseSpec: ReleaseSpec{Namespace: "a"}}, "shared": {ReleaseSpec: ReleaseSpec{Namespace: "p"}}}} in, err := parent.BuildInheritedConfig([]string{"templates"}) require.NoError(t, err) - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ - Templates: map[string]TemplateSpec{"shared": {ReleaseSpec: ReleaseSpec{Namespace: "c"}}, "local": {ReleaseSpec: ReleaseSpec{Namespace: "x"}}}, - }} + child := &HelmState{ + Templates: map[string]TemplateSpec{"shared": {ReleaseSpec: ReleaseSpec{Namespace: "c"}}, "local": {ReleaseSpec: ReleaseSpec{Namespace: "x"}}}} require.NoError(t, child.MergeInherited(in)) assert.Contains(t, child.Templates, "base", "parent-only template added") @@ -181,11 +172,11 @@ func TestMergeInherited_TemplatesUnionChildWins(t *testing.T) { } func TestMergeInherited_ApiVersionsAppendsAndDedups(t *testing.T) { - parent := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ApiVersions: []string{"v1", "v2"}}} + parent := &HelmState{ApiVersions: []string{"v1", "v2"}} in, err := parent.BuildInheritedConfig([]string{"apiVersions"}) require.NoError(t, err) - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ApiVersions: []string{"v2", "v3"}}} + child := &HelmState{ApiVersions: []string{"v2", "v3"}} require.NoError(t, child.MergeInherited(in)) assert.Equal(t, []string{"v1", "v2", "v3"}, child.ApiVersions) @@ -193,7 +184,7 @@ func TestMergeInherited_ApiVersionsAppendsAndDedups(t *testing.T) { func TestMergeInherited_KubeVersionChildWinsParentFillsGap(t *testing.T) { t.Run("child empty inherits parent", func(t *testing.T) { - parent := &HelmState{ReleaseSetSpec: ReleaseSetSpec{KubeVersion: "1.30.0"}} + parent := &HelmState{KubeVersion: "1.30.0"} in, err := parent.BuildInheritedConfig([]string{"kubeVersion"}) require.NoError(t, err) child := &HelmState{} @@ -201,10 +192,10 @@ func TestMergeInherited_KubeVersionChildWinsParentFillsGap(t *testing.T) { assert.Equal(t, "1.30.0", child.KubeVersion) }) t.Run("child set keeps its own", func(t *testing.T) { - parent := &HelmState{ReleaseSetSpec: ReleaseSetSpec{KubeVersion: "1.30.0"}} + parent := &HelmState{KubeVersion: "1.30.0"} in, err := parent.BuildInheritedConfig([]string{"kubeVersion"}) require.NoError(t, err) - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{KubeVersion: "1.29.0"}} + child := &HelmState{KubeVersion: "1.29.0"} require.NoError(t, child.MergeInherited(in)) assert.Equal(t, "1.29.0", child.KubeVersion) }) @@ -216,9 +207,8 @@ func newObservedLogger() (*zap.SugaredLogger, *observer.ObservedLogs) { } func TestWarnUninheritedRepos_WarnsWhenParentHasRepoChildLacks(t *testing.T) { - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{{Name: "myapp", Chart: "release-charts/myapp"}}, - }} + child := &HelmState{ + Releases: []ReleaseSpec{{Name: "myapp", Chart: "release-charts/myapp"}}} logger, recorded := newObservedLogger() child.WarnUninheritedRepos([]string{"release-charts"}, logger) @@ -230,10 +220,9 @@ func TestWarnUninheritedRepos_WarnsWhenParentHasRepoChildLacks(t *testing.T) { func TestWarnUninheritedRepos_NoWarnWhenRepoInherited(t *testing.T) { // child has the repo (e.g. because it was inherited and merged) -> no warn - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ + child := &HelmState{ Repositories: []RepositorySpec{{Name: "release-charts"}}, - Releases: []ReleaseSpec{{Name: "myapp", Chart: "release-charts/myapp"}}, - }} + Releases: []ReleaseSpec{{Name: "myapp", Chart: "release-charts/myapp"}}} logger, recorded := newObservedLogger() child.WarnUninheritedRepos([]string{"release-charts"}, logger) @@ -243,9 +232,8 @@ func TestWarnUninheritedRepos_NoWarnWhenRepoInherited(t *testing.T) { func TestWarnUninheritedRepos_NoWarnForRepoNotInParent(t *testing.T) { // repo absent from both -> helm will error separately, no inherit hint - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{{Name: "myapp", Chart: "other/myapp"}}, - }} + child := &HelmState{ + Releases: []ReleaseSpec{{Name: "myapp", Chart: "other/myapp"}}} logger, recorded := newObservedLogger() child.WarnUninheritedRepos([]string{"release-charts"}, logger) @@ -254,15 +242,14 @@ func TestWarnUninheritedRepos_NoWarnForRepoNotInParent(t *testing.T) { } func TestWarnUninheritedRepos_IgnoresLocalAndBareCharts(t *testing.T) { - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ + child := &HelmState{ Releases: []ReleaseSpec{ {Name: "a", Chart: "./local/chart"}, {Name: "b", Chart: "mychart"}, {Name: "c", Chart: "oci://registry/chart"}, {Name: "d", Chart: "https://host/charts/x"}, {Name: "e", Chart: "../sibling/y"}, - }, - }} + }} logger, recorded := newObservedLogger() child.WarnUninheritedRepos([]string{"release-charts"}, logger) @@ -271,12 +258,11 @@ func TestWarnUninheritedRepos_IgnoresLocalAndBareCharts(t *testing.T) { } func TestWarnUninheritedRepos_WarnsOncePerRepo(t *testing.T) { - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ + child := &HelmState{ Releases: []ReleaseSpec{ {Name: "a", Chart: "shared/x"}, {Name: "b", Chart: "shared/y"}, - }, - }} + }} logger, recorded := newObservedLogger() child.WarnUninheritedRepos([]string{"shared"}, logger) @@ -285,9 +271,8 @@ func TestWarnUninheritedRepos_WarnsOncePerRepo(t *testing.T) { } func TestWarnUninheritedRepos_NilLoggerAndEmptyInputsAreSafe(t *testing.T) { - child := &HelmState{ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{{Name: "a", Chart: "x/y"}}, - }} + child := &HelmState{ + Releases: []ReleaseSpec{{Name: "a", Chart: "x/y"}}} assert.NotPanics(t, func() { child.WarnUninheritedRepos(nil, nil) }) assert.NotPanics(t, func() { child.WarnUninheritedRepos(nil, zap.NewNop().Sugar()) }) assert.NotPanics(t, func() { child.WarnUninheritedRepos([]string{"x"}, zap.NewNop().Sugar()) }) diff --git a/pkg/state/issue_1757_test.go b/pkg/state/issue_1757_test.go index 3789e44c..0a7b3c09 100644 --- a/pkg/state/issue_1757_test.go +++ b/pkg/state/issue_1757_test.go @@ -111,12 +111,10 @@ fieldSpecs: `), 0644)) st := &HelmState{ - logger: zap.NewNop().Sugar(), - fs: filesystem.DefaultFileSystem(), - ReleaseSetSpec: ReleaseSetSpec{ - DefaultHelmBinary: helmBin, - DefaultKustomizeBinary: "kustomize", - }, + logger: zap.NewNop().Sugar(), + fs: filesystem.DefaultFileSystem(), + DefaultHelmBinary: helmBin, + DefaultKustomizeBinary: "kustomize", } chartification := &Chartify{ diff --git a/pkg/state/issue_1799_test.go b/pkg/state/issue_1799_test.go index d1cfd097..95fa1414 100644 --- a/pkg/state/issue_1799_test.go +++ b/pkg/state/issue_1799_test.go @@ -130,7 +130,7 @@ func TestChartifyTempDirConcurrentTracking(t *testing.T) { } var wg sync.WaitGroup - for i := 0; i < n; i++ { + for i := range n { wg.Add(1) go func(dir string) { defer wg.Done() diff --git a/pkg/state/issue_2444_test.go b/pkg/state/issue_2444_test.go index 4c73e3a9..5edf6961 100644 --- a/pkg/state/issue_2444_test.go +++ b/pkg/state/issue_2444_test.go @@ -215,9 +215,7 @@ func TestEffectiveTemplateArgs(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: HelmSpec{TemplateArgs: tt.helmDefaults}, - }, + HelmDefaults: HelmSpec{TemplateArgs: tt.helmDefaults}, } assert.Equal(t, tt.want, st.effectiveTemplateArgs(tt.cliArgs)) }) @@ -231,10 +229,8 @@ func TestEffectiveTemplateArgs(t *testing.T) { // command opt into server-side templating + kube-connection injection end-to-end. func TestHelmDefaultsTemplateArgsReachesChartify(t *testing.T) { st := &HelmState{ - kubeconfig: "/path/to/kubeconfig", - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: HelmSpec{TemplateArgs: []string{"--dry-run=server"}}, - }, + kubeconfig: "/path/to/kubeconfig", + HelmDefaults: HelmSpec{TemplateArgs: []string{"--dry-run=server"}}, } // Mirror processChartification's exact call shape. diff --git a/pkg/state/issue_2549_test.go b/pkg/state/issue_2549_test.go index 3dd6e343..0b6cad0c 100644 --- a/pkg/state/issue_2549_test.go +++ b/pkg/state/issue_2549_test.go @@ -74,9 +74,7 @@ func TestAppendSkipSchemaValidationFlagToChartifyTemplateArgs(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: tt.defaults, - }, + HelmDefaults: tt.defaults, } got := st.appendSkipSchemaValidationFlagToChartifyTemplateArgs(tt.templateArgs, tt.release, tt.fromCLI) diff --git a/pkg/state/issue_2766_test.go b/pkg/state/issue_2766_test.go index e569c307..2f2f424c 100644 --- a/pkg/state/issue_2766_test.go +++ b/pkg/state/issue_2766_test.go @@ -105,10 +105,8 @@ func TestGetOCIChart_ResolvesConstraintIntoCachePathAndPullFlag(t *testing.T) { logger := zap.NewExample().Sugar() st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: []RepositorySpec{ - {Name: issue2766RepoName, URL: issue2766RepoURL, OCI: true}, - }, + Repositories: []RepositorySpec{ + {Name: issue2766RepoName, URL: issue2766RepoURL, OCI: true}, }, logger: logger, valsRuntime: valsRuntime, @@ -190,10 +188,8 @@ func TestGetOCIChart_ResolvesToDifferentVersionsPicksSeparateCachePaths(t *testi logger := zap.NewExample().Sugar() st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: []RepositorySpec{ - {Name: issue2766RepoName, URL: issue2766RepoURL, OCI: true}, - }, + Repositories: []RepositorySpec{ + {Name: issue2766RepoName, URL: issue2766RepoURL, OCI: true}, }, logger: logger, valsRuntime: valsRuntime, @@ -250,10 +246,8 @@ func TestGetOCIChart_SkipRefreshSkipsConstraintResolution(t *testing.T) { logger := zap.NewExample().Sugar() st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: []RepositorySpec{ - {Name: issue2766RepoName, URL: issue2766RepoURL, OCI: true}, - }, + Repositories: []RepositorySpec{ + {Name: issue2766RepoName, URL: issue2766RepoURL, OCI: true}, }, logger: logger, valsRuntime: valsRuntime, @@ -312,10 +306,8 @@ func TestGetOCIChart_SharedConstraintResolvedOncePerProcess(t *testing.T) { logger := zap.NewExample().Sugar() st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: []RepositorySpec{ - {Name: issue2766RepoName, URL: issue2766RepoURL, OCI: true}, - }, + Repositories: []RepositorySpec{ + {Name: issue2766RepoName, URL: issue2766RepoURL, OCI: true}, }, logger: logger, valsRuntime: valsRuntime, diff --git a/pkg/state/issue_768_test.go b/pkg/state/issue_768_test.go index 957707c5..0e679b13 100644 --- a/pkg/state/issue_768_test.go +++ b/pkg/state/issue_768_test.go @@ -69,7 +69,7 @@ func TestForcedDownloadChartSerializesSameChart(t *testing.T) { numReleases := 5 var wg sync.WaitGroup - for i := 0; i < numReleases; i++ { + for i := range numReleases { wg.Add(1) go func(idx int) { defer wg.Done() @@ -164,7 +164,7 @@ func TestWithChartOperationLockSerializesSameChart(t *testing.T) { numReleases := 5 var wg sync.WaitGroup - for i := 0; i < numReleases; i++ { + for i := range numReleases { wg.Add(1) go func(idx int) { defer wg.Done() @@ -216,7 +216,7 @@ func TestWithChartOperationLockNoLockForLocalChart(t *testing.T) { numReleases := 5 var wg sync.WaitGroup - for i := 0; i < numReleases; i++ { + for i := range numReleases { wg.Add(1) go func(idx int) { defer wg.Done() diff --git a/pkg/state/oci_chart_lock_test.go b/pkg/state/oci_chart_lock_test.go index fec614f7..404810a2 100644 --- a/pkg/state/oci_chart_lock_test.go +++ b/pkg/state/oci_chart_lock_test.go @@ -37,7 +37,7 @@ func TestOCIChartFileLock(t *testing.T) { // Number of concurrent goroutines trying to acquire the lock numGoroutines := 5 - for i := 0; i < numGoroutines; i++ { + for i := range numGoroutines { wg.Add(1) go func(id int) { defer wg.Done() @@ -78,11 +78,8 @@ func TestOCIChartFileLock(t *testing.T) { // Multiple goroutines try to write to the same file numGoroutines := 10 - for i := 0; i < numGoroutines; i++ { - wg.Add(1) - go func() { - defer wg.Done() - + for range numGoroutines { + wg.Go(func() { fileLock := flock.New(lockFilePath) err := fileLock.Lock() require.NoError(t, err) @@ -95,7 +92,7 @@ func TestOCIChartFileLock(t *testing.T) { require.NoError(t, err) writeCount.Add(1) } - }() + }) } wg.Wait() @@ -195,11 +192,8 @@ func TestOCIChartSharedExclusiveLocks(t *testing.T) { var activeReaders atomic.Int32 var maxConcurrentReaders atomic.Int32 - for i := 0; i < numReaders; i++ { - wg.Add(1) - go func() { - defer wg.Done() - + for range numReaders { + wg.Go(func() { fileLock := flock.New(lockFilePath) // Acquire shared (read) lock err := fileLock.RLock() @@ -225,7 +219,7 @@ func TestOCIChartSharedExclusiveLocks(t *testing.T) { activeReaders.Add(-1) err = fileLock.Unlock() require.NoError(t, err) - }() + }) } wg.Wait() @@ -455,11 +449,8 @@ func TestOCIChartDoubleCheckLocking(t *testing.T) { var wg sync.WaitGroup // Simulate two processes trying to download the same chart - for i := 0; i < 2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - + for range 2 { + wg.Go(func() { fileLock := flock.New(lockFilePath) err := fileLock.Lock() require.NoError(t, err) @@ -476,7 +467,7 @@ func TestOCIChartDoubleCheckLocking(t *testing.T) { pullCount.Add(1) } // If directory exists, skip pull (use cached) - }() + }) } wg.Wait() diff --git a/pkg/state/release_filters.go b/pkg/state/release_filters.go index 4371ac6e..f9cc33ea 100644 --- a/pkg/state/release_filters.go +++ b/pkg/state/release_filters.go @@ -3,6 +3,7 @@ package state import ( "fmt" "regexp" + "slices" "strings" ) @@ -70,10 +71,8 @@ func SelectorsAreCompatible(selectorsA, selectorsB []string) (bool, error) { } for _, a := range filtersA { - for _, b := range filtersB { - if a.positiveLabelsCompatibleWith(b) { - return true, nil - } + if slices.ContainsFunc(filtersB, a.positiveLabelsCompatibleWith) { + return true, nil } } @@ -116,8 +115,8 @@ func ParseLabels(l string) (LabelFilter, error) { lf.positiveLabels = [][]string{} lf.negativeLabels = [][]string{} var err error - labels := strings.Split(l, ",") - for _, label := range labels { + labels := strings.SplitSeq(l, ",") + for label := range labels { if match := reLabelMismatch.MatchString(label); match { kv := strings.Split(label, "!=") lf.negativeLabels = append(lf.negativeLabels, kv) diff --git a/pkg/state/run_helm_dep_builds_skip_refresh_test.go b/pkg/state/run_helm_dep_builds_skip_refresh_test.go index be81160c..f0c5994c 100644 --- a/pkg/state/run_helm_dep_builds_skip_refresh_test.go +++ b/pkg/state/run_helm_dep_builds_skip_refresh_test.go @@ -3,6 +3,7 @@ package state import ( "os" "path/filepath" + "slices" "testing" "github.com/stretchr/testify/assert" @@ -82,7 +83,7 @@ version: 0.1.0 name: "release-level skipDeps overrides helmDefaults", helmDefaultsSkipDeps: false, helmDefaultsSkipRefresh: false, - releaseSkipDeps: boolPtr(true), + releaseSkipDeps: new(true), chartPath: "./chart", isLocal: true, expectedBuildDeps: false, // release-level skipDeps=true @@ -259,12 +260,10 @@ func TestRunHelmDepBuilds_SkipRefreshBehaviors(t *testing.T) { st := &HelmState{ logger: logger, - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: HelmSpec{ - SkipRefresh: tt.helmDefaultsSkipRefresh, - }, - Repositories: tt.repos, + HelmDefaults: HelmSpec{ + SkipRefresh: tt.helmDefaultsSkipRefresh, }, + Repositories: tt.repos, } builds := []*chartPrepareResult{ @@ -283,13 +282,7 @@ func TestRunHelmDepBuilds_SkipRefreshBehaviors(t *testing.T) { assert.Equal(t, tt.expectUpdateRepo, helm.updateRepoCalled, "UpdateRepo called mismatch: expected %v, got %v", tt.expectUpdateRepo, helm.updateRepoCalled) - hasSkipRefreshFlag := false - for _, f := range helm.buildDepsFlags { - if f == "--skip-refresh" { - hasSkipRefreshFlag = true - break - } - } + hasSkipRefreshFlag := slices.Contains(helm.buildDepsFlags, "--skip-refresh") assert.Equal(t, tt.expectSkipRefreshFlag, hasSkipRefreshFlag, "--skip-refresh flag mismatch: expected %v, got %v (flags: %v)", tt.expectSkipRefreshFlag, hasSkipRefreshFlag, helm.buildDepsFlags) }) @@ -322,11 +315,9 @@ func TestRunHelmDepBuilds_MultipleBuilds(t *testing.T) { helm := &multiBuildTracker{} st := &HelmState{ - logger: logger, - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: HelmSpec{SkipRefresh: false}, - Repositories: []RepositorySpec{{Name: "stable", URL: "https://example.com"}}, - }, + logger: logger, + HelmDefaults: HelmSpec{SkipRefresh: false}, + Repositories: []RepositorySpec{{Name: "stable", URL: "https://example.com"}}, } builds := []*chartPrepareResult{ @@ -344,13 +335,7 @@ func TestRunHelmDepBuilds_MultipleBuilds(t *testing.T) { expectedSkipRefresh := []bool{false, true} for i, flags := range helm.buildDepsCalls { - hasSkipRefresh := false - for _, f := range flags { - if f == "--skip-refresh" { - hasSkipRefresh = true - break - } - } + hasSkipRefresh := slices.Contains(flags, "--skip-refresh") assert.Equal(t, expectedSkipRefresh[i], hasSkipRefresh, "build %d skip-refresh flag mismatch: expected %v, got %v (flags: %v)", i, expectedSkipRefresh[i], hasSkipRefresh, flags) } @@ -462,9 +447,7 @@ func TestNeedsRepoUpdate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: tt.repos, - }, + Repositories: tt.repos, } result := st.NeedsRepoUpdate() assert.Equal(t, tt.expected, result, diff --git a/pkg/state/skip_test.go b/pkg/state/skip_test.go index cfcd3f2d..dcbb6e05 100644 --- a/pkg/state/skip_test.go +++ b/pkg/state/skip_test.go @@ -62,7 +62,7 @@ func TestSkipDepsAndSkipRefresh(t *testing.T) { helmDefaultsSkipDeps: false, helmDefaultsSkipRefresh: false, releaseSkipDeps: nil, - releaseSkipRefresh: boolPtr(true), + releaseSkipRefresh: new(true), optsSkipDeps: false, optsSkipRefresh: false, isLocal: true, @@ -115,7 +115,3 @@ func TestSkipDepsAndSkipRefresh(t *testing.T) { }) } } - -func boolPtr(b bool) *bool { - return &b -} diff --git a/pkg/state/span_test.go b/pkg/state/span_test.go index 64db29b2..d444f423 100644 --- a/pkg/state/span_test.go +++ b/pkg/state/span_test.go @@ -77,10 +77,8 @@ func newShimState(t *testing.T) (*HelmState, helmexec.Interface) { return &HelmState{ logger: zap.NewNop().Sugar(), fs: filesystem.DefaultFileSystem(), - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{ - {Name: "demo", Namespace: "apps", Chart: "./charts/demo"}, - }, + Releases: []ReleaseSpec{ + {Name: "demo", Namespace: "apps", Chart: "./charts/demo"}, }, }, helm } diff --git a/pkg/state/state.go b/pkg/state/state.go index 4a1b8faa..512855c3 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "io" + "maps" "net/url" "os" "path/filepath" @@ -917,7 +918,7 @@ func (st *HelmState) prepareSyncReleases(helm helmexec.Interface, additionalValu concurrency, numReleases, func() { - for i := 0; i < numReleases; i++ { + for i := range numReleases { jobs <- releases[i] } close(jobs) @@ -1158,7 +1159,7 @@ func (st *HelmState) DeleteReleasesForSync(affectedReleases *AffectedReleases, h workerLimit, len(releases), func() { - for i := 0; i < len(releases); i++ { + for i := range releases { jobQueue <- &releases[i] } close(jobQueue) @@ -1269,7 +1270,7 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme workerLimit, len(preps), func() { - for i := 0; i < len(preps); i++ { + for i := range preps { jobQueue <- &preps[i] } close(jobQueue) @@ -1711,13 +1712,13 @@ func (st *HelmState) rewriteChartDependencies(chartPath string) (string, func(), } type ChartDependency struct { - Name string `yaml:"name"` - Repository string `yaml:"repository"` - Data map[string]interface{} `yaml:",inline"` + Name string `yaml:"name"` + Repository string `yaml:"repository"` + Data map[string]any `yaml:",inline"` } type ChartMeta struct { - Dependencies []ChartDependency `yaml:"dependencies,omitempty"` - Data map[string]interface{} `yaml:",inline"` + Dependencies []ChartDependency `yaml:"dependencies,omitempty"` + Data map[string]any `yaml:",inline"` } var chartMeta ChartMeta @@ -1728,8 +1729,8 @@ func (st *HelmState) rewriteChartDependencies(chartPath string) (string, func(), modified := false for i := range chartMeta.Dependencies { dep := &chartMeta.Dependencies[i] - if strings.HasPrefix(dep.Repository, "file://") { - relPath := strings.TrimPrefix(dep.Repository, "file://") + if after, ok := strings.CutPrefix(dep.Repository, "file://"); ok { + relPath := after if !filepath.IsAbs(relPath) { absPath := filepath.Join(chartPath, relPath) @@ -1820,7 +1821,7 @@ func (st *HelmState) rewriteChartDependencies(chartPath string) (string, func(), if v, ok := d.Data["enabled"].(bool); ok { dep.Enabled = v } - if v, ok := d.Data["tags"].([]interface{}); ok { + if v, ok := d.Data["tags"].([]any); ok { tags := make([]string, 0, len(v)) for _, t := range v { if s, ok := t.(string); ok { @@ -1829,12 +1830,12 @@ func (st *HelmState) rewriteChartDependencies(chartPath string) (string, func(), } dep.Tags = tags } - if v, ok := d.Data["import-values"].([]interface{}); ok { + if v, ok := d.Data["import-values"].([]any); ok { normalized, err := maputil.RecursivelyStringifyMapKey(v) if err != nil { st.logger.Warnf("Failed to normalize import-values for dependency %s: %v", d.Name, err) } else { - dep.ImportValues = normalized.([]interface{}) + dep.ImportValues = normalized.([]any) } } req = append(req, dep) @@ -1866,7 +1867,7 @@ func (st *HelmState) rewriteChartDependencies(chartPath string) (string, func(), if err != nil { st.logger.Warnf("Failed to normalize import-values in Chart.lock for dependency %s: %v", ld.Name, err) } else { - ld.ImportValues = normalized.([]interface{}) + ld.ImportValues = normalized.([]any) } } } @@ -2000,7 +2001,7 @@ func (st *HelmState) appendSkipSchemaValidationFlagToChartifyTemplateArgs(templa } func hasTemplateArg(templateArgs, arg string) bool { - for _, token := range strings.Fields(templateArgs) { + for token := range strings.FieldsSeq(templateArgs) { if token == arg || strings.HasPrefix(token, arg+"=") { return true } @@ -2037,8 +2038,7 @@ func (st *HelmState) processLocalChart(normalizedChart, dir string, release *Rel // TLS/plain-http) so a prefetched chart behaves identically to one helm would have // downloaded itself during `helm upgrade`. See issue #2741. func (st *HelmState) chartFetchFlags(release *ReleaseSpec) []string { - var flags []string - flags = st.appendChartVersionFlags(flags, release) + flags := st.chartVersionFlags(release) // non-OCI chart should be verified here, matching flagsForUpgrade. if !st.IsOCIChart(release.Chart) { @@ -3103,7 +3103,7 @@ func (st *HelmState) prepareDiffReleases(helm helmexec.Interface, additionalValu concurrency, numReleases, func() { - for i := 0; i < numReleases; i++ { + for i := range numReleases { jobs <- releases[i] } close(jobs) @@ -3177,7 +3177,7 @@ func (st *HelmState) prepareDiffReleases(helm helmexec.Interface, additionalValu } }, func() { - for i := 0; i < numReleases; i++ { + for range numReleases { res := <-results if len(res.errors) > 0 { for _, e := range res.errors { @@ -3301,7 +3301,7 @@ func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []st workerLimit, len(preps), func() { - for i := 0; i < len(preps); i++ { + for i := range preps { jobQueue <- &preps[i] } close(jobQueue) @@ -3364,7 +3364,7 @@ func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []st } }, func() { - for i := 0; i < len(preps); i++ { + for range preps { res := <-results if res.err != nil { errs = append(errs, res.err) @@ -3525,12 +3525,8 @@ func (st *HelmState) GetReleasesWithLabels() []ReleaseSpec { spec := r labels := map[string]string{} // apply common labels - for k, v := range st.CommonLabels { - labels[k] = v - } - for k, v := range spec.Labels { - labels[k] = v - } + maps.Copy(labels, st.CommonLabels) + maps.Copy(labels, spec.Labels) // Let the release name, namespace, and chart be used as a tag labels["name"] = r.Name labels["namespace"] = r.Namespace @@ -3624,7 +3620,7 @@ func ConditionEnabled(r ReleaseSpec, values map[string]any) (bool, error) { return false, fmt.Errorf("environment values field '%s' not found", currentKey) } - iValues, ok = value.(map[string]interface{}) + iValues, ok = value.(map[string]any) if !ok { return false, fmt.Errorf("environment values field '%s' is not a map", currentKey) } @@ -4177,8 +4173,7 @@ func (st *HelmState) timeoutFlags(release *ReleaseSpec, ops *SyncOpts) []string } func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSpec, workerIndex int, opt *SyncOpts) ([]string, []string, error) { - var flags []string - flags = st.appendChartVersionFlags(flags, release) + flags := st.chartVersionFlags(release) flags = st.appendEnableDNSFlags(flags, release) flags = st.appendWaitFlags(flags, helm, release, opt) @@ -4324,8 +4319,7 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp } func (st *HelmState) flagsForTemplate(helm helmexec.Interface, release *ReleaseSpec, workerIndex int, opt *TemplateOpts) ([]string, []string, error) { - var flags []string - flags = st.appendChartVersionFlags(flags, release) + flags := st.chartVersionFlags(release) flags = st.appendHelmXFlags(flags, release) flags = st.appendEnableDNSFlags(flags, release) @@ -4375,8 +4369,7 @@ func (st *HelmState) flagsForDiff(helm helmexec.Interface, release *ReleaseSpec, } else { pluginsDir = cliv4.New().PluginsDirectory } - var flags []string - flags = st.appendChartVersionFlags(flags, release) + flags := st.chartVersionFlags(release) flags = st.appendEnableDNSFlags(flags, release) disableOpenAPIValidation := false @@ -4577,7 +4570,8 @@ func (st *HelmState) appendServerSideFlagsForDiff(flags []string, helm helmexec. return st.appendServerSideFlagsForUpgrade(flags, helm, release, serverSide) } -func (st *HelmState) appendChartVersionFlags(flags []string, release *ReleaseSpec) []string { +func (st *HelmState) chartVersionFlags(release *ReleaseSpec) []string { + var flags []string version := release.Version // Strip OCI digest from version (digest is handled in chart URL, not --version flag) if idx := strings.Index(version, "@"); idx >= 0 { @@ -5369,7 +5363,7 @@ func renderValsSecrets(e vals.Evaluator, input ...string) ([]string, error) { return nil, fmt.Errorf("type %T isn't supported", mapRendered["values"]) } - for i := 0; i < len(rendered); i++ { + for i := range rendered { output[i] = fmt.Sprintf("%v", rendered[i]) } } @@ -6238,7 +6232,7 @@ func (st *HelmState) getOCIChart(release *ReleaseSpec, tempDir string, helm helm flags = st.appendKeyringFlags(flags, release) flags = st.appendChartDownloadFlags(flags, release) // Use the clean chartVersion (without digest) from getOCIQualifiedChartName - // rather than appendChartVersionFlags which uses release.Version verbatim. + // rather than chartVersionFlags which uses release.Version verbatim. // The digest is already embedded in qualifiedChartName. // When a digest is present, omit --version: the digest is the authoritative // content identifier, and passing both causes errors in some Helm versions. @@ -6351,8 +6345,8 @@ func parseOCIChartRef(chartURL string) (baseURL, version, digest string) { // "@sha256:abc" → ("", "sha256:abc") // "" → ("", "") func parseVersionDigest(version string) (ver, digest string) { - if atIdx := strings.Index(version, "@"); atIdx >= 0 { - return version[:atIdx], version[atIdx+1:] + if before, after, ok := strings.Cut(version, "@"); ok { + return before, after } return version, "" } diff --git a/pkg/state/state_exec_tmpl.go b/pkg/state/state_exec_tmpl.go index 1a08903a..5dc78b87 100644 --- a/pkg/state/state_exec_tmpl.go +++ b/pkg/state/state_exec_tmpl.go @@ -3,7 +3,9 @@ package state import ( "errors" "fmt" + "maps" "reflect" + "slices" "strings" "dario.cat/mergo" @@ -99,8 +101,7 @@ func (st *HelmState) ExecuteTemplates() (*HelmState, error) { release, err := st.releaseWithInheritedTemplate(&rtWithDefaults, nil) if err != nil { - var cyclicInheritanceErr CyclicReleaseTemplateInheritanceError - if errors.As(err, &cyclicInheritanceErr) { + if cyclicInheritanceErr, ok := errors.AsType[CyclicReleaseTemplateInheritanceError](err); ok { return nil, fmt.Errorf("unable to load release %q with template: %w", rt.Name, cyclicInheritanceErr) } return nil, err @@ -112,9 +113,7 @@ func (st *HelmState) ExecuteTemplates() (*HelmState, error) { if release.Labels == nil { release.Labels = map[string]string{} } - for k, v := range st.CommonLabels { - release.Labels[k] = v - } + maps.Copy(release.Labels, st.CommonLabels) if len(release.ApiVersions) == 0 { release.ApiVersions = st.ApiVersions } @@ -179,11 +178,8 @@ func (st *HelmState) releaseWithInheritedTemplate(r *ReleaseSpec, inheritancePat path = append(path, templateName) var cycleFound bool - for _, t := range inheritancePath { - if t == templateName { - cycleFound = true - break - } + if slices.Contains(inheritancePath, templateName) { + cycleFound = true } if cycleFound { diff --git a/pkg/state/state_exec_tmpl_test.go b/pkg/state/state_exec_tmpl_test.go index e065ce2a..df2c0e8c 100644 --- a/pkg/state/state_exec_tmpl_test.go +++ b/pkg/state/state_exec_tmpl_test.go @@ -208,16 +208,14 @@ func TestHelmState_executeTemplates(t *testing.T) { fs: &filesystem.FileSystem{ Glob: func(s string) ([]string, error) { return nil, nil }}, basePath: ".", - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: HelmSpec{ - KubeContext: "test_context", - }, - Env: environment.Environment{Name: "test_env"}, - OverrideNamespace: "test-namespace_", - Repositories: nil, - Releases: []ReleaseSpec{ - tt.input, - }, + HelmDefaults: HelmSpec{ + KubeContext: "test_context", + }, + Env: environment.Environment{Name: "test_env"}, + OverrideNamespace: "test-namespace_", + Repositories: nil, + Releases: []ReleaseSpec{ + tt.input, }, RenderedValues: map[string]any{}, } @@ -324,16 +322,14 @@ func TestHelmState_recursiveRefsTemplates(t *testing.T) { fs: &filesystem.FileSystem{ Glob: func(s string) ([]string, error) { return nil, nil }, }, - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: HelmSpec{ - KubeContext: "test_context", - }, - Env: environment.Environment{Name: "test_env"}, - OverrideNamespace: "test-namespace_", - Repositories: nil, - Releases: []ReleaseSpec{ - tt.input, - }, + HelmDefaults: HelmSpec{ + KubeContext: "test_context", + }, + Env: environment.Environment{Name: "test_env"}, + OverrideNamespace: "test-namespace_", + Repositories: nil, + Releases: []ReleaseSpec{ + tt.input, }, RenderedValues: map[string]any{}, } @@ -411,9 +407,7 @@ func TestApplyDefaultInherit(t *testing.T) { tt := tests[i] t.Run(tt.name, func(t *testing.T) { st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - DefaultInherit: tt.defaultInherit, - }, + DefaultInherit: tt.defaultInherit, } got := st.applyDefaultInherit(tt.releaseInherit) if len(got) != len(tt.want) { @@ -439,31 +433,29 @@ func TestHelmState_executeTemplatesWithDefaultTemplates(t *testing.T) { Glob: func(s string) ([]string, error) { return nil, nil }, }, basePath: ".", - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: HelmSpec{ - KubeContext: "test_context", - }, - Env: environment.Environment{Name: "test_env"}, - Templates: map[string]TemplateSpec{ - "default": { - ReleaseSpec: ReleaseSpec{ - Namespace: "default-ns", - Labels: map[string]string{"managed": "true"}, - }, + HelmDefaults: HelmSpec{ + KubeContext: "test_context", + }, + Env: environment.Environment{Name: "test_env"}, + Templates: map[string]TemplateSpec{ + "default": { + ReleaseSpec: ReleaseSpec{ + Namespace: "default-ns", + Labels: map[string]string{"managed": "true"}, }, }, - DefaultInherit: DefaultInherits{"default"}, - Releases: []ReleaseSpec{ - { - Name: "app1", - Chart: "test-chart", - }, - { - Name: "app2", - Chart: "test-chart-2", - Inherit: Inherits{ - {Template: "default", Except: []string{"labels"}}, - }, + }, + DefaultInherit: DefaultInherits{"default"}, + Releases: []ReleaseSpec{ + { + Name: "app1", + Chart: "test-chart", + }, + { + Name: "app2", + Chart: "test-chart-2", + Inherit: Inherits{ + {Template: "default", Except: []string{"labels"}}, }, }, }, diff --git a/pkg/state/state_kubeversion_test.go b/pkg/state/state_kubeversion_test.go index f338ee20..cac17c24 100644 --- a/pkg/state/state_kubeversion_test.go +++ b/pkg/state/state_kubeversion_test.go @@ -39,9 +39,7 @@ func TestAppendApiVersionsFlags_KubeVersion(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - KubeVersion: tt.stateKubeVersion, - }, + KubeVersion: tt.stateKubeVersion, } release := &ReleaseSpec{ @@ -65,7 +63,7 @@ func TestAppendApiVersionsFlags_KubeVersion(t *testing.T) { require.True(t, foundKubeVersion, "Should have --kube-version flag in result") } else { // Should NOT have --kube-version flag - for i := 0; i < len(result); i++ { + for i := range result { require.NotEqual(t, "--kube-version", result[i], "Should not have --kube-version flag when nothing is set") } diff --git a/pkg/state/state_run.go b/pkg/state/state_run.go index 08b8600e..ce2d5b96 100644 --- a/pkg/state/state_run.go +++ b/pkg/state/state_run.go @@ -229,7 +229,7 @@ func GroupReleasesByDependency(releases []Release, opts PlanOptions) ([][]Releas var result [][]Release - for groupIndex := 0; groupIndex < len(plan); groupIndex++ { + for groupIndex := range plan { dagNodesInGroup := plan[groupIndex] var idsInGroup []string diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go index f7058019..ea416ad9 100644 --- a/pkg/state/state_test.go +++ b/pkg/state/state_test.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "reflect" + "slices" "testing" "github.com/Masterminds/semver/v3" @@ -139,12 +140,10 @@ func TestHelmState_applyDefaultsTo(t *testing.T) { tt := tests[i] t.Run(tt.name, func(t *testing.T) { state := &HelmState{ - basePath: tt.fields.BaseChartPath, - ReleaseSetSpec: ReleaseSetSpec{ - OverrideNamespace: tt.fields.Namespace, - Repositories: tt.fields.Repositories, - Releases: tt.fields.Releases, - }, + basePath: tt.fields.BaseChartPath, + OverrideNamespace: tt.fields.Namespace, + Repositories: tt.fields.Repositories, + Releases: tt.fields.Releases, } if state.ApplyOverrides(&tt.args.spec); !reflect.DeepEqual(tt.args.spec, tt.want) { t.Errorf("HelmState.ApplyOverrides() = %v, want %v", tt.args.spec, tt.want) @@ -153,10 +152,6 @@ func TestHelmState_applyDefaultsTo(t *testing.T) { } } -func boolValue(v bool) *bool { - return &v -} - func TestHelmState_flagsForUpgrade(t *testing.T) { enable := true disable := false @@ -1430,12 +1425,10 @@ func TestHelmState_flagsForUpgrade(t *testing.T) { tt := tests[i] t.Run(tt.name, func(t *testing.T) { state := &HelmState{ - basePath: "./", - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{*tt.release}, - HelmDefaults: tt.defaults, - }, - valsRuntime: valsRuntime, + basePath: "./", + Releases: []ReleaseSpec{*tt.release}, + HelmDefaults: tt.defaults, + valsRuntime: valsRuntime, } helm := &exectest.Helm{ Version: tt.version, @@ -1911,14 +1904,12 @@ func TestHelmState_flagsForTemplate(t *testing.T) { environments = make(map[string]EnvironmentSpec) } state := &HelmState{ - basePath: "./", - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{*tt.release}, - HelmDefaults: tt.defaults, - Environments: environments, - Env: environment.Environment{Name: envName}, - }, - valsRuntime: valsRuntime, + basePath: "./", + Releases: []ReleaseSpec{*tt.release}, + HelmDefaults: tt.defaults, + Environments: environments, + Env: environment.Environment{Name: envName}, + valsRuntime: valsRuntime, } helm := &exectest.Helm{ Version: tt.version, @@ -2241,9 +2232,7 @@ func TestHelmState_SyncRepos(t *testing.T) { t.Setenv(k, v) } state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: tt.repos, - }, + Repositories: tt.repos, } if _, _ = state.SyncRepos(tt.helm, map[string]bool{}); !reflect.DeepEqual(tt.helm.Repo, tt.want) { t.Errorf("HelmState.SyncRepos() for [%s] = %v, want %v", tt.name, tt.helm.Repo, tt.want) @@ -2387,9 +2376,7 @@ func TestHelmState_SyncReleases(t *testing.T) { tt := tests[i] t.Run(tt.name, func(t *testing.T) { state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: tt.releases, - }, + Releases: tt.releases, logger: logger, valsRuntime: valsRuntime, RenderedValues: map[string]any{}, @@ -2493,10 +2480,8 @@ func TestHelmState_SyncReleases_MissingValuesFileForUndesiredRelease(t *testing. tt := tests[i] t.Run(tt.name, func(t *testing.T) { state := &HelmState{ - basePath: ".", - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{tt.release}, - }, + basePath: ".", + Releases: []ReleaseSpec{tt.release}, logger: logger, valsRuntime: valsRuntime, RenderedValues: map[string]any{}, @@ -2640,9 +2625,7 @@ func TestHelmState_SyncReleasesAffectedRealeases(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: tt.releases, - }, + Releases: tt.releases, logger: logger, valsRuntime: valsRuntime, RenderedValues: map[string]any{}, @@ -2743,9 +2726,7 @@ func TestHelmState_SyncReleasesAffectedReleasesWithReinstallIfForbidden(t *testi for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: tt.releases, - }, + Releases: tt.releases, logger: logger, valsRuntime: valsRuntime, RenderedValues: map[string]any{}, @@ -2859,9 +2840,7 @@ func TestGetDeployedVersion(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{tt.release}, - }, + Releases: []ReleaseSpec{tt.release}, logger: logger, valsRuntime: valsRuntime, RenderedValues: map[string]any{}, @@ -2978,9 +2957,7 @@ func TestHelmState_DiffReleases(t *testing.T) { tt := tests[i] t.Run(tt.name, func(t *testing.T) { state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: tt.releases, - }, + Releases: tt.releases, logger: logger, valsRuntime: valsRuntime, RenderedValues: map[string]any{}, @@ -3078,10 +3055,8 @@ func TestHelmState_DiffFlags(t *testing.T) { tt := tests[i] t.Run(tt.name, func(t *testing.T) { state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: tt.releases, - HelmDefaults: tt.defaults, - }, + Releases: tt.releases, + HelmDefaults: tt.defaults, logger: logger, valsRuntime: valsRuntime, RenderedValues: map[string]any{}, @@ -3156,9 +3131,7 @@ func TestHelmState_SyncReleasesCleanup(t *testing.T) { t.Run(tt.name, func(t *testing.T) { numRemovedFiles := 0 state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: tt.releases, - }, + Releases: tt.releases, logger: logger, valsRuntime: valsRuntime, RenderedValues: map[string]any{}, @@ -3243,9 +3216,7 @@ func TestHelmState_DiffReleasesCleanup(t *testing.T) { t.Run(tt.name, func(t *testing.T) { numRemovedFiles := 0 state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: tt.releases, - }, + Releases: tt.releases, logger: logger, valsRuntime: valsRuntime, RenderedValues: map[string]any{}, @@ -3310,31 +3281,29 @@ generated: 2019-05-16T15:42:45.50486+09:00 state := &HelmState{ basePath: basePath, FilePath: filepath.Join(basePath, "helmfile.yaml"), - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{ - { - Chart: "/example", - }, - { - Chart: "./example", - }, - { - Chart: "published/deeper", - }, - { - Chart: "stable/envoy", - Version: "1.5.0", - }, - { - Chart: "stable/envoy", - Version: "1.4.0", - }, + Releases: []ReleaseSpec{ + { + Chart: "/example", }, - Repositories: []RepositorySpec{ - { - Name: "stable", - URL: "https://kubernetes-charts.storage.googleapis.com", - }, + { + Chart: "./example", + }, + { + Chart: "published/deeper", + }, + { + Chart: "stable/envoy", + Version: "1.5.0", + }, + { + Chart: "stable/envoy", + Version: "1.4.0", + }, + }, + Repositories: []RepositorySpec{ + { + Name: "stable", + URL: "https://kubernetes-charts.storage.googleapis.com", }, }, tempDir: tempDir, @@ -3407,21 +3376,19 @@ generated: 2023-08-01T23:04:02Z state := &HelmState{ basePath: basePath, FilePath: filepath.Join(basePath, "helmfile.yaml"), - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{ - { - Name: "example", - Chart: "myrepo/path_with_underscores/example", - Version: "1.0.0", - Namespace: "myns", - }, + Releases: []ReleaseSpec{ + { + Name: "example", + Chart: "myrepo/path_with_underscores/example", + Version: "1.0.0", + Namespace: "myns", }, - Repositories: []RepositorySpec{ - { - Name: "myrepo", - URL: "harbor.custom.com", - OCI: true, - }, + }, + Repositories: []RepositorySpec{ + { + Name: "myrepo", + URL: "harbor.custom.com", + OCI: true, }, }, tempDir: tempDir, @@ -3468,20 +3435,18 @@ generated: 2023-08-01T23:04:02Z state := &HelmState{ basePath: basePath, FilePath: filepath.Join(basePath, "helmfile.yaml"), - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{ - { - Name: "example", - Chart: "myrepo/path_with_underscores/example", - Version: "1.0.0", - }, + Releases: []ReleaseSpec{ + { + Name: "example", + Chart: "myrepo/path_with_underscores/example", + Version: "1.0.0", }, - Repositories: []RepositorySpec{ - { - Name: "myrepo", - URL: "harbor.custom.com", - OCI: true, - }, + }, + Repositories: []RepositorySpec{ + { + Name: "myrepo", + URL: "harbor.custom.com", + OCI: true, }, }, logger: logger, @@ -3502,32 +3467,30 @@ func TestHelmState_ResolveDeps_NoLockFile(t *testing.T) { state := &HelmState{ basePath: "/src", FilePath: "/src/helmfile.yaml", - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{ - { - Chart: "./..", - }, - { - Chart: "../examples", - }, - { - Chart: "../../helmfile", - }, - { - Chart: "published", - }, - { - Chart: "published/deeper", - }, - { - Chart: "stable/envoy", - }, + Releases: []ReleaseSpec{ + { + Chart: "./..", }, - Repositories: []RepositorySpec{ - { - Name: "stable", - URL: "https://kubernetes-charts.storage.googleapis.com", - }, + { + Chart: "../examples", + }, + { + Chart: "../../helmfile", + }, + { + Chart: "published", + }, + { + Chart: "published/deeper", + }, + { + Chart: "stable/envoy", + }, + }, + Repositories: []RepositorySpec{ + { + Name: "stable", + URL: "https://kubernetes-charts.storage.googleapis.com", }, }, logger: logger, @@ -3552,33 +3515,31 @@ func TestHelmState_ResolveDeps_NoLockFile_WithCustomLockFile(t *testing.T) { state := &HelmState{ basePath: "/src", FilePath: "/src/helmfile.yaml", - ReleaseSetSpec: ReleaseSetSpec{ - LockFile: "custom-lock-file", - Releases: []ReleaseSpec{ - { - Chart: "./..", - }, - { - Chart: "../examples", - }, - { - Chart: "../../helmfile", - }, - { - Chart: "published", - }, - { - Chart: "published/deeper", - }, - { - Chart: "stable/envoy", - }, + LockFile: "custom-lock-file", + Releases: []ReleaseSpec{ + { + Chart: "./..", }, - Repositories: []RepositorySpec{ - { - Name: "stable", - URL: "https://kubernetes-charts.storage.googleapis.com", - }, + { + Chart: "../examples", + }, + { + Chart: "../../helmfile", + }, + { + Chart: "published", + }, + { + Chart: "published/deeper", + }, + { + Chart: "stable/envoy", + }, + }, + Repositories: []RepositorySpec{ + { + Name: "stable", + URL: "https://kubernetes-charts.storage.googleapis.com", }, }, logger: logger, @@ -3648,7 +3609,7 @@ func TestHelmState_ReleaseStatuses(t *testing.T) { Values: []any{ "foo.yaml", }, - Installed: boolValue(false), + Installed: new(false), }, }, helm: &exectest.Helm{}, @@ -3659,10 +3620,8 @@ func TestHelmState_ReleaseStatuses(t *testing.T) { tt := tests[i] f := func(t *testing.T) { state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: tt.releases, - }, - logger: logger, + Releases: tt.releases, + logger: logger, fs: &filesystem.FileSystem{ FileExists: func(f string) (bool, error) { if f != "foo.yaml" { @@ -3735,10 +3694,8 @@ func TestHelmState_TestReleasesNoCleanUp(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: tt.releases, - }, - logger: logger, + Releases: tt.releases, + logger: logger, } errs := state.TestReleases(tt.helm, tt.cleanup, 1, 1) if (errs != nil) != tt.wantErr { @@ -3968,9 +3925,7 @@ func TestHelmState_NoReleaseMatched(t *testing.T) { tt := tests[i] f := func(t *testing.T) { state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: releases, - }, + Releases: releases, logger: logger, RenderedValues: map[string]any{}, } @@ -4004,7 +3959,7 @@ func TestHelmState_Delete(t *testing.T) { name: "delete wait enabled", deleteWait: true, wantErr: false, - desired: boolValue(true), + desired: new(true), installed: true, purge: false, deleted: []exectest.Release{{Name: "releaseA", Flags: []string{"--wait"}}}, @@ -4014,7 +3969,7 @@ func TestHelmState_Delete(t *testing.T) { deleteWait: true, deleteTimeout: 800, wantErr: false, - desired: boolValue(true), + desired: new(true), installed: true, purge: false, deleted: []exectest.Release{{Name: "releaseA", Flags: []string{"--wait", "--timeout", "800s"}}}, @@ -4022,7 +3977,7 @@ func TestHelmState_Delete(t *testing.T) { { name: "desired and installed (purge=false)", wantErr: false, - desired: boolValue(true), + desired: new(true), installed: true, purge: false, deleted: []exectest.Release{{Name: "releaseA", Flags: []string{}}}, @@ -4046,7 +4001,7 @@ func TestHelmState_Delete(t *testing.T) { { name: "desired and installed (purge=true)", wantErr: false, - desired: boolValue(true), + desired: new(true), installed: true, purge: true, deleted: []exectest.Release{{Name: "releaseA", Flags: []string{}}}, @@ -4054,7 +4009,7 @@ func TestHelmState_Delete(t *testing.T) { { name: "desired but not installed (purge=false)", wantErr: false, - desired: boolValue(true), + desired: new(true), installed: false, purge: false, deleted: []exectest.Release{{Name: "releaseA", Flags: []string{}}}, @@ -4062,7 +4017,7 @@ func TestHelmState_Delete(t *testing.T) { { name: "desired but not installed (purge=true)", wantErr: false, - desired: boolValue(true), + desired: new(true), installed: false, purge: true, deleted: []exectest.Release{{Name: "releaseA", Flags: []string{}}}, @@ -4070,7 +4025,7 @@ func TestHelmState_Delete(t *testing.T) { { name: "installed but filtered (purge=false)", wantErr: false, - desired: boolValue(false), + desired: new(false), installed: true, purge: false, deleted: []exectest.Release{{Name: "releaseA", Flags: []string{}}}, @@ -4078,7 +4033,7 @@ func TestHelmState_Delete(t *testing.T) { { name: "installed but filtered (purge=true)", wantErr: false, - desired: boolValue(false), + desired: new(false), installed: true, purge: true, deleted: []exectest.Release{{Name: "releaseA", Flags: []string{}}}, @@ -4086,7 +4041,7 @@ func TestHelmState_Delete(t *testing.T) { { name: "not installed, and filtered (purge=false)", wantErr: false, - desired: boolValue(false), + desired: new(false), installed: false, purge: false, deleted: []exectest.Release{{Name: "releaseA", Flags: []string{}}}, @@ -4094,7 +4049,7 @@ func TestHelmState_Delete(t *testing.T) { { name: "not installed, and filtered (purge=true)", wantErr: false, - desired: boolValue(false), + desired: new(false), installed: false, purge: true, deleted: []exectest.Release{{Name: "releaseA", Flags: []string{}}}, @@ -4147,14 +4102,12 @@ func TestHelmState_Delete(t *testing.T) { release, } state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: HelmSpec{ - KubeContext: tt.defKubeContext, - DeleteWait: tt.deleteWait, - DeleteTimeout: tt.deleteTimeout, - }, - Releases: releases, + HelmDefaults: HelmSpec{ + KubeContext: tt.defKubeContext, + DeleteWait: tt.deleteWait, + DeleteTimeout: tt.deleteTimeout, }, + Releases: releases, logger: logger, RenderedValues: map[string]any{}, } @@ -4248,12 +4201,10 @@ func TestDiffpareSyncReleases(t *testing.T) { release, } state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: releases, - HelmDefaults: *tt.helmDefaults, - }, - logger: logger, - valsRuntime: valsRuntime, + Releases: releases, + HelmDefaults: *tt.helmDefaults, + logger: logger, + valsRuntime: valsRuntime, } helm := &exectest.Helm{ Lists: map[exectest.ListKey]string{}, @@ -4313,15 +4264,13 @@ func TestPrepareDiffReleases_SkipDiffValidationOnInstall(t *testing.T) { Chart: "stable/foo", } if tt.perReleaseDisableValidation { - release.DisableValidationOnInstall = boolValue(true) + release.DisableValidationOnInstall = new(true) } st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: []ReleaseSpec{release}, - HelmDefaults: HelmSpec{}, - }, - logger: logger, - valsRuntime: valsRuntime, + Releases: []ReleaseSpec{release}, + HelmDefaults: HelmSpec{}, + logger: logger, + valsRuntime: valsRuntime, } lists := map[exectest.ListKey]string{} if tt.installed { @@ -4337,13 +4286,7 @@ func TestPrepareDiffReleases_SkipDiffValidationOnInstall(t *testing.T) { require.Len(t, results, 1) flags := results[0].flags - hasDisableValidation := false - for _, f := range flags { - if f == "--disable-validation" { - hasDisableValidation = true - break - } - } + hasDisableValidation := slices.Contains(flags, "--disable-validation") require.Equal(t, tt.wantDisableValidation, hasDisableValidation, "--disable-validation presence mismatch for case %q; flags: %v", tt.name, flags) }) @@ -4419,12 +4362,10 @@ func TestPrepareSyncReleases(t *testing.T) { release, } state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: releases, - HelmDefaults: *tt.helmDefaults, - }, - logger: logger, - valsRuntime: valsRuntime, + Releases: releases, + HelmDefaults: *tt.helmDefaults, + logger: logger, + valsRuntime: valsRuntime, } helm := &exectest.Helm{ Lists: map[exectest.ListKey]string{}, @@ -4444,7 +4385,7 @@ func TestReverse(t *testing.T) { num := 8 st := &HelmState{} - for i := 0; i < num; i++ { + for i := range num { name := fmt.Sprintf("%d", i) st.Helmfiles = append(st.Helmfiles, SubHelmfileSpec{ Path: name, @@ -4456,7 +4397,7 @@ func TestReverse(t *testing.T) { st.Reverse() - for i := 0; i < num; i++ { + for i := range num { j := num - 1 - i want := fmt.Sprintf("%d", j) @@ -4635,9 +4576,7 @@ func TestHelmState_SyncRepos_OCI(t *testing.T) { t.Run(tt.name, func(t *testing.T) { helm := &exectest.Helm{} state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: tt.repos, - }, + Repositories: tt.repos, } _, err := state.SyncRepos(helm, map[string]bool{}) if err != nil { @@ -4703,9 +4642,7 @@ func TestHelmState_SyncRepos_OCIOnly(t *testing.T) { t.Run(tt.name, func(t *testing.T) { helm := &exectest.Helm{} state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: tt.repos, - }, + Repositories: tt.repos, } _, err := state.SyncRepos(helm, map[string]bool{}, tt.opts...) if err != nil { @@ -4755,10 +4692,8 @@ func TestGenerateOutputFilePath(t *testing.T) { t.Run(tt.envName, func(t *testing.T) { st := &HelmState{ FilePath: tt.envName, - ReleaseSetSpec: ReleaseSetSpec{ - Env: environment.Environment{ - Name: tt.envName, - }, + Env: environment.Environment{ + Name: tt.envName, }, } ra := &ReleaseSpec{ @@ -5285,13 +5220,11 @@ func TestGenerateChartPath(t *testing.T) { }, } st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Env: environment.Environment{ - Name: "test-env", - Values: map[string]any{ - "cluster": map[string]any{ - "name": "my-test-cluster", - }, + Env: environment.Environment{ + Name: "test-env", + Values: map[string]any{ + "cluster": map[string]any{ + "name": "my-test-cluster", }, }, }, @@ -5348,14 +5281,12 @@ func TestGenerateOutputDir(t *testing.T) { t.Run(tt.testName, func(t *testing.T) { st := &HelmState{ FilePath: "test.yaml", - ReleaseSetSpec: ReleaseSetSpec{ - Env: environment.Environment{ - Name: "test-env", - KubeContext: "test-kubecontext", - Values: map[string]any{ - "cluster": map[string]any{ - "name": "my-test-cluster", - }, + Env: environment.Environment{ + Name: "test-env", + KubeContext: "test-kubecontext", + Values: map[string]any{ + "cluster": map[string]any{ + "name": "my-test-cluster", }, }, }, @@ -5562,9 +5493,7 @@ func TestNeedsPlainHttp(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: tt.defaults, - }, + HelmDefaults: tt.defaults, } require.Equal(t, tt.expected, st.needsPlainHttp(tt.release, tt.repo)) }) @@ -5608,9 +5537,7 @@ func TestNeedsInsecureSkipTLSVerify(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: tt.defaults, - }, + HelmDefaults: tt.defaults, } require.Equal(t, tt.expected, st.needsInsecureSkipTLSVerify(tt.release, tt.repo)) }) @@ -5714,11 +5641,9 @@ func Test_appendExtraDiffFlags(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { result := (&HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: HelmSpec{ - DiffArgs: test.inputDefaults, - TemplateArgs: test.inputTemplateDefaults, - }, + HelmDefaults: HelmSpec{ + DiffArgs: test.inputDefaults, + TemplateArgs: test.inputTemplateDefaults, }, }).appendExtraDiffFlags(test.inputFlags, test.inputOpts) if !reflect.DeepEqual(result, test.expected) { @@ -5763,10 +5688,8 @@ func Test_appendExtraSyncFlags(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { result := (&HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: HelmSpec{ - SyncArgs: test.inputDefaults, - }, + HelmDefaults: HelmSpec{ + SyncArgs: test.inputDefaults, }, }).appendExtraSyncFlags(test.inputFlags, test.inputOpts) if !reflect.DeepEqual(result, test.expected) { @@ -5871,10 +5794,8 @@ func TestHelmState_appendApiVersionsFlags(t *testing.T) { test.flags = []string{} } state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - KubeVersion: test.stateKubeVersion, - ApiVersions: test.stateApiVersion, - }, + KubeVersion: test.stateKubeVersion, + ApiVersions: test.stateApiVersion, } r := &ReleaseSpec{ KubeVersion: test.releaseKubeVersion, @@ -6036,12 +5957,10 @@ func TestResolveOCIConstraintVersion(t *testing.T) { newState := func(defaults HelmSpec) *HelmState { return &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: defaults, - Repositories: baseRepositories, - }, - logger: logger, - valsRuntime: valsRuntime, + HelmDefaults: defaults, + Repositories: baseRepositories, + logger: logger, + valsRuntime: valsRuntime, } } @@ -6242,7 +6161,7 @@ func TestSkipOCIConstraintResolution(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - st := &HelmState{ReleaseSetSpec: ReleaseSetSpec{HelmDefaults: tt.defaults}} + st := &HelmState{HelmDefaults: tt.defaults} require.Equal(t, tt.want, st.skipOCIConstraintResolution(&tt.release, tt.opts)) }) } @@ -6268,11 +6187,9 @@ func TestResolveOCIConstraintVersion_Memoized(t *testing.T) { }, } st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: []RepositorySpec{{Name: "myrepo", URL: repoURL, OCI: true}}, - }, - logger: logger, - valsRuntime: valsRuntime, + Repositories: []RepositorySpec{{Name: "myrepo", URL: repoURL, OCI: true}}, + logger: logger, + valsRuntime: valsRuntime, } first, changed, err := st.resolveOCIConstraintVersion(&ReleaseSpec{Name: "app", Chart: chartRef, Version: "~1"}, helm, qualified, "~1") @@ -6316,10 +6233,8 @@ func TestResolveOCIConstraintVersion_ChartInspectorFallback(t *testing.T) { qualified = "registry.example.com/charts/fallbackchart" ) st := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: []RepositorySpec{ - {Name: repoName, URL: repoURL, OCI: true}, - }, + Repositories: []RepositorySpec{ + {Name: repoName, URL: repoURL, OCI: true}, }, logger: logger, valsRuntime: valsRuntime, @@ -6563,13 +6478,11 @@ func TestIsOCIChart(t *testing.T) { {&HelmState{}, "myrepo/mychart:1.0.0", false}, { &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: []RepositorySpec{ - { - Name: "ocirepo", - URL: "ocirepo.com", - OCI: true, - }, + Repositories: []RepositorySpec{ + { + Name: "ocirepo", + URL: "ocirepo.com", + OCI: true, }, }, }, @@ -6578,12 +6491,10 @@ func TestIsOCIChart(t *testing.T) { }, { &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: []RepositorySpec{ - { - Name: "nonocirepo", - URL: "nonocirepo.com", - }, + Repositories: []RepositorySpec{ + { + Name: "nonocirepo", + URL: "nonocirepo.com", }, }, }, @@ -6612,14 +6523,14 @@ func TestAppendVerifyFlags(t *testing.T) { }{ { name: "Release with true verify flag", - release: &ReleaseSpec{Verify: boolValue(true)}, + release: &ReleaseSpec{Verify: new(true)}, repo: nil, helmDefaults: HelmSpec{}, expected: []string{"--verify"}, }, { name: "Release with false verify flag", - release: &ReleaseSpec{Verify: boolValue(false)}, + release: &ReleaseSpec{Verify: new(false)}, repo: nil, helmDefaults: HelmSpec{}, expected: []string(nil), @@ -6695,12 +6606,12 @@ func TestChartFetchFlags(t *testing.T) { }, { name: "devel release", - release: &ReleaseSpec{Chart: "myrepo/mychart", Devel: boolValue(true)}, + release: &ReleaseSpec{Chart: "myrepo/mychart", Devel: new(true)}, expected: []string{"--devel"}, }, { name: "release-level verify and keyring", - release: &ReleaseSpec{Chart: "myrepo/mychart", Verify: boolValue(true), Keyring: "/keys/release.gpg"}, + release: &ReleaseSpec{Chart: "myrepo/mychart", Verify: new(true), Keyring: "/keys/release.gpg"}, expected: []string{"--verify", "--keyring", "/keys/release.gpg"}, }, { @@ -6821,7 +6732,7 @@ func TestPrepareDiffReleases_ValueControlReleaseOverride(t *testing.T) { helmDefaults: &HelmSpec{}, release: &ReleaseSpec{ Name: "reuse-values-from-release", - ReuseValues: boolValue(true), + ReuseValues: new(true), }, }, { @@ -6832,7 +6743,7 @@ func TestPrepareDiffReleases_ValueControlReleaseOverride(t *testing.T) { helmDefaults: &HelmSpec{}, release: &ReleaseSpec{ Name: "reuse-values-from-cli", - ReuseValues: boolValue(false), + ReuseValues: new(false), }, }, { @@ -6845,7 +6756,7 @@ func TestPrepareDiffReleases_ValueControlReleaseOverride(t *testing.T) { }, release: &ReleaseSpec{ Name: "reuse-values-all", - ReuseValues: boolValue(true), + ReuseValues: new(true), }, }, { @@ -6856,7 +6767,7 @@ func TestPrepareDiffReleases_ValueControlReleaseOverride(t *testing.T) { }, release: &ReleaseSpec{ Name: "reset-values-from-helm-defaults", - ReuseValues: boolValue(false), + ReuseValues: new(false), }, }, { @@ -6865,7 +6776,7 @@ func TestPrepareDiffReleases_ValueControlReleaseOverride(t *testing.T) { helmDefaults: &HelmSpec{}, release: &ReleaseSpec{ Name: "reset-values-from-release", - ReuseValues: boolValue(false), + ReuseValues: new(false), }, }, { @@ -6876,7 +6787,7 @@ func TestPrepareDiffReleases_ValueControlReleaseOverride(t *testing.T) { helmDefaults: &HelmSpec{}, release: &ReleaseSpec{ Name: "reset-values-cli-overrides-release", - ReuseValues: boolValue(true), + ReuseValues: new(true), }, }, } @@ -6886,12 +6797,10 @@ func TestPrepareDiffReleases_ValueControlReleaseOverride(t *testing.T) { *tt.release, } state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: releases, - HelmDefaults: *tt.helmDefaults, - }, - logger: logger, - valsRuntime: valsRuntime, + Releases: releases, + HelmDefaults: *tt.helmDefaults, + logger: logger, + valsRuntime: valsRuntime, } helm := &exectest.Helm{ Lists: map[exectest.ListKey]string{}, @@ -6920,7 +6829,7 @@ func TestPrepareSyncReleases_ValueControlReleaseOverride(t *testing.T) { helmDefaults: &HelmSpec{}, release: &ReleaseSpec{ Name: "reuse-values-from-release", - ReuseValues: boolValue(true), + ReuseValues: new(true), }, }, { @@ -6931,7 +6840,7 @@ func TestPrepareSyncReleases_ValueControlReleaseOverride(t *testing.T) { helmDefaults: &HelmSpec{}, release: &ReleaseSpec{ Name: "reuse-values-from-cli", - ReuseValues: boolValue(false), + ReuseValues: new(false), }, }, { @@ -6944,7 +6853,7 @@ func TestPrepareSyncReleases_ValueControlReleaseOverride(t *testing.T) { }, release: &ReleaseSpec{ Name: "reuse-values-all", - ReuseValues: boolValue(true), + ReuseValues: new(true), }, }, { @@ -6955,7 +6864,7 @@ func TestPrepareSyncReleases_ValueControlReleaseOverride(t *testing.T) { }, release: &ReleaseSpec{ Name: "reset-values-from-helm-defaults", - ReuseValues: boolValue(false), + ReuseValues: new(false), }, }, { @@ -6964,7 +6873,7 @@ func TestPrepareSyncReleases_ValueControlReleaseOverride(t *testing.T) { helmDefaults: &HelmSpec{}, release: &ReleaseSpec{ Name: "reset-values-from-release", - ReuseValues: boolValue(false), + ReuseValues: new(false), }, }, { @@ -6975,7 +6884,7 @@ func TestPrepareSyncReleases_ValueControlReleaseOverride(t *testing.T) { helmDefaults: &HelmSpec{}, release: &ReleaseSpec{ Name: "reset-values-cli-overrides-release", - ReuseValues: boolValue(true), + ReuseValues: new(true), }, }, } @@ -6985,12 +6894,10 @@ func TestPrepareSyncReleases_ValueControlReleaseOverride(t *testing.T) { *tt.release, } state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Releases: releases, - HelmDefaults: *tt.helmDefaults, - }, - logger: logger, - valsRuntime: valsRuntime, + Releases: releases, + HelmDefaults: *tt.helmDefaults, + logger: logger, + valsRuntime: valsRuntime, } helm := &exectest.Helm{ Lists: map[exectest.ListKey]string{}, @@ -7142,13 +7049,11 @@ func TestHelmState_getKubeContext(t *testing.T) { } state := &HelmState{ - basePath: "./", - ReleaseSetSpec: ReleaseSetSpec{ - HelmDefaults: tt.defaults, - Environments: environments, - Env: environment.Environment{ - Name: envName, - }, + basePath: "./", + HelmDefaults: tt.defaults, + Environments: environments, + Env: environment.Environment{ + Name: envName, }, } @@ -7166,12 +7071,10 @@ func TestHelmState_getKubeContext(t *testing.T) { // through unchanged so we never disturb existing behavior. func TestResolveOCIAdhocDepChart(t *testing.T) { state := &HelmState{ - ReleaseSetSpec: ReleaseSetSpec{ - Repositories: []RepositorySpec{ - {Name: "ociregistry", URL: "registry.example.com:5000/charts", OCI: true}, - {Name: "ociregistry-trailing", URL: "registry.example.com:5000/charts/", OCI: true}, - {Name: "stable", URL: "https://charts.helm.sh/stable"}, - }, + Repositories: []RepositorySpec{ + {Name: "ociregistry", URL: "registry.example.com:5000/charts", OCI: true}, + {Name: "ociregistry-trailing", URL: "registry.example.com:5000/charts/", OCI: true}, + {Name: "stable", URL: "https://charts.helm.sh/stable"}, }, } diff --git a/pkg/telemetry/exporter.go b/pkg/telemetry/exporter.go index 6496f737..f20792f5 100644 --- a/pkg/telemetry/exporter.go +++ b/pkg/telemetry/exporter.go @@ -165,7 +165,7 @@ func propagatorsFromEnv(logger *zap.SugaredLogger) propagation.TextMapPropagator } var propagators []propagation.TextMapPropagator - for _, name := range strings.Split(raw, ",") { + for name := range strings.SplitSeq(raw, ",") { switch strings.TrimSpace(name) { case "tracecontext": propagators = append(propagators, propagation.TraceContext{}) diff --git a/pkg/testhelper/require_log.go b/pkg/testhelper/require_log.go index 2f0b6923..72314c18 100644 --- a/pkg/testhelper/require_log.go +++ b/pkg/testhelper/require_log.go @@ -17,10 +17,7 @@ func RequireLog(t *testing.T, dir string, bs *bytes.Buffer) { // Get the caller pkg used for instruction on rerunning the specific test pc, _, _, _ := runtime.Caller(1) funcName := runtime.FuncForPC(pc).Name() - lastSlash := strings.LastIndexByte(funcName, '/') - if lastSlash < 0 { - lastSlash = 0 - } + lastSlash := max(strings.LastIndexByte(funcName, '/'), 0) firstDot := strings.IndexByte(funcName[lastSlash:], '.') + lastSlash callerPkg := funcName[:firstDot] diff --git a/pkg/tmpl/context_tmpl.go b/pkg/tmpl/context_tmpl.go index f49ccddf..fe50cd8e 100644 --- a/pkg/tmpl/context_tmpl.go +++ b/pkg/tmpl/context_tmpl.go @@ -3,6 +3,7 @@ package tmpl import ( "bytes" "fmt" + "maps" "path/filepath" "strings" "text/template" @@ -36,14 +37,10 @@ func (c *Context) CreateFuncMap() template.FuncMap { } // add functions from the Context's createFuncMap() method to the funcMap - for name, f := range c.createFuncMap() { - funcMap[name] = f - } + maps.Copy(funcMap, c.createFuncMap()) // add aliased functions to the funcMap - for name, f := range aliased { - funcMap[name] = f - } + maps.Copy(funcMap, aliased) return funcMap } @@ -90,7 +87,7 @@ func (c *Context) newTemplate() (*template.Template, error) { includedNames := make(map[string]int) // Add the 'include' function here so we can close over t. - funcMap["include"] = func(name string, data interface{}) (string, error) { + funcMap["include"] = func(name string, data any) (string, error) { var buf strings.Builder if v, ok := includedNames[name]; ok { if v > recursionMaxNums { diff --git a/pkg/tmpl/context_tmpl_test.go b/pkg/tmpl/context_tmpl_test.go index 8d280360..001f446b 100644 --- a/pkg/tmpl/context_tmpl_test.go +++ b/pkg/tmpl/context_tmpl_test.go @@ -404,7 +404,7 @@ func TestContext_RenderTemplateToBuffer(t *testing.T) { }, } s := "Hello, {{ include \"name\" . }}!" - data := map[string]interface{}{ + data := map[string]any{ "Name": "Alice", } expected := "Hello, Alice!"