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

use .helmignore when identifying changed charts #591

Merged
merged 2 commits into from
May 22, 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
1 change: 1 addition & 0 deletions ct/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func addCommonFlags(flags *pflag.FlagSet) {
flags.Bool("github-groups", false, heredoc.Doc(`
Change the delimiters for github to create collapsible groups
for command output`))
flags.Bool("use-helmignore", false, "Use .helmignore when identifying changed charts")
}

func addCommonLintAndInstallFlags(flags *pflag.FlagSet) {
Expand Down
1 change: 1 addition & 0 deletions doc/ct_install.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ ct install [flags]
--target-branch string The name of the target branch used to identify changed charts (default "main")
--upgrade Whether to test an in-place upgrade of each chart from its previous revision if the
current version should not introduce a breaking change according to the SemVer spec
--use-helmignore Use .helmignore when identifying changed charts
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions doc/ct_lint-and-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ ct lint-and-install [flags]
--target-branch string The name of the target branch used to identify changed charts (default "main")
--upgrade Whether to test an in-place upgrade of each chart from its previous revision if the
current version should not introduce a breaking change according to the SemVer spec
--use-helmignore Use .helmignore when identifying changed charts
--validate-chart-schema Enable schema validation of 'Chart.yaml' using Yamale (default true)
--validate-maintainers Enable validation of maintainer account names in chart.yml.
Works for GitHub, GitLab, and Bitbucket (default true)
Expand Down
1 change: 1 addition & 0 deletions doc/ct_lint.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ ct lint [flags]
--remote string The name of the Git remote used to identify changed charts (default "origin")
--since string The Git reference used to identify changed charts (default "HEAD")
--target-branch string The name of the target branch used to identify changed charts (default "main")
--use-helmignore Use .helmignore when identifying changed charts
--validate-chart-schema Enable schema validation of 'Chart.yaml' using Yamale (default true)
--validate-maintainers Enable validation of maintainer account names in chart.yml.
Works for GitHub, GitLab, and Bitbucket (default true)
Expand Down
1 change: 1 addition & 0 deletions doc/ct_list-changed.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ ct list-changed [flags]
--remote string The name of the Git remote used to identify changed charts (default "origin")
--since string The Git reference used to identify changed charts (default "HEAD")
--target-branch string The name of the target branch used to identify changed charts (default "main")
--use-helmignore Use .helmignore when identifying changed charts
```

### SEE ALSO
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.9.0
gopkg.in/yaml.v2 v2.4.0
helm.sh/helm/v3 v3.14.4
)

require (
Expand All @@ -27,6 +28,7 @@ require (
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
Expand Down
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
Expand Down Expand Up @@ -53,6 +53,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down Expand Up @@ -110,3 +112,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
helm.sh/helm/v3 v3.14.4 h1:6FSpEfqyDalHq3kUr4gOMThhgY55kXUEjdQoyODYnrM=
helm.sh/helm/v3 v3.14.4/go.mod h1:Tje7LL4gprZpuBNTbG34d1Xn5NmRT3OWfBRwpOSer9I=
32 changes: 27 additions & 5 deletions pkg/chart/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ import (
"strings"

"github.com/Masterminds/semver"
helmignore "helm.sh/helm/v3/pkg/ignore"

"github.com/helm/chart-testing/v3/pkg/config"
"github.com/helm/chart-testing/v3/pkg/exec"
"github.com/helm/chart-testing/v3/pkg/ignore"
"github.com/helm/chart-testing/v3/pkg/tool"
"github.com/helm/chart-testing/v3/pkg/util"
)
Expand Down Expand Up @@ -242,6 +244,7 @@ type Testing struct {
directoryLister DirectoryLister
utils Utils
previousRevisionWorktree string
loadRules func(string) (*helmignore.Rules, error)
}

// TestResults holds results and overall status
Expand Down Expand Up @@ -272,6 +275,7 @@ func NewTesting(config config.Configuration, extraSetArgs string) (Testing, erro
accountValidator: tool.AccountValidator{},
directoryLister: util.DirectoryLister{},
utils: util.Utils{},
loadRules: ignore.LoadRules,
}

versionString, err := testing.helm.Version()
Expand Down Expand Up @@ -746,7 +750,7 @@ func (t *Testing) ComputeChangedChartDirectories() ([]string, error) {
return nil, fmt.Errorf("failed creating diff: %w", err)
}

var changedChartDirs []string
changedChartFiles := map[string][]string{}
for _, file := range allChangedChartFiles {
pathElements := strings.SplitN(filepath.ToSlash(file), "/", 3)
if len(pathElements) < 2 || util.StringSliceContains(cfg.ExcludedCharts, pathElements[1]) {
Expand All @@ -763,15 +767,33 @@ func (t *Testing) ComputeChangedChartDirectories() ([]string, error) {
continue
}
}
// Only add it if not already in the list
if !util.StringSliceContains(changedChartDirs, chartDir) {
changedChartDirs = append(changedChartDirs, chartDir)
}
changedChartFiles[chartDir] = append(changedChartFiles[chartDir], strings.TrimPrefix(file, chartDir+"/"))
} else {
fmt.Fprintf(os.Stderr, "Directory %q is not a valid chart directory. Skipping...\n", dir)
}
}

changedChartDirs := []string{}
if t.config.UseHelmignore {
for chartDir, changedChartFiles := range changedChartFiles {
rules, err := t.loadRules(chartDir)
if err != nil {
return nil, err
}
filteredChartFiles, err := ignore.FilterFiles(changedChartFiles, rules)
if err != nil {
return nil, err
}
if len(filteredChartFiles) > 0 {
changedChartDirs = append(changedChartDirs, chartDir)
}
}
} else {
for chartDir := range changedChartFiles {
changedChartDirs = append(changedChartDirs, chartDir)
}
}

return changedChartDirs, nil
}

Expand Down
47 changes: 47 additions & 0 deletions pkg/chart/chart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/helm/chart-testing/v3/pkg/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
helmignore "helm.sh/helm/v3/pkg/ignore"
)

type fakeGit struct{}
Expand Down Expand Up @@ -152,6 +153,26 @@ func newTestingMock(cfg config.Configuration) Testing {
accountValidator: fakeAccountValidator{},
linter: fakeMockLinter,
helm: new(fakeHelm),
loadRules: func(dir string) (*helmignore.Rules, error) {
rules := helmignore.Empty()
if dir == "test_charts/foo" {
var err error
rules, err = helmignore.Parse(strings.NewReader("Chart.yaml\n"))
if err != nil {
return nil, err
}
rules.AddDefaults()
}
if dir == "test_chart_at_multi_level/foo/baz" {
var err error
rules, err = helmignore.Parse(strings.NewReader("Chart.yaml\n"))
if err != nil {
return nil, err
}
rules.AddDefaults()
}
return rules, nil
},
}
}

Expand All @@ -165,6 +186,19 @@ func TestComputeChangedChartDirectories(t *testing.T) {
assert.Nil(t, err)
}

func TestComputeChangedChartDirectoriesWithHelmignore(t *testing.T) {
cfg := config.Configuration{
ExcludedCharts: []string{"excluded"},
ChartDirs: []string{"test_charts", "."},
UseHelmignore: true,
}
ct := newTestingMock(cfg)
actual, err := ct.ComputeChangedChartDirectories()
expected := []string{"test_charts/bar", "test_chart_at_root"}
assert.Nil(t, err)
assert.ElementsMatch(t, expected, actual)
}

func TestComputeChangedChartDirectoriesWithMultiLevelChart(t *testing.T) {
cfg := config.Configuration{
ExcludedCharts: []string{"excluded"},
Expand All @@ -180,6 +214,19 @@ func TestComputeChangedChartDirectoriesWithMultiLevelChart(t *testing.T) {
assert.Nil(t, err)
}

func TestComputeChangedChartDirectoriesWithMultiLevelChartWithHelmIgnore(t *testing.T) {
cfg := config.Configuration{
ExcludedCharts: []string{"excluded"},
ChartDirs: []string{"test_chart_at_multi_level/foo"},
UseHelmignore: true,
}
ct := newTestingMock(cfg)
actual, err := ct.ComputeChangedChartDirectories()
expected := []string{"test_chart_at_multi_level/foo/bar"}
assert.Nil(t, err)
assert.ElementsMatch(t, expected, actual)
}

func TestReadAllChartDirectories(t *testing.T) {
actual, err := ct.ReadAllChartDirectories()
expected := []string{
Expand Down
1 change: 1 addition & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type Configuration struct {
KubectlTimeout time.Duration `mapstructure:"kubectl-timeout"`
PrintLogs bool `mapstructure:"print-logs"`
GithubGroups bool `mapstructure:"github-groups"`
UseHelmignore bool `mapstructure:"use-helmignore"`
}

func LoadConfiguration(cfgFile string, cmd *cobra.Command, printConfig bool) (*Configuration, error) {
Expand Down
1 change: 1 addition & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func loadAndAssertConfigFromFile(t *testing.T, configFile string) {
require.Equal(t, true, cfg.ExcludeDeprecated)
require.Equal(t, 120*time.Second, cfg.KubectlTimeout)
require.Equal(t, true, cfg.SkipCleanUp)
require.Equal(t, true, cfg.UseHelmignore)
}

func Test_findConfigFile(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion pkg/config/test_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@
"release-label": "release",
"exclude-deprecated": true,
"kubectl-timeout": "120s",
"skip-clean-up": true
"skip-clean-up": true,
"use-helmignore": true
}
1 change: 1 addition & 0 deletions pkg/config/test_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ release-label: release
exclude-deprecated: true
kubectl-timeout: 120s
skip-clean-up: true
use-helmignore: true
83 changes: 83 additions & 0 deletions pkg/ignore/ignore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package ignore

import (
"io/fs"
"os"
"path/filepath"
"testing/fstest"

helmignore "helm.sh/helm/v3/pkg/ignore"
)

func LoadRules(dir string) (*helmignore.Rules, error) {
rules, err := helmignore.ParseFile(filepath.Join(dir, helmignore.HelmIgnore))
if err != nil && !os.IsNotExist(err) {
return nil, err
}
if rules == nil {
rules = helmignore.Empty()
}
rules.AddDefaults()
return rules, nil
}

func FilterFiles(files []string, rules *helmignore.Rules) ([]string, error) {
fsys := fstest.MapFS{}
for _, file := range files {
fsys[file] = &fstest.MapFile{}
}

filteredFiles := []string{}

err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}

fi, err := d.Info()
if err != nil {
return err
}

// Normalize to / since it will also work on Windows
path = filepath.ToSlash(path)

if fi.IsDir() {
// Directory-based ignore rules should involve skipping the entire
// contents of that directory.
if rules.Ignore(path, fi) {
return filepath.SkipDir
}
return nil
}

// If a .helmignore file matches, skip this file.
if rules.Ignore(path, fi) {
return nil
}

filteredFiles = append(filteredFiles, path)
return nil
})
if err != nil {
return nil, err
}

return filteredFiles, nil
}
35 changes: 35 additions & 0 deletions pkg/ignore/ignore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
Copyright The Helm Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package ignore
jouve marked this conversation as resolved.
Show resolved Hide resolved

import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
helmignore "helm.sh/helm/v3/pkg/ignore"
)

func TestFilter(t *testing.T) {
rules, err := helmignore.Parse(strings.NewReader("/bar/\nREADME.md\n"))
assert.Nil(t, err)
files := []string{"Chart.yaml", "bar/xxx", "template/svc.yaml", "baz/bar/biz.txt", "README.md"}
actual, err := FilterFiles(files, rules)
assert.Nil(t, err)
expected := []string{"Chart.yaml", "baz/bar/biz.txt", "template/svc.yaml"}
assert.ElementsMatch(t, expected, actual)
}