mirror of
https://github.com/helmfile/helmfile.git
synced 2026-10-18 19:01:50 +02:00
build: update Go to 1.27.1 and modernize the codebase (#2798)
Toolchain and CI:
- Bump go directive from 1.26.8 to 1.27.1 (go.mod)
- Use golang:1.27-alpine builder images in all Dockerfiles
- Bump golangci-lint to v2.13.2 (first release with go1.27 support)
- Add CI gate: `go fix -diff` fails when outdated Go patterns are
detected (locally: `make check-modernize`)
Note: darwin binaries now require macOS 13 or later.
Lint fixes required by golangci-lint v2.13.2:
- goconst: ignore tests (all 436 findings were test-only; goconst
got stricter since v2.12 and this option was added for it)
- openai.go: keep deprecated MaxTokens deliberately with a nolint
rationale (max_tokens is the only form universally supported by
OpenAI-compatible backends like One-API, LiteLLM, Ollama shim)
- state.go: drop always-nil flags param from appendChartVersionFlags
(renamed to chartVersionFlags, unparam)
Modernization (go fix ./..., 62 files):
- interface{} -> any, maps.Copy, strings.SplitSeq, range-over-int,
builtin min/max, slices.Contains/ContainsFunc/Sort, WaitGroup.Go,
reflect.Type.Fields(), new(expr)
- exit_error.go: strings.Builder + fmt.Fprintf instead of string
concatenation and WriteString(fmt.Sprintf(...)) (QF1012)
- chart_dependency.go: strings.CutLast for OCI dependency helpers
Signed-off-by: yxxhero <aiopsclub@163.com>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
|
||||
+7
-11
@@ -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"},
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
+37
-52
@@ -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()) })
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+33
-39
@@ -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, ""
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+232
-329
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user