diff --git a/cmd/apply.go b/cmd/apply.go index 0431fc8b..76382752 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -40,6 +40,7 @@ func NewApplyCmd(globalCfg *config.GlobalImpl) *cobra.Command { f.IntVar(&applyOptions.Context, "context", 0, "output NUM lines of context around changes") f.StringVar(&applyOptions.Output, "output", "", "output format for diff plugin") f.BoolVar(&applyOptions.DetailedExitcode, "detailed-exitcode", false, "return a non-zero exit code 2 instead of 0 when there were changes detected AND the changes are synced successfully") + f.BoolVar(&applyOptions.StripTrailingCR, "strip-trailing-cr", false, "strip trailing carriage return on input") f.StringVar(&globalCfg.GlobalOptions.Args, "args", "", "pass args to helm exec") if !runtime.V1Mode { // TODO: Remove this function once Helmfile v0.x diff --git a/cmd/diff.go b/cmd/diff.go index 00bad84c..c269d94b 100644 --- a/cmd/diff.go +++ b/cmd/diff.go @@ -44,6 +44,7 @@ func NewDiffCmd(globalCfg *config.GlobalImpl) *cobra.Command { f.BoolVar(&diffOptions.ShowSecrets, "show-secrets", false, "do not redact secret values in the output. should be used for debug purpose only") f.BoolVar(&diffOptions.NoHooks, "no-hooks", false, "do not diff changes made by hooks.") f.BoolVar(&diffOptions.DetailedExitcode, "detailed-exitcode", false, "return a detailed exit code") + f.BoolVar(&diffOptions.StripTrailingCR, "strip-trailing-cr", false, "strip trailing carriage return on input") f.IntVar(&diffOptions.Context, "context", 0, "output NUM lines of context around changes") f.StringVar(&diffOptions.Output, "output", "", "output format for diff plugin") f.BoolVar(&diffOptions.SuppressSecrets, "suppress-secrets", false, "suppress secrets in the output. highly recommended to specify on CI/CD use-cases") diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index 792c81ba..da9e707c 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -2211,6 +2211,7 @@ type applyConfig struct { diffOutput string concurrency int detailedExitcode bool + stripTrailingCR bool interactive bool skipDiffOnInstall bool logger *zap.SugaredLogger @@ -2321,6 +2322,10 @@ func (a applyConfig) DetailedExitcode() bool { return a.detailedExitcode } +func (a applyConfig) StripTrailingCR() bool { + return a.stripTrailingCR +} + func (a applyConfig) Interactive() bool { return a.interactive } diff --git a/pkg/app/config.go b/pkg/app/config.go index 3ba235f4..9cfa1585 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -63,6 +63,7 @@ type ApplyConfigProvider interface { SuppressDiff() bool DetailedExitcode() bool + StripTrailingCR() bool Color() bool NoColor() bool @@ -130,6 +131,7 @@ type DiffConfigProvider interface { DAGConfig DetailedExitcode() bool + StripTrailingCR() bool Color() bool NoColor() bool Context() int diff --git a/pkg/app/diff_test.go b/pkg/app/diff_test.go index 10efd45a..340db337 100644 --- a/pkg/app/diff_test.go +++ b/pkg/app/diff_test.go @@ -36,6 +36,7 @@ type diffConfig struct { diffOutput string concurrency int detailedExitcode bool + stripTrailingCR bool interactive bool skipDiffOnInstall bool reuseValues bool @@ -126,6 +127,10 @@ func (a diffConfig) DetailedExitcode() bool { return a.detailedExitcode } +func (a diffConfig) StripTrailingCR() bool { + return a.stripTrailingCR +} + func (a diffConfig) Interactive() bool { return a.interactive } diff --git a/pkg/app/run.go b/pkg/app/run.go index c7ab9093..72e80ae9 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -137,7 +137,7 @@ func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfig // TODO Better way to detect diff on only filtered releases { - changedReleases, planningErrs = st.DiffReleases(helm, c.Values(), c.Concurrency(), detailedExitCode, c.IncludeTests(), c.Suppress(), c.SuppressSecrets(), c.ShowSecrets(), c.NoHooks(), c.SuppressDiff(), triggerCleanupEvent, diffOpts) + changedReleases, planningErrs = st.DiffReleases(helm, c.Values(), c.Concurrency(), detailedExitCode, c.StripTrailingCR(), c.IncludeTests(), c.Suppress(), c.SuppressSecrets(), c.ShowSecrets(), c.NoHooks(), c.SuppressDiff(), triggerCleanupEvent, diffOpts) var err error deletingReleases, err = st.DetectReleasesToBeDeletedForSync(helm, st.Releases) diff --git a/pkg/config/apply.go b/pkg/config/apply.go index 770c1be7..5862bd96 100644 --- a/pkg/config/apply.go +++ b/pkg/config/apply.go @@ -16,7 +16,8 @@ type ApplyOptions struct { Output string // DetailedExitcode is true if the exit code should be 2 instead of 0 if there were changes detected and the changes were synced successfully DetailedExitcode bool - + // StripTrailingCR is true if trailing carriage returns should be stripped during diffing + StripTrailingCR bool // TODO: Remove this function once Helmfile v0.x // DEPRECATED: Use skip-cleanup instead RetainValuesFiles bool @@ -96,6 +97,11 @@ func (a *ApplyImpl) DetailedExitcode() bool { return a.ApplyOptions.DetailedExitcode } +// StripTrailingCR is true if trailing carriage returns should be stripped during diffing +func (a *ApplyImpl) StripTrailingCR() bool { + return a.ApplyOptions.StripTrailingCR +} + // DiffOutput returns the diff output. func (a *ApplyImpl) DiffOutput() string { return a.ApplyOptions.Output diff --git a/pkg/config/diff.go b/pkg/config/diff.go index f488d5a1..4c118798 100644 --- a/pkg/config/diff.go +++ b/pkg/config/diff.go @@ -8,6 +8,8 @@ type DiffOptions struct { Values []string // DetailedExitcode is the detailed exit code DetailedExitcode bool + // StripTrailingCR is true if trailing carriage returns should be stripped during diffing + StripTrailingCR bool // IncludeTests is the include tests flag IncludeTests bool // SkipNeeds is the include crds flag @@ -110,6 +112,11 @@ func (t *DiffImpl) DetailedExitcode() bool { return t.DiffOptions.DetailedExitcode } +// StripTrailingCR is true if trailing carriage returns should be stripped during diffing +func (a *DiffImpl) StripTrailingCR() bool { + return a.DiffOptions.StripTrailingCR +} + // Output returns the output func (t *DiffImpl) DiffOutput() string { return t.DiffOptions.Output diff --git a/pkg/state/state.go b/pkg/state/state.go index cdf4aaf4..34a6ab83 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -1679,13 +1679,17 @@ type diffPrepareResult struct { upgradeDueToSkippedDiff bool } -func (st *HelmState) commonDiffFlags(detailedExitCode bool, includeTests bool, suppress []string, suppressSecrets bool, showSecrets bool, noHooks bool, opt *DiffOpts) []string { +func (st *HelmState) commonDiffFlags(detailedExitCode bool, stripTrailingCR bool, includeTests bool, suppress []string, suppressSecrets bool, showSecrets bool, noHooks bool, opt *DiffOpts) []string { var flags []string if detailedExitCode { flags = append(flags, "--detailed-exitcode") } + if stripTrailingCR { + flags = append(flags, "--strip-trailing-cr") + } + if includeTests { flags = append(flags, "--include-tests") } @@ -1731,7 +1735,7 @@ func (st *HelmState) commonDiffFlags(detailedExitCode bool, includeTests bool, s return flags } -func (st *HelmState) prepareDiffReleases(helm helmexec.Interface, additionalValues []string, concurrency int, detailedExitCode bool, includeTests bool, suppress []string, suppressSecrets bool, showSecrets bool, noHooks bool, opts ...DiffOpt) ([]diffPrepareResult, []error) { +func (st *HelmState) prepareDiffReleases(helm helmexec.Interface, additionalValues []string, concurrency int, detailedExitCode bool, stripTrailingCR bool, includeTests bool, suppress []string, suppressSecrets bool, showSecrets bool, noHooks bool, opts ...DiffOpt) ([]diffPrepareResult, []error) { opt := &DiffOpts{} for _, o := range opts { o.Apply(opt) @@ -1775,7 +1779,7 @@ func (st *HelmState) prepareDiffReleases(helm helmexec.Interface, additionalValu jobs := make(chan *ReleaseSpec, numReleases) results := make(chan diffPrepareResult, numReleases) resultsMap := map[string]diffPrepareResult{} - commonDiffFlags := st.commonDiffFlags(detailedExitCode, includeTests, suppress, suppressSecrets, showSecrets, noHooks, opt) + commonDiffFlags := st.commonDiffFlags(detailedExitCode, stripTrailingCR, includeTests, suppress, suppressSecrets, showSecrets, noHooks, opt) rs := []diffPrepareResult{} errs := []error{} @@ -1915,13 +1919,13 @@ type DiffOpt interface{ Apply(*DiffOpts) } // For example, terraform-provider-helmfile runs a helmfile-diff on `terraform plan` and another on `terraform apply`. // `terraform`, by design, fails when helmfile-diff outputs were not equivalent. // Stabilized helmfile-diff output rescues that. -func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []string, workerLimit int, detailedExitCode bool, includeTests bool, suppress []string, suppressSecrets, showSecrets, noHooks bool, suppressDiff, triggerCleanupEvents bool, opt ...DiffOpt) ([]ReleaseSpec, []error) { +func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []string, workerLimit int, detailedExitCode bool, stripTrailingCR bool, includeTests bool, suppress []string, suppressSecrets, showSecrets, noHooks bool, suppressDiff, triggerCleanupEvents bool, opt ...DiffOpt) ([]ReleaseSpec, []error) { opts := &DiffOpts{} for _, o := range opt { o.Apply(opts) } - preps, prepErrs := st.prepareDiffReleases(helm, additionalValues, workerLimit, detailedExitCode, includeTests, suppress, suppressSecrets, showSecrets, noHooks, opts) + preps, prepErrs := st.prepareDiffReleases(helm, additionalValues, workerLimit, detailedExitCode, stripTrailingCR, includeTests, suppress, suppressSecrets, showSecrets, noHooks, opts) if !opts.SkipCleanup { defer func() { diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go index 577effcc..1d5cecf4 100644 --- a/pkg/state/state_test.go +++ b/pkg/state/state_test.go @@ -1853,7 +1853,7 @@ func TestHelmState_DiffReleases(t *testing.T) { valsRuntime: valsRuntime, RenderedValues: map[string]interface{}{}, } - _, errs := state.DiffReleases(tt.helm, []string{}, 1, false, false, []string{}, false, false, false, false, false) + _, errs := state.DiffReleases(tt.helm, []string{}, 1, false, false, false, []string{}, false, false, false, false, false) if len(errs) > 0 { t.Errorf("unexpected error: %v", errs) } @@ -2069,7 +2069,7 @@ func TestHelmState_DiffReleasesCleanup(t *testing.T) { return nil } state = injectFs(state, testfs) - if _, errs := state.DiffReleases(tt.helm, []string{}, 1, false, false, []string{}, false, false, false, false, false); len(errs) > 0 { + if _, errs := state.DiffReleases(tt.helm, []string{}, 1, false, false, false, []string{}, false, false, false, false, false); len(errs) > 0 { t.Errorf("unexpected errors: %v", errs) } @@ -2740,7 +2740,7 @@ func TestDiffpareSyncReleases(t *testing.T) { Lists: map[exectest.ListKey]string{}, Helm3: true, } - results, es := state.prepareDiffReleases(helm, []string{}, 1, false, false, []string{}, false, false, false, tt.diffOptions) + results, es := state.prepareDiffReleases(helm, []string{}, 1, false, false, false, []string{}, false, false, false, tt.diffOptions) require.Len(t, es, 0) require.Len(t, results, 1) @@ -3225,3 +3225,33 @@ func TestGenerateChartPath(t *testing.T) { }) } } + +func TestCommonDiffFlags(t *testing.T) { + tests := []struct { + name string + // stripTrailingCR is a flag to strip trailing carriage returns from the output + stripTrailingCR bool + expected []string + }{ + { + name: "stripTrailingCR enabled", + stripTrailingCR: true, + expected: []string{ + "--strip-trailing-cr", + "--reset-values", + }, + }, + { + name: "stripTrailingCR disenabled", + expected: []string{ + "--reset-values", + }, + }, + } + for _, tt := range tests { + st := &HelmState{} + result := st.commonDiffFlags(false, tt.stripTrailingCR, false, []string{}, false, false, false, &DiffOpts{}) + + require.Equal(t, tt.expected, result) + } +}