feat: add an arg that passing description to `helm upgrade` command (#2497)

* feat: add an arg that passing description to `helm upgrade` command

fix: github actions

Signed-off-by: swimablefish <swimablefish@gmail.com>

* fix: lint and test failed

Signed-off-by: swimablefish <swimablefish@gmail.com>

* feat: encapsulation

Signed-off-by: swimablefish <swimablefish@gmail.com>

* feat: add version gate

Signed-off-by: swimablefish <swimablefish@gmail.com>

* feat: rephrase

Signed-off-by: swimablefish <swimablefish@gmail.com>

---------

Signed-off-by: swimablefish <swimablefish@gmail.com>
This commit is contained in:
Jinyu 2026-03-24 21:01:44 +08:00 committed by GitHub
parent e72315a876
commit c70b20ad7a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 246 additions and 7 deletions

View File

@ -72,6 +72,7 @@ func NewApplyCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.StringVar(&applyOptions.TrackMode, "track-mode", "", "Track mode for releases: 'helm' (default), 'helm-legacy' (Helm v4 only), or 'kubedog'")
f.IntVar(&applyOptions.TrackTimeout, "track-timeout", 0, `Timeout in seconds for kubedog tracking (0 to use default 300s timeout)`)
f.BoolVar(&applyOptions.TrackLogs, "track-logs", false, "Enable log streaming with kubedog tracking")
f.StringVar(&applyOptions.Description, "description", "", `Set description for all releases. If set, overridesdescriptions in helmfile.yaml. Will be passed to "helm upgrade --description"`)
return cmd
}

View File

@ -57,6 +57,7 @@ func NewSyncCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.StringVar(&syncOptions.TrackMode, "track-mode", "", "Track mode for releases: 'helm' (default), 'helm-legacy' (Helm v4 only), or 'kubedog'")
f.IntVar(&syncOptions.TrackTimeout, "track-timeout", 0, `Timeout in seconds for kubedog tracking (0 to use default 300s timeout)`)
f.BoolVar(&syncOptions.TrackLogs, "track-logs", false, "Enable log streaming with kubedog tracking")
f.StringVar(&syncOptions.Description, "description", "", `Set description for all releases. If set, overrides descriptions in helmfile.yaml. Will be passed to "helm upgrade --description"`)
return cmd
}

View File

@ -1798,6 +1798,7 @@ Do you really want to apply?
TrackMode: c.TrackMode(),
TrackTimeout: c.TrackTimeout(),
TrackLogs: c.TrackLogs(),
Description: c.Description(),
}
return subst.SyncReleases(&affectedReleases, helm, c.Values(), c.Concurrency(), syncOpts)
}))
@ -2267,6 +2268,7 @@ Do you really want to sync?
TrackMode: c.TrackMode(),
TrackTimeout: c.TrackTimeout(),
TrackLogs: c.TrackLogs(),
Description: c.Description(),
}
return subst.SyncReleases(&affectedReleases, helm, c.Values(), c.Concurrency(), opts)
}))

View File

@ -2610,6 +2610,10 @@ func (a applyConfig) TrackLogs() bool {
return a.trackLogs
}
func (a applyConfig) Description() string {
return ""
}
type depsConfig struct {
skipRepos bool
includeTransitiveNeeds bool

View File

@ -92,6 +92,8 @@ type ApplyConfigProvider interface {
TrackTimeout() int
TrackLogs() bool
Description() string
concurrencyConfig
interactive
loggingConfig
@ -129,6 +131,8 @@ type SyncConfigProvider interface {
TrackTimeout() int
TrackLogs() bool
Description() string
DAGConfig
concurrencyConfig

View File

@ -88,6 +88,8 @@ type ApplyOptions struct {
TrackTimeout int
// TrackLogs enables log streaming with kubedog
TrackLogs bool
// Description is the description that will be passed to helm upgrade --description
Description string
}
// NewApply creates a new Apply
@ -314,6 +316,11 @@ func (a *ApplyImpl) TrackLogs() bool {
return a.ApplyOptions.TrackLogs
}
// Description returns the description.
func (a *ApplyImpl) Description() string {
return a.ApplyOptions.Description
}
func (a *ApplyImpl) ValidateConfig() error {
validTrackModes := []string{"helm", "helm-legacy", "kubedog"}
if a.ApplyOptions.TrackMode != "" && !slices.Contains(validTrackModes, a.ApplyOptions.TrackMode) {

View File

@ -59,6 +59,8 @@ type SyncOptions struct {
TrackTimeout int
// TrackLogs enables log streaming with kubedog
TrackLogs bool
// Description is the description that will be passed to helm upgrade --description
Description string
}
// NewSyncOptions creates a new Apply
@ -214,6 +216,11 @@ func (t *SyncImpl) TrackLogs() bool {
return t.SyncOptions.TrackLogs
}
// Description returns the description.
func (t *SyncImpl) Description() string {
return t.SyncOptions.Description
}
func (t *SyncImpl) ValidateConfig() error {
validTrackModes := []string{"helm", "helm-legacy", "kubedog"}
if t.SyncOptions.TrackMode != "" && !slices.Contains(validTrackModes, t.SyncOptions.TrackMode) {

View File

@ -353,7 +353,10 @@ type ReleaseSpec struct {
UnitTests []string `yaml:"unitTests,omitempty"`
// Name is the name of this release
Name string `yaml:"name,omitempty"`
Name string `yaml:"name,omitempty"`
// Description is the description for this release that will be passed to helm upgrade with --description flag
Description string `yaml:"description,omitempty"`
Namespace string `yaml:"namespace,omitempty"`
Labels map[string]string `yaml:"labels,omitempty"`
Values []any `yaml:"values,omitempty"`
@ -909,6 +912,7 @@ type SyncOpts struct {
TrackMode string
TrackTimeout int
TrackLogs bool
Description string
}
type SyncOpt interface{ Apply(*SyncOpts) }
@ -3394,6 +3398,28 @@ func (st *HelmState) appendChartDownloadFlags(flags []string, release *ReleaseSp
return flags
}
// appendDescriptionFlags appends the helm command-line flag for release description
// Command line takes precedence over config file
func (st *HelmState) appendDescriptionFlags(flags []string, release *ReleaseSpec, opt *SyncOpts, helm helmexec.Interface) ([]string, error) {
description := release.Description
if opt != nil && opt.Description != "" {
description = opt.Description
}
if description != "" {
if !helm.IsVersionAtLeast("3.3.0") {
// Determine error message based on source
if opt != nil && opt.Description != "" {
return nil, fmt.Errorf("--description flag requires Helm 3.3.0 or greater")
}
return nil, fmt.Errorf("releases[].description requires Helm 3.3.0 or greater")
}
flags = append(flags, "--description", description)
}
return flags, nil
}
func (st *HelmState) needsPlainHttp(release *ReleaseSpec, repo *RepositorySpec) bool {
var repoPlainHttp, relPlainHttp bool
if repo != nil {
@ -3523,6 +3549,11 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp
flags = st.appendPostRenderFlags(flags, release, postRenderer, helm)
flags, err := st.appendDescriptionFlags(flags, release, opt, helm)
if err != nil {
return nil, nil, err
}
var postRendererArgs []string
if opt != nil {
postRendererArgs = opt.PostRendererArgs

View File

@ -908,6 +908,188 @@ func TestHelmState_flagsForUpgrade(t *testing.T) {
"--namespace", "test-namespace",
},
},
{
name: "description-from-release",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &disable,
},
version: semver.MustParse("3.10.0"),
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Name: "test-charts",
Namespace: "test-namespace",
Description: "Release description from config",
},
syncOpts: &SyncOpts{},
want: []string{
"--version", "0.1",
"--description", "Release description from config",
"--namespace", "test-namespace",
},
},
{
name: "description-from-cli",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &disable,
},
version: semver.MustParse("3.10.0"),
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Name: "test-charts",
Namespace: "test-namespace",
},
syncOpts: &SyncOpts{
Description: "CLI description from --description flag",
},
want: []string{
"--version", "0.1",
"--description", "CLI description from --description flag",
"--namespace", "test-namespace",
},
},
{
name: "description-cli-overrides-release",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &disable,
},
version: semver.MustParse("3.10.0"),
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Name: "test-charts",
Namespace: "test-namespace",
Description: "Release description from config",
},
syncOpts: &SyncOpts{
Description: "CLI description overrides config",
},
want: []string{
"--version", "0.1",
"--description", "CLI description overrides config",
"--namespace", "test-namespace",
},
},
{
name: "description-empty-string-not-passed",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &disable,
},
version: semver.MustParse("3.10.0"),
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Name: "test-charts",
Namespace: "test-namespace",
Description: "",
},
syncOpts: &SyncOpts{
Description: "",
},
want: []string{
"--version", "0.1",
"--namespace", "test-namespace",
},
},
{
name: "description-from-config-unsupported-version-3.1.0",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &disable,
},
version: semver.MustParse("3.1.0"),
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Name: "test-charts",
Namespace: "test-namespace",
Description: "Release description from config",
},
syncOpts: &SyncOpts{},
wantErr: "releases[].description requires Helm 3.3.0 or greater",
},
{
name: "description-from-config-unsupported-version-3.2.4",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &disable,
},
version: semver.MustParse("3.2.4"),
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Name: "test-charts",
Namespace: "test-namespace",
Description: "Release description from config",
},
syncOpts: &SyncOpts{},
wantErr: "releases[].description requires Helm 3.3.0 or greater",
},
{
name: "description-from-cli-unsupported-version",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &disable,
},
version: semver.MustParse("3.2.0"),
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Name: "test-charts",
Namespace: "test-namespace",
},
syncOpts: &SyncOpts{
Description: "CLI description from --description flag",
},
wantErr: "--description flag requires Helm 3.3.0 or greater",
},
{
name: "description-empty-on-old-version",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &disable,
},
version: semver.MustParse("3.1.0"),
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Name: "test-charts",
Namespace: "test-namespace",
// No description set
},
syncOpts: &SyncOpts{},
want: []string{
"--version", "0.1",
"--namespace", "test-namespace",
// No --description flag should appear
},
},
{
name: "description-from-config-supported-version-3.3.0",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &disable,
},
version: semver.MustParse("3.3.0"),
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Name: "test-charts",
Namespace: "test-namespace",
Description: "Release description from config",
},
syncOpts: &SyncOpts{},
want: []string{
"--version", "0.1",
"--description", "Release description from config",
"--namespace", "test-namespace",
},
},
}
for i := range tests {
tt := tests[i]

View File

@ -38,39 +38,39 @@ func TestGenerateID(t *testing.T) {
run(testcase{
subject: "baseline",
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
want: "foo-values-5bc9c89c6b",
want: "foo-values-6ccb848dcd",
})
run(testcase{
subject: "different bytes content",
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
data: []byte(`{"k":"v"}`),
want: "foo-values-7bf9c8bcdf",
want: "foo-values-5bcbbc4c85",
})
run(testcase{
subject: "different map content",
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
data: map[string]any{"k": "v"},
want: "foo-values-65694d8947",
want: "foo-values-7c6468f955",
})
run(testcase{
subject: "different chart",
release: ReleaseSpec{Name: "foo", Chart: "stable/envoy"},
want: "foo-values-856c5f7dd5",
want: "foo-values-8645f5847f",
})
run(testcase{
subject: "different name",
release: ReleaseSpec{Name: "bar", Chart: "incubator/raw"},
want: "bar-values-fff55fbf5",
want: "bar-values-54bd8c865",
})
run(testcase{
subject: "specific ns",
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw", Namespace: "myns"},
want: "myns-foo-values-6bfbb74765",
want: "myns-foo-values-b4849b445",
})
for id, n := range ids {