Skip to content

Commit

Permalink
allow checking for value changes before bumping helm chart version
Browse files Browse the repository at this point in the history
Add flag `--check-for-value-updates` to `arkade chart bump` which skips
bumping the chart version if the values file does not have any changes
according to the Git.

Signed-off-by: Sanskar Jaiswal <[email protected]>
  • Loading branch information
aryan9600 committed Jan 25, 2024
1 parent 4e0c130 commit cbd2998
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 72 deletions.
16 changes: 7 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,22 +289,20 @@ If you just need system applications, you could also try "setup-arkade":
To bump the minor of your Helm chart version, run `arkade chart bump --dir ./chart`. This updates the minor component of the version specified in Chart.yaml.

```bash
arkade chart bump --dir ~/Development/fluxcd/flagger/charts/flagger
/Users/sanskarjaiswal/Development/fluxcd/flagger/charts/flagger/Chart.yaml 1.35.0 => 1.36.0
arkade chart bump --dir ./charts/flagger
charts/flagger/Chart.yaml 1.36.0 => 1.37.0
```

By default, the new version is written to stdout. To bump the version in the file, run the above command with the `--write` flag.
If you have a parent directory containing multiple charts, you can bump all charts by specifying the `--recursive` flag.
To bump the version in the chart's Chart.yaml only if the adjacent values file has any changes, specify the `--check-for-value-updates` flag:

```bash
❯ ./arkade chart bump --dir ~/Development/fluxcd/flagger/charts --recursive
Found 4 charts
/Users/sanskarjaiswal/Development/fluxcd/flagger/charts/flagger/Chart.yaml 1.35.0 => 1.36.0
/Users/sanskarjaiswal/Development/fluxcd/flagger/charts/grafana/Chart.yaml 1.7.0 => 1.8.0
/Users/sanskarjaiswal/Development/fluxcd/flagger/charts/loadtester/Chart.yaml 0.30.0 => 0.31.0
/Users/sanskarjaiswal/Development/fluxcd/flagger/charts/podinfo/Chart.yaml 6.1.3 => 6.2.0
arkade chart bump --dir ./charts/flagger/ --check-for-value-updates values.yaml
no changes detected in charts/flagger/values.yaml; skipping version bump
```

The directory that contains the Helm chart should be a Git repository. If the flag is specified, the command runs `git diff --exit-code <file>` to figure out if the file has any changes.

## Verify and upgrade images in Helm charts

There are two commands built into arkade designed for software vendors and open source maintainers.
Expand Down
135 changes: 72 additions & 63 deletions cmd/chart/bump.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package chart

import (
"context"
"fmt"
"io/fs"
"log"
"os"
"path/filepath"

"github.com/Masterminds/semver"
"github.com/alexellis/arkade/pkg/helm"
"github.com/alexellis/go-execute/v2"
"github.com/spf13/cobra"
)

Expand All @@ -21,19 +22,21 @@ const (
func MakeBump() *cobra.Command {
var command = &cobra.Command{
Use: "bump",
Short: "Bump the version of the Helm chart(s)",
Short: "Bump the version of the Helm chart.",
Long: `Bump the version present in the Chart.yaml of a Helm chart.
If the provided directory contains multiple charts, then the --recursive flag
can be used to bump the version in all charts.`,
Example: `arkade bump --dir ./chart
arkade --dir ./charts --recursive`,
To bump the version only if the adjacent values file has changes then specify
the --check-for-value-updates flag. If the values file has no changes the command
returns early with an exit code zero.
`,
Example: `arkade chart bump --dir ./chart
arkade chart bump --dir ./charts --check-for-value-updates values.yaml`,
SilenceUsage: true,
}

command.Flags().StringP("dir", "d", "", "Path to the Helm chart directory or a directory containing Helm charts")
command.Flags().BoolP("recursive", "r", false, "Recursively iterate through directory while bumping chart versions")
command.Flags().BoolP("verbose", "v", false, "Verbose output")
command.Flags().BoolP("write", "w", false, "Write the updated values back to the file, or stdout when set to false")
command.Flags().String("check-for-value-updates", "", "Name of the values file to check if the chart's values have been modified before bumping version")

command.RunE = func(cmd *cobra.Command, args []string) error {
chartDir, err := cmd.Flags().GetString("dir")
Expand All @@ -44,80 +47,86 @@ can be used to bump the version in all charts.`,
return fmt.Errorf("flag --dir is required")
}
verbose, _ := cmd.Flags().GetBool("verbose")
recursive, err := cmd.Flags().GetBool("recursive")
if err != nil {
return fmt.Errorf("invalid value for --recursive")
}
write, err := cmd.Flags().GetBool("write")
if err != nil {
return fmt.Errorf("invalid value for --write")
}
valuesFile, err := cmd.Flags().GetString("check-for-value-updates")
if err != nil {
return fmt.Errorf("invalid value for --check-for-value-updates")
}

// Map with key as the path to Chart.yaml and the value as the parsed contents of Chart.yaml
chartYamls := make(map[string]helm.ValuesMap, 0)
if !recursive {
chartYamlPath := filepath.Join(chartDir, ChartYamlFileName)
var values helm.ValuesMap
// Try to read a Chart.yaml, but if thats unsuccessful then fall back to Chart.yml
chartYamlPath := filepath.Join(chartDir, ChartYamlFileName)
var values helm.ValuesMap
// Try to read a Chart.yaml, but if thats unsuccessful then fall back to Chart.yml
if values, err = helm.Load(chartYamlPath); err != nil {
if verbose {
log.Printf("unable to read %s, falling back to Chart.yml\n", chartYamlPath)
}
chartYamlPath = filepath.Join(chartDir, ChartYmlFileName)
if values, err = helm.Load(chartYamlPath); err != nil {
if verbose {
log.Printf("unable to read %s, falling back to Chart.yml\n", chartYamlPath)
}
chartYamlPath = filepath.Join(chartDir, ChartYmlFileName)
if values, err = helm.Load(chartYamlPath); err != nil {
return fmt.Errorf("unable to read Chart.yaml or Chart.yml in directory %s", chartDir)
}
return fmt.Errorf("unable to read Chart.yaml or Chart.yml in directory %s", chartDir)
}
chartYamls[chartYamlPath] = values
}

// If the yaml does not contain a `version` key then error out.
if val, ok := values[versionKey]; !ok {
return fmt.Errorf("unable to find a version in %s", chartYamlPath)
} else {
filepath.WalkDir(chartDir, func(path string, d fs.DirEntry, err error) error {
version, ok := val.(string)
if !ok {
log.Printf("unable to find a valid version in %s", chartYamlPath)
}
if valuesFile != "" {
absPath, err := filepath.Abs(chartDir)
if err != nil {
return err
}
if d.Name() == ChartYamlFileName || d.Name() == ChartYmlFileName {
values, err := helm.Load(path)
if err != nil {
return err
}
chartYamls[path] = values
absValuesFile := filepath.Join(absPath, valuesFile)
_, err = os.Stat(absValuesFile)
if err != nil {
return fmt.Errorf("unable to find values file: %s", absValuesFile)
}

// Run `git diff --exit-code <file>` to check if the values file has any changes.
// An exit code of 0 indicates that there are no changes, thus we skip bumping the
// version of the chart.
cmd := execute.ExecTask{
Command: "git",
Args: []string{"diff", "--exit-code", valuesFile},
Cwd: absPath,
}
res, err := cmd.Execute(context.Background())
if err != nil {
return fmt.Errorf("could not check updates to chart values: %s", err)
}

if res.ExitCode == 0 {
fmt.Printf("no changes detected in %s; skipping version bump\n", filepath.Join(chartDir, valuesFile))
os.Exit(0)
}
return nil
})
if len(chartYamls) > 0 {
fmt.Printf("Found %d chart(s)\n", len(chartYamls))
}
}

for file, contents := range chartYamls {
// If the yaml does not contain a `version` key then skip it.
if val, ok := contents[versionKey]; !ok {
continue
} else {
version, ok := val.(string)
if !ok {
log.Printf("unable to find a valid version in %s", file)
continue
ver, err := semver.NewVersion(version)
if err != nil {
return fmt.Errorf("%s", err)
}
newVer := ver.IncMinor()
fmt.Printf("%s %s => %s\n", chartYamlPath, ver.String(), newVer.String())
if write {
if verbose {
log.Printf("Bumping version")
}
update := map[string]string{
fmt.Sprintf("%s: %s", versionKey, ver.String()): fmt.Sprintf("%s: %s", versionKey, newVer.String()),
}
ver, err := semver.NewVersion(version)
rawChartYaml, err := helm.ReplaceValuesInHelmValuesFile(update, chartYamlPath)
if err != nil {
continue
return fmt.Errorf("unable to bump chart version in %s", chartYamlPath)
}
newVer := ver.IncMinor()
fmt.Printf("%s %s => %s\n", file, ver.String(), newVer.String())
if write {
if verbose {
log.Printf("Bumping version")
}
update := map[string]string{
fmt.Sprintf("%s: %s", versionKey, ver.String()): fmt.Sprintf("%s: %s", versionKey, newVer.String()),
}
rawChartYaml, err := helm.ReplaceValuesInHelmValuesFile(update, file)
if err != nil {
return fmt.Errorf("unable to bump chart version in %s", file)
}
if err = os.WriteFile(file, []byte(rawChartYaml), 0600); err != nil {
return fmt.Errorf("unable to write updated yaml to %s", file)
}
if err = os.WriteFile(chartYamlPath, []byte(rawChartYaml), 0600); err != nil {
return fmt.Errorf("unable to write updated yaml to %s", chartYamlPath)
}
}
}
Expand Down

0 comments on commit cbd2998

Please sign in to comment.