Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

helm: add arkade chart bump to bump chart versions #1030

Merged
merged 1 commit into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ With over 120 CLIs and 55 Kubernetes apps (charts, manifests, installers) availa
- [Download CLI tools with arkade](#download-cli-tools-with-arkade)
- [Install System Packages](#install-system-packages)
- [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 @@ -283,6 +284,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
Loading