Implement --timeout flag for helmfile sync command

- Add Timeout field to SyncOptions struct in pkg/config/sync.go
- Add --timeout flag to sync command in cmd/sync.go
- Add Timeout field to SyncOpts struct in pkg/state/state.go
- Modify timeoutFlags() function to prioritize CLI timeout over release and default configs
- Add test case to verify CLI timeout overrides other timeout settings
- Follow same pattern as existing --wait and --wait-for-jobs flags

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-08-23 08:17:14 +00:00
parent 3905d3502d
commit cbdb642aa6
4 changed files with 44 additions and 10 deletions

View File

@ -46,6 +46,7 @@ func NewSyncCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.BoolVar(&syncOptions.SyncReleaseLabels, "sync-release-labels", false, "sync release labels to the target release")
f.BoolVar(&syncOptions.Wait, "wait", false, `Override helmDefaults.wait setting "helm upgrade --install --wait"`)
f.BoolVar(&syncOptions.WaitForJobs, "wait-for-jobs", false, `Override helmDefaults.waitForJobs setting "helm upgrade --install --wait-for-jobs"`)
f.IntVar(&syncOptions.Timeout, "timeout", 0, `Override helmDefaults.timeout setting "helm upgrade --install --timeout" (default 0, which means no timeout)`)
f.BoolVar(&syncOptions.ReuseValues, "reuse-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reuse-values"`)
f.BoolVar(&syncOptions.ResetValues, "reset-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reset-values"`)
f.StringVar(&syncOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`)

View File

@ -24,6 +24,8 @@ type SyncOptions struct {
WaitRetries int
// WaitForJobs is the wait for jobs flag
WaitForJobs bool
// Timeout is the timeout flag in seconds
Timeout int
// ReuseValues is true if the helm command should reuse the values
ReuseValues bool
// ResetValues is true if helm command should reset values to charts' default
@ -124,6 +126,11 @@ func (t *SyncImpl) WaitForJobs() bool {
return t.SyncOptions.WaitForJobs
}
// Timeout returns the timeout
func (t *SyncImpl) Timeout() int {
return t.SyncOptions.Timeout
}
// ReuseValues returns the ReuseValues.
func (t *SyncImpl) ReuseValues() bool {
if !t.ResetValues() {

View File

@ -807,6 +807,7 @@ type SyncOpts struct {
Wait bool
WaitRetries int
WaitForJobs bool
Timeout int
SyncReleaseLabels bool
ReuseValues bool
ResetValues bool
@ -2271,7 +2272,7 @@ func (st *HelmState) TestReleases(helm helmexec.Interface, cleanup bool, timeout
}
if timeout == EmptyTimeout {
flags = append(flags, st.timeoutFlags(&release)...)
flags = append(flags, st.timeoutFlags(&release, nil)...)
} else {
duration := strconv.Itoa(timeout)
duration += "s"
@ -2751,13 +2752,16 @@ func (st *HelmState) needsInsecureSkipTLSVerify(release *ReleaseSpec, repo *Repo
return relSkipTLSVerify || st.HelmDefaults.InsecureSkipTLSVerify || repoSkipTLSVerify
}
func (st *HelmState) timeoutFlags(release *ReleaseSpec) []string {
func (st *HelmState) timeoutFlags(release *ReleaseSpec, ops *SyncOpts) []string {
var flags []string
timeout := st.HelmDefaults.Timeout
if release.Timeout != nil {
timeout = *release.Timeout
}
if ops != nil && ops.Timeout > 0 {
timeout = ops.Timeout
}
if timeout != 0 {
duration := strconv.Itoa(timeout)
duration += "s"
@ -2783,7 +2787,7 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp
flags = st.appendKeyringFlags(flags, release)
}
flags = append(flags, st.timeoutFlags(release)...)
flags = append(flags, st.timeoutFlags(release, opt)...)
if release.Force != nil && *release.Force || release.Force == nil && st.HelmDefaults.Force {
flags = append(flags, "--force")

View File

@ -166,12 +166,13 @@ func TestHelmState_flagsForUpgrade(t *testing.T) {
}
tests := []struct {
name string
version *semver.Version
defaults HelmSpec
release *ReleaseSpec
want []string
wantErr string
name string
version *semver.Version
defaults HelmSpec
release *ReleaseSpec
syncOpts *SyncOpts
want []string
wantErr string
}{
{
name: "no-options",
@ -455,6 +456,27 @@ func TestHelmState_flagsForUpgrade(t *testing.T) {
"--namespace", "test-namespace",
},
},
{
name: "timeout-from-cli-flag",
defaults: HelmSpec{
Timeout: 123,
},
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Timeout: some(456),
Name: "test-charts",
Namespace: "test-namespace",
},
syncOpts: &SyncOpts{
Timeout: 789,
},
want: []string{
"--version", "0.1",
"--timeout", "789s",
"--namespace", "test-namespace",
},
},
{
name: "atomic",
defaults: HelmSpec{
@ -737,7 +759,7 @@ func TestHelmState_flagsForUpgrade(t *testing.T) {
Version: tt.version,
}
args, _, err := state.flagsForUpgrade(helm, tt.release, 0, nil)
args, _, err := state.flagsForUpgrade(helm, tt.release, 0, tt.syncOpts)
if err != nil && tt.wantErr == "" {
t.Errorf("unexpected error flagsForUpgrade: %v", err)
}