From 10486d36f390ee35fda47fe0d281d10e3cdd93b8 Mon Sep 17 00:00:00 2001 From: sd109 Date: Tue, 8 Jul 2025 12:13:06 +0100 Subject: [PATCH] Add Helm lint CI workflow --- .github/workflows/helm-lint.yaml | 43 ++++ .github/workflows/test-pr.yaml | 9 +- .github/workflows/update-dependencies.yaml | 9 +- CONTRIBUTING.md | 22 ++ chart/.helmignore | 2 + chart/Chart.yaml | 2 +- .../__snapshot__/snapshot_test.yaml.snap | 206 ++++++++++++++++++ chart/tests/snapshot_test.yaml | 7 + chart/values.yaml | 2 +- lintconf.yaml | 5 + 10 files changed, 303 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/helm-lint.yaml create mode 100644 CONTRIBUTING.md create mode 100644 chart/tests/__snapshot__/snapshot_test.yaml.snap create mode 100644 chart/tests/snapshot_test.yaml create mode 100644 lintconf.yaml diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml new file mode 100644 index 0000000..008af44 --- /dev/null +++ b/.github/workflows/helm-lint.yaml @@ -0,0 +1,43 @@ +name: Helm Lint +on: + workflow_call: + inputs: + ref: + type: string + description: The Git ref under test. + required: true + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: v3.15.3 + + - name: Set up chart-testing + uses: helm/chart-testing-action@v2 + + - name: Run chart-testing (lint) + run: |- + ct lint \ + --lint-conf lintconf.yaml \ + --target-branch ${{ github.event.repository.default_branch }} \ + --charts chart/ \ + --validate-maintainers=false + + - name: Run template validation + run: |- + helm template foo chart \ + | docker run -i --rm ghcr.io/yannh/kubeconform:latest \ + --strict --summary + + - name: Run manifest snapshot test + run: docker run -i --rm -v $(pwd):/apps helmunittest/helm-unittest chart diff --git a/.github/workflows/test-pr.yaml b/.github/workflows/test-pr.yaml index ff49a78..8293801 100644 --- a/.github/workflows/test-pr.yaml +++ b/.github/workflows/test-pr.yaml @@ -24,15 +24,22 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} + # Run the chart linting on every PR, even from external repos + helm_lint: + uses: ./.github/workflows/helm-lint.yaml + with: + ref: ${{ github.event.pull_request.head.sha }} + # This job exists so that PRs from outside the main repo are rejected fail_on_remote: + needs: [unit_tests, helm_lint] runs-on: ubuntu-latest steps: - name: PR must be from a branch in the azimuth-cloud/azimuth-apps-operator repo run: exit ${{ github.event.pull_request.head.repo.full_name == 'azimuth-cloud/azimuth-apps-operator' && '0' || '1' }} publish_artifacts: - needs: [unit_tests, fail_on_remote] + needs: [fail_on_remote] uses: ./.github/workflows/build-push-artifacts.yaml with: ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/update-dependencies.yaml b/.github/workflows/update-dependencies.yaml index 2f6d882..025905b 100644 --- a/.github/workflows/update-dependencies.yaml +++ b/.github/workflows/update-dependencies.yaml @@ -6,7 +6,7 @@ on: workflow_dispatch: # Run nightly schedule: - - cron: '0 0 * * *' + - cron: "0 0 * * *" jobs: propose_github_release_updates: @@ -43,6 +43,13 @@ jobs: updates: | ${{ matrix.version_jsonpath }}=${{ steps.next.outputs.version }} + - name: Update manifest snapshots + run: |- + docker run -i --rm --user $(id -u) \ + -v $(pwd):/apps \ + helmunittest/helm-unittest -u \ + chart + - name: Generate app token for PR uses: azimuth-cloud/github-actions/generate-app-token@master id: generate-app-token diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1e97e73 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,22 @@ +# Contributing + +We welcome contributions and suggestions for improvements to this code base. +Please check for relevant issues and PRs before opening a new one of your own. + +## Making a contribution + +### Helm template snapshots + +The CI in this repository uses the Helm +[unittest](https://github.com/helm-unittest/helm-unittest) plugin's +snapshotting functionality to check PRs for changes to the templated manifests. +Therefore, if your PR makes changes to the manifest templates or values, you +will need to update the saved snapshots to allow your changes to pass the +automated tests. The easiest way to do this is to run the `helm unittest` command +inside a docker container from the repo root. + +``` +docker run -i --rm -v $(pwd):/apps helmunittest/helm-unittest chart -u +``` + +where the `-u` option is used to update the existing snapshots. diff --git a/chart/.helmignore b/chart/.helmignore index 0e8a0eb..31bcbc2 100644 --- a/chart/.helmignore +++ b/chart/.helmignore @@ -21,3 +21,5 @@ .idea/ *.tmproj .vscode/ +# Helm unit-test files +tests/ diff --git a/chart/Chart.yaml b/chart/Chart.yaml index bed2788..a405bb8 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -3,6 +3,6 @@ apiVersion: v2 name: azimuth-apps-operator description: Helm chart for deploying the Azimuth apps operator. type: application -# The version and appVersion are updated by the chart build script +# The version and appVersion are updated by the chart build script version: 0.1.0 appVersion: main diff --git a/chart/tests/__snapshot__/snapshot_test.yaml.snap b/chart/tests/__snapshot__/snapshot_test.yaml.snap new file mode 100644 index 0000000..b21f976 --- /dev/null +++ b/chart/tests/__snapshot__/snapshot_test.yaml.snap @@ -0,0 +1,206 @@ +templated manifests should match snapshot: + 1: | + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: azimuth-apps-operator + app.kubernetes.io/version: main + helm.sh/chart: azimuth-apps-operator-0.1.0 + name: release-name-azimuth-apps-operator + rules: + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - list + - get + - watch + - create + - apiGroups: + - apiextensions.k8s.io + resourceNames: + - apptemplates.apps.azimuth-cloud.io + - apps.apps.azimuth-cloud.io + resources: + - customresourcedefinitions + verbs: + - update + - patch + - apiGroups: + - "" + resources: + - namespaces + verbs: + - list + - watch + - apiGroups: + - "" + - events.k8s.io + resources: + - events + verbs: + - create + - apiGroups: + - apps.azimuth-cloud.io + resources: + - '*' + verbs: + - '*' + - apiGroups: + - "" + resources: + - secrets + verbs: + - '*' + - apiGroups: + - source.toolkit.fluxcd.io + resources: + - helmrepositories + - helmcharts + verbs: + - '*' + - apiGroups: + - helm.toolkit.fluxcd.io + resources: + - helmreleases + verbs: + - '*' + - apiGroups: + - identity.azimuth.stackhpc.com + resources: + - platforms + verbs: + - '*' + 2: | + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: azimuth-apps-operator + app.kubernetes.io/version: main + helm.sh/chart: azimuth-apps-operator-0.1.0 + name: release-name-azimuth-apps-operator + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: release-name-azimuth-apps-operator + subjects: + - kind: ServiceAccount + name: release-name-azimuth-apps-operator + namespace: NAMESPACE + 3: | + apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: azimuth-apps-operator + app.kubernetes.io/version: main + helm.sh/chart: azimuth-apps-operator-0.1.0 + name: release-name-azimuth-apps-operator + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: azimuth-apps-operator + strategy: + type: Recreate + template: + metadata: + annotations: + azimuth.stackhpc.com/config-hash: 433363aa020effa397e7e63c535231db34bbc4d2900eadd596609a64cb8ebbbd + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: azimuth-apps-operator + spec: + containers: + - image: ghcr.io/azimuth-cloud/azimuth-apps-operator:main + imagePullPolicy: IfNotPresent + name: operator + ports: + - containerPort: 8080 + name: metrics + protocol: TCP + resources: {} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + volumeMounts: + - mountPath: /etc/azimuth + name: etc-azimuth + readOnly: true + - mountPath: /tmp + name: tmp + securityContext: + runAsNonRoot: true + serviceAccountName: release-name-azimuth-apps-operator + volumes: + - name: etc-azimuth + secret: + secretName: release-name-azimuth-apps-operator + - emptyDir: {} + name: tmp + 4: | + apiVersion: v1 + kind: Secret + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: azimuth-apps-operator + app.kubernetes.io/version: main + helm.sh/chart: azimuth-apps-operator-0.1.0 + name: release-name-azimuth-apps-operator + stringData: + apps-operator.yaml: | + !include "/etc/azimuth/defaults.yaml,/etc/azimuth/user-config.yaml" + defaults.yaml: | + {} + user-config.yaml: | + zenithOperator: + chartName: zenith-operator + chartRepo: https://azimuth-cloud.github.io/zenith + chartVersion: 0.15.1 + 5: | + apiVersion: v1 + kind: Service + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: azimuth-apps-operator + app.kubernetes.io/version: main + helm.sh/chart: azimuth-apps-operator-0.1.0 + name: release-name-azimuth-apps-operator + spec: + ports: + - name: metrics + port: 8080 + protocol: TCP + targetPort: metrics + selector: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/name: azimuth-apps-operator + type: ClusterIP + 6: | + apiVersion: v1 + kind: ServiceAccount + metadata: + labels: + app.kubernetes.io/instance: RELEASE-NAME + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: azimuth-apps-operator + app.kubernetes.io/version: main + helm.sh/chart: azimuth-apps-operator-0.1.0 + name: release-name-azimuth-apps-operator diff --git a/chart/tests/snapshot_test.yaml b/chart/tests/snapshot_test.yaml new file mode 100644 index 0000000..262562e --- /dev/null +++ b/chart/tests/snapshot_test.yaml @@ -0,0 +1,7 @@ +# To update manifest snapshots run helm unittest plugin with -u option: +# docker run -i --rm -v $(pwd):/apps helmunittest/helm-unittest -u chart +suite: Manifest snapshot tests +tests: + - it: templated manifests should match snapshot + asserts: + - matchSnapshot: {} diff --git a/chart/values.yaml b/chart/values.yaml index 1c52d58..c3d8021 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -14,7 +14,7 @@ trustBundle: image: repository: ghcr.io/azimuth-cloud/azimuth-apps-operator pullPolicy: IfNotPresent - tag: "" # Defaults to appVersion if not given + tag: "" # Defaults to appVersion if not given imagePullSecrets: [] diff --git a/lintconf.yaml b/lintconf.yaml new file mode 100644 index 0000000..328e2ce --- /dev/null +++ b/lintconf.yaml @@ -0,0 +1,5 @@ +rules: + comments: + require-starting-space: true + ignore-shebangs: true + min-spaces-from-content: 1