Repository: helm/chart-testing (go)

Issue:
Feature: ignore changes in files like README.md?
**Is this a request for help?**: No.

---

**Is this a BUG REPORT or FEATURE REQUEST?** (choose one): FEATURE REQUEST

I am using the helm-chart-testing action, that uses the chart-testing tool (obviously :-) ). It complains, if I make small changes to e.g. a chart's README.md file, and do not bump the chart's version.

For things like README.md or similar it would be nice, if those files could be ignored by `ct list-changed`.

I found no commandline argument that would allow me to do that. I also found no hint if this is possible using the config file.

Pull request:
use .helmignore when identifying changed charts
<!--
Thank you for contributing to helm/chart-testing.

When updates to your PR are requested, please add new commits and do not squash the
history. This will make it easier to identify new changes. The PR will be squashed
anyways when it is merged. Thanks.

Please make sure you test your changes before you push them. Once pushed, a CircleCI
will run across your changes and do some initial checks and linting. These checks run
very quickly. Please check the results. We would like these checks to pass before we
even continue reviewing your changes.
-->

**What this PR does / why we need it**:

use .helmignore when identifying changed charts.

The use of .helmignore is disabled by default (current behaviour) and enabled with `--use-helmignore`

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #530

**Special notes for your reviewer**:

the FilterFiles function is adapted from https://github.com/helm/helm/blob/main/pkg/chart/loader/directory.go#L46

Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index aa5375b..40d017e 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -12,7 +12,7 @@ jobs:
     steps:
 
       - name: checkout
-        uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+        uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
         with:
           fetch-depth: 0
 
@@ -20,7 +20,7 @@ jobs:
         uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0
 
       - name: Setup go
-        uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+        uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
         with:
           go-version-file: './go.mod'
           check-latest: true
@@ -30,7 +30,7 @@ jobs:
           version: v3.14.4
 
       - name: Install GoReleaser
-        uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0
+        uses: goreleaser/goreleaser-action@5742e2a039330cbb23ebf35f046f814d4c6ff811 # v5.1.0
         with:
           install-only: true
 
@@ -38,10 +38,10 @@ jobs:
         uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
 
       - name: Install syft
-        uses: anchore/sbom-action/download-syft@ab5d7b5f48981941c4c5d6bf33aeb98fe3bae38c # v0.15.10
+        uses: anchore/sbom-action/download-syft@e8d2a6937ecead383dfe75190d104edd1f9c5751 # v0.16.0
 
       - name: Install k8s Kind
-        uses: helm/kind-action@99576bfa6ddf9a8e612d83b513da5a75875caced # v1.3.0
+        uses: helm/kind-action@0025e74a8c7512023d06dc019c617aa3cf561fde # v1.3.0
         with:
           install_only: true
 
@@ -82,8 +82,8 @@ jobs:
     name: check-docs
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
-      - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+      - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
+      - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
         with:
           go-version-file: './go.mod'
           check-latest: true
@@ -103,12 +103,12 @@ jobs:
     name: lint
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
-      - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+      - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
+      - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
         with:
           go-version-file: './go.mod'
           check-latest: true
       - name: golangci-lint
-        uses: golangci/golangci-lint-action@3cfe3a4abbb849e10058ce4af15d205b6da42804 # v3
+        uses: golangci/golangci-lint-action@a4f60bb28d35aeee14e6880718e0c85ff1882e64 # v6.0.1
         with:
           version: v1.57
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 0b66796..5da5211 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -17,7 +17,7 @@ jobs:
     steps:
 
       - name: checkout
-        uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+        uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
         with:
           fetch-depth: 0
 
@@ -25,13 +25,13 @@ jobs:
         uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0
 
       - name: Setup go
-        uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+        uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
         with:
           go-version-file: './go.mod'
           check-latest: true
 
       - name: Install GoReleaser
-        uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0
+        uses: goreleaser/goreleaser-action@5742e2a039330cbb23ebf35f046f814d4c6ff811 # v5.1.0
         with:
           install-only: true
 
@@ -39,7 +39,7 @@ jobs:
         uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
 
       - name: Install syft
-        uses: anchore/sbom-action/download-syft@ab5d7b5f48981941c4c5d6bf33aeb98fe3bae38c # v0.15.10
+        uses: anchore/sbom-action/download-syft@e8d2a6937ecead383dfe75190d104edd1f9c5751 # v0.16.0
 
       - name: Install tools
         run: |
diff --git a/ct/cmd/root.go b/ct/cmd/root.go
index 16b4048..3afb262 100644
--- a/ct/cmd/root.go
+++ b/ct/cmd/root.go
@@ -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) {
diff --git a/doc/ct_install.md b/doc/ct_install.md
index 355a14c..fe1e72d 100644
--- a/doc/ct_install.md
+++ b/doc/ct_install.md
@@ -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
diff --git a/doc/ct_lint-and-install.md b/doc/ct_lint-and-install.md
index 765448f..215a70f 100644
--- a/doc/ct_lint-and-install.md
+++ b/doc/ct_lint-and-install.md
@@ -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)
diff --git a/doc/ct_lint.md b/doc/ct_lint.md
index 5fb4ef6..a08644a 100644
--- a/doc/ct_lint.md
+++ b/doc/ct_lint.md
@@ -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)
diff --git a/doc/ct_list-changed.md b/doc/ct_list-changed.md
index 5080a62..109f6c4 100644
--- a/doc/ct_list-changed.md
+++ b/doc/ct_list-changed.md
@@ -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
diff --git a/go.mod b/go.mod
index 24542c4..a72f62f 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/MakeNowJust/heredoc v1.0.0
 	github.com/Masterminds/semver v1.5.0
 	github.com/hashicorp/go-multierror v1.1.1
-	github.com/hashicorp/go-retryablehttp v0.7.5
+	github.com/hashicorp/go-retryablehttp v0.7.6
 	github.com/mattn/go-shellwords v1.0.12
 	github.com/mitchellh/go-homedir v1.1.0
 	github.com/spf13/cobra v1.8.0
@@ -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 (
@@ -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
@@ -39,7 +41,7 @@ require (
 	go.uber.org/atomic v1.9.0 // indirect
 	go.uber.org/multierr v1.9.0 // indirect
 	golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
-	golang.org/x/sys v0.15.0 // indirect
+	golang.org/x/sys v0.20.0 // indirect
 	golang.org/x/text v0.14.0 // indirect
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
diff --git a/go.sum b/go.sum
index 6992ee6..12cffbb 100644
--- a/go.sum
+++ b/go.sum
@@ -8,26 +8,25 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
-github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
+github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
+github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
 github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
 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=
 github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
 github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
-github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
-github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
-github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
+github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
+github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
 github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
 github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
-github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
+github.com/hashicorp/go-retryablehttp v0.7.6 h1:TwRYfx2z2C4cLbXmT8I5PgP/xmuqASDyiVuGYfs9GZM=
+github.com/hashicorp/go-retryablehttp v0.7.6/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -43,8 +42,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
 github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
-github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
 github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@@ -53,6 +52,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=
@@ -81,7 +82,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
 github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@@ -96,8 +96,8 @@ go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
 go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
 golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -110,3 +110,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=
diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go
index 4e2f3e1..4790b82 100644
--- a/pkg/chart/chart.go
+++ b/pkg/chart/chart.go
@@ -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"
 )
@@ -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
@@ -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()
@@ -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]) {
@@ -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
 }
 
diff --git a/pkg/config/config.go b/pkg/config/config.go
index c6b8287..989c467 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -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) {
diff --git a/pkg/ignore/ignore.go b/pkg/ignore/ignore.go
new file mode 100644
index 0000000..99d19c2
--- /dev/null
+++ b/pkg/ignore/ignore.go
@@ -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
+}


Test patch:
diff --git a/pkg/chart/chart_test.go b/pkg/chart/chart_test.go
index a45ff82..678bb80 100644
--- a/pkg/chart/chart_test.go
+++ b/pkg/chart/chart_test.go
@@ -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{}
@@ -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
+		},
 	}
 }
 
@@ -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"},
@@ -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{
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index 80b33f0..f07d912 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -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) {
diff --git a/pkg/config/test_config.json b/pkg/config/test_config.json
index 4dc445a..73cd457 100644
--- a/pkg/config/test_config.json
+++ b/pkg/config/test_config.json
@@ -32,5 +32,6 @@
     "release-label": "release",
     "exclude-deprecated": true,
     "kubectl-timeout": "120s",
-    "skip-clean-up": true
+    "skip-clean-up": true,
+    "use-helmignore": true
 }
diff --git a/pkg/config/test_config.yaml b/pkg/config/test_config.yaml
index 6fe391d..0d1c7c0 100644
--- a/pkg/config/test_config.yaml
+++ b/pkg/config/test_config.yaml
@@ -28,3 +28,4 @@ release-label: release
 exclude-deprecated: true
 kubectl-timeout: 120s
 skip-clean-up: true
+use-helmignore: true
diff --git a/pkg/ignore/ignore_test.go b/pkg/ignore/ignore_test.go
new file mode 100644
index 0000000..4976b1f
--- /dev/null
+++ b/pkg/ignore/ignore_test.go
@@ -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
+
+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)
+}