Auto Docs, Test And Release A Helm Chart With GitHub Actions

Auto Docs, Test And Release A Helm Chart With GitHub Actions

TL;DR Code

-> github.com/dirien/minecraft-prometheus-expo..

Motivation

Currently, I am working on a Helm chart for my Minecraft Prometheus exporter. Nothing special on this task.

But now I want to put as much automation into it, as possible.

Automated Documentation With helm-docs

image.png

The helm-docs tool auto-generates documentation from helm charts into markdown files. That is very cool, as we don't need to take care to keep the documentation manually in up to date with the values.yaml

To do this, we create in the chart directory a README.md.gotmpl file, this contains the template to render the final README.md file with the informations from your Helm chart values.

Here is the example of my file:

# Minecraft Exporter for Prometheus

![Version: {{ .Version }}](https://img.shields.io/badge/Version-{{ .Version | replace "-" "--" }}-informational?style=for-the-badge)
{{ if .Type }}![Type: {{ .Type }}](https://img.shields.io/badge/Type-{{ .Type }}-informational?style=for-the-badge) {{ end }}
{{ if .AppVersion }}![AppVersion: {{ .AppVersion }}](https://img.shields.io/badge/AppVersion-{{ .AppVersion | replace "-" "--" }}-informational?style=for-the-badge) {{ end }}

![Prometheus](https://img.shields.io/badge/Prometheus-E6522C?style=for-the-badge&logo=Prometheus&logoColor=white)
![Minecraft](https://img.shields.io/badge/Minecraft-62B47A?style=for-the-badge&logo=Minecraft&logoColor=white)
![Docker](https://img.shields.io/badge/docker-2496ED?style=for-the-badge&logo=docker&logoColor=white)
![Alpine Linux 3.15.0](https://img.shields.io/badge/alpine_linux_3.15.0-0D597F?style=for-the-badge&logo=alpine-linux&logoColor=white)

## Description

{{ template "chart.description" . }}

## Usage
<fill out>

{{ template "chart.valuesSection" . }}

{{ template "chart.homepageLine" . }}

{{ template "chart.sourcesSection" . }}

{{ template "chart.maintainersSection" . }}

helm-docs has plenty of predefined template functions for you to use, on top you can also use the variables directly. Throw in your static parts and you get an awesome automated documentations.

Hint: To get the values table rendered with proper default values, you need to add # -- description

image:
  # -- The docker image repository to use
  repository: ghcr.io/dirien/minecraft-exporter
  # -- The docker image tag to use
  tag: ''
  # -- The docker image pull policy
  pullPolicy: IfNotPresent

You can add # -- @default defaut text

image:
  # -- The docker image repository to use
  repository: ghcr.io/dirien/minecraft-exporter
  # -- The docker image tag to use
  # @default Chart version
  tag: ''
  # -- The docker image pull policy
  pullPolicy: IfNotPresent

Now you have to option to create a pre-commit hook, so everytime you change a specified file in your .pre-commit-hooks.yaml the README.md file gets re-rendered

Let me shortly explain pre-commit

How to configure the pre-commit hook

I am going to use pre-commit. Check the docs on how to install it.

pre-commit is a multi-language package manager for pre-commit hooks. You specify a list of hooks you want and pre-commit manages the installation and execution of any hook written in any language before every commit.

I created a git-hook folder with the helm-docs.sh. This file will be called, from pre-commit

#!/usr/bin/env bash

set -e

if ! command -v helm-docs > /dev/null 2>&1; then
    echo "Please install helm-docs to run the pre-commit hook! https://github.com/norwoodj/helm-docs#installation"
    exit 1
fi

helm-docs "${@}"

in the .pre-commit-config.yaml you can set the args to pass the the git-hook/helm-docs.sh script.

repos:
  - repo: https://github.com/dirien/minecraft-prometheus-exporter
    rev: v0.10.0
    hooks:
      - id: helm-docs
        args:
          - --template-files=README.md.gotmpl

The content of the .pre-commit-hooks.yaml defines on which files, the git-hook/helm-docs.sh should be called.

And of course the relation between the id of the .pre-commit-config.yaml

- id: helm-docs
  args: []
  description: Uses 'helm-docs' to create documentation from the Helm chart's 'values.yaml' file, and inserts the result into a corresponding 'README.md' file.
  entry: git-hook/helm-docs.sh
  files: (README\.md\.gotmpl|(Chart|requirements|values)\.yaml)$
  language: script
  name: Helm Docs
  require_serial: true

Hint: During the start, you will not have a remote location with your .pre-commit-hooks.yaml so you can test your hook via this command:

pre-commit install
pre-commit try-repo . helm-docs --verbose --all-files

Otherwise, you can just call:

pre-commit install
pre-commit install-hooks

The only downside of this approach is: If someone is contributing to your helm chart you need to rely on, that has the pre-commit hook cli installed.

GitHub Action

So I will add the generation of the helm-docs to my CI pipeline called chart-publish

name: chart-publish
...
env:
  HELM_DOCS_VERSION: "1.7.0"
...
      - name: install helm-docs
        run: |
          cd /tmp
          wget https://github.com/norwoodj/helm-docs/releases/download/v${{env.HELM_DOCS_VERSION}}/helm-docs_${{env.HELM_DOCS_VERSION}}_Linux_x86_64.tar.gz
          tar -xvf helm-docs_${{env.HELM_DOCS_VERSION}}_Linux_x86_64.tar.gz
          sudo mv helm-docs /usr/local/sbin

      - name: run helm-docs
        run: |
          helm-docs -t README.md.gotmpl -o README.md
...

You can find more information here -> github.com/norwoodj/helm-docs

Chart Testing & Linting With ct

image.png

ct is the tool for testing Helm charts. It automatically detects if a charts changed against the target branch we define.

We need to create a config file called ct-lint.yaml under the .github/configs with following content:

remote: origin
target-branch: main
chart-dirs:
  - charts
helm-extra-args: "--timeout 600s"
validate-chart-schema: true
validate-chart-values: true
validate-maintainers: true
validate-yaml: true
exclude-deprecated: true
excluded-charts: []

This are some properties for the cli, so we don't need to pass them via flag. On addition, I created lintconf.yamlunder the same folder, which contains some rules for yamllint.

yamllint does not only check for syntax validity, but for weirdnesses like key repetition and cosmetic problems such as lines length, trailing spaces, indentation, etc.

---
rules:
  braces:
    min-spaces-inside: 0
    max-spaces-inside: 0
    min-spaces-inside-empty: -1
    max-spaces-inside-empty: -1
.....  snip .....
  without
    check-multi-line-strings: false
  key-duplicates: enable
  line-length: disable # Lines can be any length
  new-line-at-end-of-file: enable
  new-lines:
    type: unix
  trailing-spaces: enable
  truthy:
    level: warning

And then you can run:

ct lint  --config .github/configs/ct-lint.yaml --lint-conf .github/configs/lintconf.yaml

to execute the linting.

As mentioned before, ct needs some additional tools like Helm, Git (2.17.0 or later), Yamllint, Yamale, Kubectl so it is better to use the ct container, if you want to test the linting localy:

docker container run --rm -v $(pwd):/workspace -ti quay.io/helmpack/chart-testing:v3.5.0

Next step is the testing of the chart. If you have a local kubernetes cluster you can call:

ct install --config ./.github/configs/ct-lint.yaml

It will install the chart and if everything works fine instantly deletes it.

Installing charts...

------------------------------------------------------------------------------------------------------------------------
 Charts to be processed:
------------------------------------------------------------------------------------------------------------------------
 minecraft-exporter => (version: "0.1.0", path: "charts/minecraft-exporter")
------------------------------------------------------------------------------------------------------------------------

Installing chart 'minecraft-exporter => (version: "0.1.0", path: "charts/minecraft-exporter")'...
Creating namespace 'minecraft-exporter-hwe920t1yv'...
namespace/minecraft-exporter-hwe920t1yv created
NAME: minecraft-exporter-hwe920t1yv

....

========================================================================================================================
Deleting release 'minecraft-exporter-hwe920t1yv'...
release "minecraft-exporter-hwe920t1yv" uninstalled
W0122 21:56:16.272288   69990 warnings.go:70] policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
Deleting namespace 'minecraft-exporter-hwe920t1yv'...
namespace "minecraft-exporter-hwe920t1yv" deleted
Namespace 'minecraft-exporter-hwe920t1yv' terminated.
------------------------------------------------------------------------------------------------------------------------
 ✔︎ minecraft-exporter => (version: "0.1.0", path: "charts/minecraft-exporter")
------------------------------------------------------------------------------------------------------------------------
All charts installed successfully

Of course you can call both actions in one command with ct lint-and-install

Finally we can create a GitHub action to run during a PR and test and lint our Helm chart.

---
name: ct-linting-and-testing
on: pull_request
jobs:
  chart-test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          fetch-depth: 0

      - name: Set up Helm
        uses: azure/setup-helm@v1
        with:
          version: v3.6.3

      - name: Set up python
        uses: actions/setup-python@v2
        with:
          python-version: 3.7

      - name: Setup Chart Linting
        id: lint
        uses: helm/chart-testing-action@v2.2.0

      - name: List changed charts
        id: list-changed
        run: |
          ## If executed with debug this won't work anymore.
          changed=$(ct --config ./.github/configs/ct-lint.yaml list-changed)
          charts=$(echo "$changed" | tr '\n' ' ' | xargs)
          if [[ -n "$changed" ]]; then
            echo "::set-output name=changed::true"
            echo "::set-output name=changed_charts::$charts"
          fi

      - name: Run chart-testing (lint)
        run: ct lint --debug --config ./.github/configs/ct-lint.yaml --lint-conf ./.github/configs/lintconf.yaml

      - name: Create kind cluster
        uses: helm/kind-action@v1.2.0
        if: steps.list-changed.outputs.changed == 'true'

      - name: Run chart-testing (install)
        run: ct install --config ./.github/configs/ct-lint.yaml
        if: steps.list-changed.outputs.changed == 'true'

We use the predefined GitHub action for installing the ct CLI tool. So we dont need to take care about the additional tools ct needs.

image.png

On top as you can see, we start a Kind cluster to install and test the helm chart.

You can find more information here -> github.com/helm/chart-testing

Releasing the Chart with cr

image.png

The last part, is the quickest. We use cr as tool here. cr is a tool designed to help us to release a helm chart and connect a GitHub releases with the index.yaml and upload this to the GitHub pages.

We just need to add in our chart-publish following task at as an additional task:

---
name: chart-publish
...
     - name: Run chart-releaser
        uses: helm/chart-releaser-action@v1.2.1
        env:
          CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
...

That's it.

You should be now apple to install your chart via your GitHub pages url.

helm repo add minecraft-exporter https://dirien.github.io/minecraft-prometheus-exporter
helm repo update
helm install minecraft-exporter minecraft-exporter/minecraft-exporter --version 0.1.2

You can find more information here -> github.com/helm/chart-releaser

Result on ArtifactHub

Artifact Hub

The End

image.png

Resources