Helm is a popular open-source package manager for Kubernetes that simplifies the process of installing, upgrading, and managing applications on a Kubernetes cluster. Helm packages, called charts, contain all the necessary resources and configuration for deploying an application on a Kubernetes cluster.
As with any software project, it’s important to test charts before deploying them to ensure that they are reliable and function as intended. Chart testing is the process of verifying the functionality and correctness of a Helm chart before it is deployed to a Kubernetes cluster.
There are a variety of tools and approaches available for testing Helm charts, ranging from simple shell scripts to more advanced testing frameworks. In this blog post, we’ll explore how to use the chart-testing tool, which is maintained by the Helm project, to test Helm charts with GitHub Actions.
Helm Chart Testing with Pull Requests
- Lets create a new file in the
.github/workflowsdirectory of your repository calledchart-testing.yml. This file will define the workflow and specify the steps required to run the chart tests.
Update the chart-testing.yml file with below contents :
name: Lint and Test Charts
on: pull_request
jobs:
lint-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Helm
uses: azure/setup-helm@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.7
- name: Set up chart-testing
uses: helm/chart-testing-action@v2.3.1
- name: Run chart-testing (list-changed)
id: list-changed
run: |
changed=$(ct list-changed --config ct.yaml)
if [[ -n "$changed" ]]; then
echo "{changed}={true}" >> $GITHUB_OUTPUT
fi
- name: Run chart-testing (lint)
run: ct lint --config ct.yaml
- name: Create kind cluster
uses: helm/kind-action@v1.5.0
if: ${{ needs.list-changed.outputs.changed }} == 'true'
- name: Run chart-testing (install)
run: ct install --config ct.yaml
Let’s understand the above config in detail.
on: pull_request: This indicates when the workflow should trigger, in our case whenever a pull request is opened.Checkout: This step uses theactions/checkout@v3action to check out (clone) the code in the repository so that the workflow can access it. Thefetch-depthparameter is set to0to fetch the entire repository history i.e all history for all branches and tags.Set up Helm: This step uses theazure/setup-helm@v3action to install and set up Helm v3 latest on the machine.Set up Python: This step uses theactions/setup-python@v4action to install and set up Python on the machine. Thepython-versionparameter is set to3.7.Set up chart-testing: This step uses thehelm/chart-testing-action@v2.3.1action to install and set up chart-testing, a tool for testing Helm charts.Run chart-testing (list-changed): This step runs thect list-changedcommand using chart-testing to list the charts that have changed since the last commit by referring to themaintarget branch. Theidfield is used to give this step an identifier, which can be used to reference it later in the workflow execution. If there are changed charts, the step sets an output namedchangedtotrueusing theecho "{changed}={true}" >> $GITHUB_OUTPUTsyntax.Run chart-testing (lint): This step runs thect lintcommand using chart-testing to lint the charts in the repository.Create kind cluster: This step uses thehelm/kind-action@v1.5.0action to create a Kubernetes cluster using kind (Kubernetes IN Docker). Theiffield is used to specify that this step should only be run if thechangedoutput of thelist-changedstep istrue. I.e this step will only run if there are changes in the helm charts compared to main branch helm charts, basically git diff between them result should not be empty.Run chart-testing (install): This step runs thect installcommand using chart-testing to install the charts in the repository into the kind cluster.
- Now, create a file called
ct.yamlwhich will store the configs for the chart-testing:
target-branch: main
chart-dirs:
- deploy/helm/charts
helm-extra-args: --timeout 600s
check-version-increment: false
The
target-branchfield specifies the target branch used to identify changed charts.The
chart-dirsfield specifies the directories where the charts to be tested are located. In this case, the value isdeploy/charts, which means that the charts are located in thedeploy/chartsdirectory. Update this according to your directory structure.The
helm-extra-argsfield specifies additional arguments to pass to the Helm command when installing or upgrading charts. In this case, the value is--timeout 600s, which means that the Helm command will have a timeout of 600 seconds (10 minutes).The
check-version-incrementfield specifies whether chart-testing should check that the chart version is incremented inChart.yaml. In this case, the value isfalse
The directory structure finally looks like this :
Repository
└── ct.yaml
└── Deploy
└── Charts
└── example-helm-chart
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── ingress.yaml
│ ├── service.yaml
└── values.yaml
Now that we have configure, we can see the flow in action.
Consider a case where we wanted to enable Ingress for my app, but didn’t complete the all correct values in the helm chart and pushed to my test-branch .
#supplied values for my-app
ingress:
enabled: true
annotations: {}
hosts:
- host: chart-example.local
paths: []
#I didn't specify the path and helm install should fail.
As you can see the below, the Action failed :

Since the problem is with helm templating, the lint step passsed sucessfully.
Run ct lint --config ct.yaml
Linting charts...
------------------------------------------------------------------------------------------------------------------------
Charts to be processed:
------------------------------------------------------------------------------------------------------------------------
my-app => (version: "0.2.8", path: "charts/my-app")
------------------------------------------------------------------------------------------------------------------------
Linting chart "my-app => (version: \"0.2.8\", path: \"charts/my-app\")"
Validating /home/runner/work/helm-actions-demo/my-app/Chart.yaml...
Validation success! 👍
Validating maintainers...
==> Linting /charts/my-app
1 chart(s) linted, 0 chart(s) failed
------------------------------------------------------------------------------------------------------------------------
✔︎ example-v2 => (version: "0.2.8", path: "charts/my-app")
------------------------------------------------------------------------------------------------------------------------
All charts linted successfully
- Helm templating and helm install should fail since the Ingress config is incomplete :
Run ct install --config ct.yaml
Installing charts...
------------------------------------------------------------------------------------------------------------------------
Charts to be processed:
------------------------------------------------------------------------------------------------------------------------
my-app => (version: "0.2.8", path: "charts/my-app")
------------------------------------------------------------------------------------------------------------------------
Installing chart "my-app => (version: \"0.2.8\", path: \"my-app\")"...
Error: INSTALLATION FAILED: Ingress.extensions "my-app-ingress" is invalid: spec.rules[0].http.paths: Required value
Trigger GitHub Actions for Push and other Events
Similar to the above config, you can just configure GitHub actions to trigger whenever a push to the main (or any other) branch happens. Or you can combine the trigger with push and pull request.
#For both
on: [pull_request,push]
#For Push event
on: push
#For push to specifc branches
on:
push:
branches:
- main
- test-branch
#For PR on specific branches
on:
pull_request:
branches:
- main
- develop
You can more about GitHub workflow trigger events here.
Specifying Multiple Helm Values Files
You can specify values in a folder called cilocated in the root of your chart repository. The ci folder should contain a values.yamlfile with the desired values, and chart-testing will automatically use these values when running the ct installcommand.
For example, if you have a chart repository with the following structure:
Repository
└── ct.yaml
└── Deploy
└── Charts
└── example-helm-chart
├── ci
│ ├── values-us-east-1.yaml
│ ├── values-us-west-2.yaml
│ └── values-eu-west-1.yaml
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── ingress.yaml
│ ├── service.yaml
└── values.yaml
It will iterate over every values-*.yaml and the chart is installed and tested for each of these files.
Helm Upgrades
To test an in-place upgrade of a chart, you can use the upgrade flag with the ct install
command. This flag will cause chart-testing to test an in-place upgrade of the chart from its previous revision.
Add upgrade: true into your ct.yaml to enable the upgrade of the Helm charts.
If the current version should not introduce a breaking change according to the SemVer specification, the upgrade will be tested.
Handling Dependencies in Chart Testing
When testing Helm charts that have dependencies on other charts, it is important to ensure that these dependencies are properly installed and configured in order for the tests to be successful. By default, ct install handles dependency of helm charts i.e it will run helm dependency build to fetch the chart specified.
- You need to add the repository in
ct.yamlso that repository index can be fetched, Bitnami in this example :
chart-repos:
- bitnami=https://charts.bitnami.com/bitnami
- Next, you need to specify the required chart in your
Chart.yamlof your specific helm chart, Redis for Example :
dependencies:
- name: redis
version: 17.4.0
repository: https://charts.bitnami.com/bitnami
- Then you can use your custom values for the Redis chart in the
values.yaml, for example :
redis:
enabled: true
cluster:
enabled: false
References :
https://GitHub.com/helm/chart-testing
https://GitHub.com/marketplace/actions/helm-chart-testing
https://docs.GitHub.com/en/actions/using-jobs/using-jobs-in-a-workflow#overview