diff --git a/README.md b/README.md index 38385fce..b981e99e 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ Examples: Flags: -h, --help help for upgrade + --detailed-exitcode return a non-zero exit code when there are changes --reset-values reset the values to the ones built into the chart and merge in any new values --reuse-values reuse the last release's values and merge in any new values --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) diff --git a/cmd/upgrade.go b/cmd/upgrade.go index 50b9d156..7fa7bcd9 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -9,20 +9,22 @@ import ( "github.com/databus23/helm-diff/manifest" "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" + "errors" ) type diffCmd struct { - release string - chart string - chartVersion string - client helm.Interface - valueFiles valueFiles - values []string - reuseValues bool - resetValues bool - allowUnreleased bool - suppressedKinds []string - outputContext int + release string + chart string + chartVersion string + client helm.Interface + detailedExitCode bool + valueFiles valueFiles + values []string + reuseValues bool + resetValues bool + allowUnreleased bool + suppressedKinds []string + outputContext int } const globalUsage = `Show a diff explaining what a helm upgrade would change. @@ -61,6 +63,7 @@ func newChartCommand() *cobra.Command { f := cmd.Flags() f.StringVar(&diff.chartVersion, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used") + f.BoolVar(&diff.detailedExitCode, "detailed-exitcode", false, "return a non-zero exit code when there are changes") f.BoolP("suppress-secrets", "q", false, "suppress secrets in the output") f.VarP(&diff.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)") f.StringArrayVar(&diff.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") @@ -139,7 +142,11 @@ func (d *diffCmd) run() error { newSpecs = manifest.Parse(upgradeResponse.Release.Manifest, upgradeResponse.Release.Namespace) } - diff.DiffManifests(currentSpecs, newSpecs, d.suppressedKinds, d.outputContext, os.Stdout) + seenAnyChanges := diff.DiffManifests(currentSpecs, newSpecs, d.suppressedKinds, d.outputContext, os.Stdout) + + if d.detailedExitCode && seenAnyChanges { + return errors.New("identified at least one change, exiting with non-zero exit code (detailed-exitcode parameter enabled)") + } return nil } diff --git a/diff/diff.go b/diff/diff.go index 651ad633..61c52252 100644 --- a/diff/diff.go +++ b/diff/diff.go @@ -12,18 +12,28 @@ import ( "github.com/databus23/helm-diff/manifest" ) -func DiffManifests(oldIndex, newIndex map[string]*manifest.MappingResult, suppressedKinds []string, context int, to io.Writer) { +func DiffManifests(oldIndex, newIndex map[string]*manifest.MappingResult, suppressedKinds []string, context int, to io.Writer) bool { + seenAnyChanges := false + emptyMapping := &manifest.MappingResult{} for key, oldContent := range oldIndex { if newContent, ok := newIndex[key]; ok { if oldContent.Content != newContent.Content { // modified fmt.Fprintf(to, ansi.Color("%s has changed:", "yellow")+"\n", key) - printDiff(suppressedKinds, oldContent.Kind, context, oldContent.Content, newContent.Content, to) + diffs := generateDiff(oldContent, newContent) + if len(diffs) > 0 { + seenAnyChanges = true + } + printDiff(suppressedKinds, oldContent.Kind, context, diffs, to) } } else { // removed fmt.Fprintf(to, ansi.Color("%s has been removed:", "yellow")+"\n", key) - printDiff(suppressedKinds, oldContent.Kind, context, oldContent.Content, "", to) + diffs := generateDiff(oldContent, emptyMapping) + if len(diffs) > 0 { + seenAnyChanges = true + } + printDiff(suppressedKinds, oldContent.Kind, context, diffs, to) } } @@ -31,12 +41,22 @@ func DiffManifests(oldIndex, newIndex map[string]*manifest.MappingResult, suppre if _, ok := oldIndex[key]; !ok { // added fmt.Fprintf(to, ansi.Color("%s has been added:", "yellow")+"\n", key) - printDiff(suppressedKinds, newContent.Kind, context, "", newContent.Content, to) + diffs := generateDiff(emptyMapping, newContent) + if len(diffs) > 0 { + seenAnyChanges = true + } + printDiff(suppressedKinds, newContent.Kind, context, diffs, to) } } + return seenAnyChanges +} + +func generateDiff(oldContent *manifest.MappingResult, newContent *manifest.MappingResult) []difflib.DiffRecord { + const sep = "\n" + return difflib.Diff(strings.Split(oldContent.Content, sep), strings.Split(newContent.Content, sep)) } -func printDiff(suppressedKinds []string, kind string, context int, before, after string, to io.Writer) { +func printDiff(suppressedKinds []string, kind string, context int, diffs []difflib.DiffRecord, to io.Writer) { diffs := difflib.Diff(strings.Split(before, "\n"), strings.Split(after, "\n")) for _, ckind := range suppressedKinds { diff --git a/main.go b/main.go index 42e4da20..1f56f4cc 100644 --- a/main.go +++ b/main.go @@ -2,12 +2,14 @@ package main import ( "os" + "fmt" "github.com/databus23/helm-diff/cmd" ) func main() { if err := cmd.New().Execute(); err != nil { + fmt.Println(err) os.Exit(1) } }