Skip to content

Commit

Permalink
helm: add arkade chart bump to bump chart versions
Browse files Browse the repository at this point in the history
Update patch version of charts automatically, whenever there
are adjacent files changed in git for the folder next to
Chart.yaml.

Signed-off-by: Sanskar Jaiswal <[email protected]>
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <[email protected]>
  • Loading branch information
aryan9600 authored and alexellis committed Feb 16, 2024
1 parent 205dbac commit 39ffd8b
Show file tree
Hide file tree
Showing 5 changed files with 3,110 additions and 6 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ With over 120 CLIs and 55 Kubernetes apps (charts, manifests, installers) availa
- [Install System Packages](#install-system-packages)
- [Install packages from OCI images](#install-packages-from-oci-images)
- [Install CLIs during CI with GitHub Actions](#install-clis-during-ci-with-github-actions)
- [Bump Helm chart versions](#bump-helm-chart-versions)
- [Verify and upgrade images in Helm charts](#verify-and-upgrade-images-in-helm-charts)
- [Upgrade images within a Helm chart](#upgrade-images-within-a-helm-chart)
- [Verify images within a helm chart](#verify-images-within-a-helm-chart)
Expand Down Expand Up @@ -299,6 +300,25 @@ If you just need system applications, you could also try "setup-arkade":
arkade system install go
```
## Bump Helm chart versions
To bump the patch version of your Helm chart, run `arkade chart bump -f ./chart/values.yaml`. This updates the patch component of the version specified in Chart.yaml.

```bash
arkade chart bump -f ./charts/flagger/values.yaml
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.
To bump the version in the chart's Chart.yaml only if the chart has any changes, specify the `--check-for-updates` flag:

```bash
arkade chart bump -f ./charts/flagger/values.yaml --check-for-updates
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
147 changes: 147 additions & 0 deletions cmd/chart/bump.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package chart

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

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

const (
versionKey = "version"
ChartYamlFileName = "Chart.yaml"
ChartYmlFileName = "Chart.yml"
)

func MakeBump() *cobra.Command {
var command = &cobra.Command{
Use: "bump",
Short: "Bump the patch version of the Helm chart.",
Long: `Bump the version present in the Chart.yaml of a Helm chart
if there are changes staged in git.
Add --force to bump the version even if there are not changes staged in git.
`,
Example: ` # Bump if there are changes
arkade chart bump -f ./chart/values.yaml
# Force a bump, even if there are no changes
arkade chart bump -f ./charts/values.yaml --check-for-updates`,
SilenceUsage: true,
}

command.Flags().StringP("file", "f", "", "Path to values.yaml file")
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().Bool("force", false, "Update the version even if there are no changes staged in the adjacent folder to Chart.yaml")

command.RunE = func(cmd *cobra.Command, args []string) error {
valuesFile, err := cmd.Flags().GetString("file")
if err != nil {
return fmt.Errorf("invalid value for --file")
}
if valuesFile == "" {
return fmt.Errorf("flag --file is required")
}
verbose, err := cmd.Flags().GetBool("verbose")
if err != nil {
return err
}

write, err := cmd.Flags().GetBool("write")
if err != nil {
return err
}
force, err := cmd.Flags().GetBool("force")
if err != nil {
return err
}

chartDir := filepath.Dir(valuesFile)
chartYamlPath := filepath.Join(chartDir, ChartYamlFileName)

// Map with key as the path to Chart.yaml and the value as the parsed contents of Chart.yaml
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 {
return fmt.Errorf("unable to read Chart.yaml or Chart.yml in directory %s", chartDir)
}
}

// 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 {
version, ok := val.(string)
if !ok {
log.Printf("unable to find a valid version in %s", chartYamlPath)
}
if !force {
absPath, err := filepath.Abs(chartDir)
if err != nil {
return err
}

// Run `git diff --exit-code <file>` to check if any files in the chart dir changed.
// 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", "."},
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 {
if verbose {
fmt.Printf("No changes in: %s, ignoring.\n", chartDir)
}
os.Exit(0)
}
}

ver, err := semver.NewVersion(version)
if err != nil {
return fmt.Errorf("%s", err)
}

newVer := ver.IncPatch()

fmt.Printf("%s %s => %s\n", chartYamlPath, ver.String(), newVer.String())

if write {

update := map[string]string{
fmt.Sprintf("%s: %s", versionKey, ver.String()): fmt.Sprintf("%s: %s", versionKey, newVer.String()),
}
rawChartYaml, err := helm.ReplaceValuesInHelmValuesFile(update, chartYamlPath)
if err != nil {
return fmt.Errorf("unable to bump chart version in %s", chartYamlPath)
}
if err = os.WriteFile(chartYamlPath, []byte(rawChartYaml), 0600); err != nil {
return fmt.Errorf("unable to write updated yaml to %s", chartYamlPath)
}
fmt.Printf("Wrote to: %s. OK.\n", chartYamlPath)
} else {
fmt.Printf("Not updating: %s (--write=false)\n", chartYamlPath)
}

}
return nil
}
return command
}
1 change: 1 addition & 0 deletions cmd/chart/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func MakeChart() *cobra.Command {

command.AddCommand(MakeVerify())
command.AddCommand(MakeUpgrade())
command.AddCommand(MakeBump())

return command
}
8 changes: 2 additions & 6 deletions cmd/chart/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ Otherwise, it returns a non-zero exit code and the updated values.yaml file.`,

latestTag := vs[0].String()

if latestTag != tag {
// AE: Don't upgrade to an RC tag, even if it's newer.
if latestTag != tag && !strings.Contains(latestTag, "-rc") {
updated++
// Semver is "eating" the "v" prefix, so we need to add it back, if it was there in first place
if strings.HasPrefix(tag, "v") {
Expand All @@ -129,11 +130,6 @@ Otherwise, it returns a non-zero exit code and the updated values.yaml file.`,
log.Printf("Wrote %d updates to: %s", updated, file)
}

if !writeFile {
// Output updated YAML file to stdout
fmt.Print(rawValues)
}

return nil
}

Expand Down
Loading

0 comments on commit 39ffd8b

Please sign in to comment.