Promote Flux Helm Releases with GitHub Actions
This guide shows how to configure Flux and GitHub Actions to promote Helm Releases across environments when a new Helm chart version is available.
For this guide we assume a scenario with two clusters: staging and production; with the following promotion pipeline:
- On the staging cluster, Flux will monitor the Helm repository for new chart versions, and it will automatically upgrade and test the Helm release.
- After the Helm release is successfully upgraded, Flux will send an event to GitHub that will trigger a GitHub Actions workflow.
- The GitHub workflow receives the new chart version, updates the Flux
HelmRelease
manifest YAML for the production cluster and opens a Pull Request. - When the Pull Request is merged, Flux upgrades the Helm release on the production cluster to the chart version that was tested in staging.
Prerequisites
For this guide we assume you have two clusters bootstrapped with Flux and a good understanding of how Flux manages Helm releases. Please see the helm example repository to familiarise yourself with Flux and Helm.
Define staging and production releases
For the staging cluster, we’ll define a HelmRelease
for which Flux will monitor the Helm repository,
and it will automatically upgrade the Helm release to the latest chart version based on a semver range.
Example of clusters/staging/apps/demo.yaml
:
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: demo
namespace: apps
spec:
interval: 60m
chart:
spec:
chart: demo
version: "1.x" # automatically upgrade to the latest version
interval: 5m # scan the Helm repository every five minutes
sourceRef:
kind: HelmRepository
name: demo-charts
test:
enable: true # run tests on upgrades
valuesFrom:
- kind: Secret
name: demo-staging-values
For the production cluster, we’ll define a HelmRelease
with a fixed version, the chart version will be
updated in Git by GitHub Actions based on the Flux events.
Example of clusters/production/apps/demo.yaml
:
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: demo
namespace: apps
spec:
interval: 60m
chart:
spec:
chart: demo
# This field will be updated by GitHub Actions.
version: "1.0.0"
sourceRef:
kind: HelmRepository
name: demo-charts
valuesFrom:
- kind: Secret
name: demo-production-values
Define the promotion GitHub workflow
To promote a chart version that was successfully deployed and tested on staging, we’ll create a GitHub workflow that reacts to Flux repository dispatch events.
The event
that Flux generate for a HelmRelease
upgrade contains metadata about the event,
with fields that can be used to determine the chart version that was deployed:
metadata.revision
: the Helm chart version deployed.metadata.oci-digest
(optional): the OCI digest of the oci artifact deployed in case of anOCIRepository
source.
Promoting a HelmRelease with a chart version
The following .github/workflows/demo-promotion.yaml
workflow will react to the Flux
repository dispatch events and promote the Helm release to the production cluster.
name: demo-promotion
on:
repository_dispatch:
types:
- HelmRelease/demo.apps
permissions:
contents: write
pull-requests: write
jobs:
promote:
runs-on: ubuntu-latest
# Start promotion when the staging cluster has successfully
# upgraded the Helm release to a new chart version.
if: |
github.event.client_payload.metadata.env == 'staging' &&
github.event.client_payload.severity == 'info'
steps:
# Checkout main branch.
- uses: actions/checkout@v3
with:
ref: main
# Parse the event metadata to determine the chart version deployed on staging.
- name: Get chart version from staging
id: staging
run: |
VERSION=$(echo ${{ github.event.client_payload.metadata.revision }} | cut -d '@' -f1)
echo VERSION=${VERSION} >> $GITHUB_OUTPUT
# Patch the chart version in the production Helm release manifest.
- name: Set chart version in production
id: production
env:
CHART_VERSION: ${{ steps.staging.outputs.version }}
run: |
echo "set chart version to ${CHART_VERSION}"
yq eval '.spec.chart.spec.version=env(CHART_VERSION)' -i ./clusters/production/apps/demo.yaml
# Open a Pull Request if an upgrade is needed in production.
- name: Open promotion PR
uses: peter-evans/create-pull-request@v4
with:
branch: demo-promotion
delete-branch: true
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: Update demo to v${{ steps.staging.outputs.version }}
title: Promote demo release to v${{ steps.staging.outputs.version }}
body: |
Promote demo release on production to v${{ steps.staging.outputs.version }}
The above workflow does the following:
- Runs on repository dispatch events issued by Flux with the
HelmRelease/demo.apps
type. - Filters the events to take into consideration only success Helm release upgrades.
- Clones the
main
branch where the FluxHelmRelease
YAML manifests are defined. - Parses the event metadata to determine the chart version deployed on staging.
- Patches the chart version in the
HelmRelease
manifest atclusters/production/apps/demo.yaml
. - Creates a new branch called
demo-promotion
, commits the version change and opens a Pull Request againstmain
.
Note that you should adapt the workflow to match your release name, namespace and YAML path.
Promoting a HelmRelease using OCI digests
The following .github/workflows/demo-promotion.yaml
workflow will react to the Flux
repository dispatch events and promote the Helm release to the production cluster.
The expected HelmRelease
is one with a OCIRepository
source.
name: demo-promotion
on:
repository_dispatch:
types:
- HelmRelease/demo.apps
permissions:
contents: write
pull-requests: write
jobs:
promote:
runs-on: ubuntu-latest
# Start promotion when the staging cluster has successfully
# upgraded the Helm release to a new chart version.
if: |
github.event.client_payload.metadata.env == 'staging' &&
github.event.client_payload.severity == 'info'
steps:
# Checkout main branch.
- uses: actions/checkout@v3
with:
ref: main
# Parse the event metadata to determine the chart version deployed on staging.
- name: Get chart version from staging
id: staging
run: |
DIGEST=$(echo ${{ github.event.client_payload.metadata.oci-digest }} | cut -d '@' -f1)
echo DIGEST=${DIGEST} >> $GITHUB_OUTPUT
VERSION=$(echo ${{ github.event.client_payload.metadata.revision }} | cut -d '@' -f1)
echo VERSION=${VERSION} >> $GITHUB_OUTPUT
# Patch the digest in the production OCIRepository manifest.
- name: Set chart version in production
id: production
env:
DIGEST: ${{ steps.staging.outputs.digest }}
run: |
echo "set ociRepository digest to ${DIGEST}"
# This will filter out all resources that do not have a digest field.
yq eval '(select(.spec.ref.digest) | .spec.ref.digest) = env(DIGEST)' -i ./clusters/production/apps/demo.yaml
# add the chart version as a line comment
env lc="version ${{ steps.staging.outputs.version }}" \
yq eval '(select(.spec.ref.digest) | .spec.ref.digest) line_comment=env(lc)' -i ./clusters/production/apps/demo.yaml
# Open a Pull Request if an upgrade is needed in production.
- name: Open promotion PR
uses: peter-evans/create-pull-request@v4
with:
branch: demo-promotion
delete-branch: true
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: Update demo to v${{ steps.staging.outputs.version }} with digest ${{ steps.staging.outputs.digest }}
title: Promote demo release to v${{ steps.staging.outputs.version }} with digest ${{ steps.staging.outputs.digest }}
body: |
Promote demo release on production to v${{ steps.staging.outputs.version }} with digest ${{ steps.staging.outputs.digest }}
The above workflow does the following:
- Runs on repository dispatch events issued by Flux with the
HelmRelease/demo.apps
type. - Filters the events to take into consideration only success Helm release upgrades.
- Clones the
main
branch where the FluxHelmRelease
YAML manifests are defined. - Parses the event metadata to determine the chart version deployed on staging and the corresponding OCI digest.
- Patches the digest in the
OCIRepository
manifest atclusters/production/apps/demo.yaml
. - Creates a new branch called
demo-promotion
, commits the version change and opens a Pull Request againstmain
.
Note that you should adapt the workflow to match your release name, namespace and YAML path.
Configure Flux for repository dispatching
On the staging cluster, we’ll configure Flux to send events to GitHub every time it performs a Helm release upgrade.
Example of clusters/staging/apps/demo-github.yaml
:
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Provider
metadata:
name: github
namespace: apps
spec:
type: githubdispatch
address: https://github.com/org/repo
secretRef:
name: github-token
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
name: demo-dispatch
namespace: apps
spec:
providerRef:
name: github
summary: "Trigger promotion"
eventMetadata:
env: staging
cluster: staging-1
region: eu-central-1
eventSeverity: info
eventSources:
- kind: HelmRelease
name: demo
inclusionList:
- ".*.upgrade.*succeeded.*"
Note that you should adapt the above definitions to match your GitHub repository address.
If
testing is enabled
in your HelmRelease, you can use the ".*.test.*succeeded.*"
expression in the inclusion list instead of ".*.upgrade.*succeeded.*"
.
This will ensure the promotion happens only after tests have been successfully run.
You also need to create a Kubernetes secret with a GitHub Personal Access Token that has access to the repository:
kubectl -n apps create secret generic github-token \
--from-literal=token=${GITHUB_TOKEN}
GitHub PAT
Note that it is advised to create a dedicated user for Flux under your GitHub organisation. Make the Flux user part of a GitHub team, so that you can give Flux access only to the repositories used withflux bootstrap github
.Relevant documentation
- Guides > Manage Helm Releases
- Toolkit Components > Helm Repository API
- Toolkit Components > Helm Release API
- Toolkit Components > Notification API > GitHub Dispatch