Skip to content

ci: add helm-template-smoke job to catch chart-render parse errors#533

Closed
bussyjd wants to merge 1 commit into
mainfrom
ci/helm-template-smoke
Closed

ci: add helm-template-smoke job to catch chart-render parse errors#533
bussyjd wants to merge 1 commit into
mainfrom
ci/helm-template-smoke

Conversation

@bussyjd
Copy link
Copy Markdown
Collaborator

@bussyjd bussyjd commented May 24, 2026

Summary

  • Add a new GitHub Actions workflow that pipes the embedded base chart through helm template and helm lint on every PR touching internal/embed/infrastructure/**
  • Catches Go-template parse errors (e.g. the unescaped {{ \$labels }} in a PrometheusRule annotation fixed in fix(prometheus-rules): escape PromQL $labels for Helm rendering #527) that go test ./... cannot detect because unit tests don't exercise Helm rendering
  • Uses helm v3.20.1 (matches the version pinned in obolup.sh)

Why

PR #527 fixed an unescaped {{ \$labels.X }} in a PrometheusRule annotation that made helm upgrade base ./base fail on every obol stack up:

Error: UPGRADE FAILED: parse error at
(base-infra/templates/x402-prometheus-rules.yaml:N):
undefined variable "\$labels"

The bug shipped to integration testing because nothing in CI exercises helm template against internal/embed/infrastructure/base. This PR closes that gap.

What the job does (and doesn't) catch

Catches:

  • Go-template parse errors anywhere in base/templates/*.yaml
  • Chart structure issues (helm lint)
  • Same parse errors in cloudflared/templates/*.yaml

Doesn't catch (left for future jobs):

  • Helmfile state-value substitution errors in values/*.yaml.gotmpl (needs helmfile lint)
  • Real-cluster install issues — helm/chart-testing-action in the existing lint-test.yaml workflow already handles top-level charts; this job complements it for the embedded chart

Implementation notes

The base chart contains {{OLLAMA_HOST_IP}}, {{OLLAMA_HOST}}, and {{CLUSTER_ID}} placeholders that are not Helm template actions — they're substituted by internal/defaults/defaults.go::InfrastructureReplacements before helmfile sync runs (see obol stack init). Plain helm template chokes on them as undefined-variable actions, so the job copies the chart to a temp dir and sed-substitutes CI stubs first, mirroring the runtime flow.

Test plan

PR #527 fixed an unescaped {{ $labels }} in a PrometheusRule
annotation that broke `helm upgrade base` on every `obol stack up`.
The bug shipped to integration testing because go test ./...
doesn't exercise Helm rendering.

This job pipes the embedded base chart through `helm template`
on every PR; parse errors fail the build before merge.

  - Runs against ./internal/embed/infrastructure/base
  - Uses helm v3.20.1 (matches obolup.sh pinned version)
  - Also runs `helm lint` for chart-structure issues
  - Substitutes {{OLLAMA_HOST_IP}}/{{CLUSTER_ID}} stubs in a temp
    copy of the chart (mirroring what `obol stack init` does via
    internal/defaults/defaults.go::InfrastructureReplacements)
  - Future: pair with a helmfile-lint job for state-value tests

If we ever land a chart-template change that this doesn't catch,
expand the helm-template invocation with --set values mimicking
what `obol stack up` provides.
Comment on lines +17 to +83
name: helm template embedded chart
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Set up Helm
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1
with:
version: v3.20.1 # match obolup.sh pinned version

- name: helm template ./base
run: |
# Render the embedded `base` chart and fail on Go-template parse
# errors. Catches bugs like the unescaped `{{ $labels }}` in
# PrometheusRule annotations that broke `helm upgrade base` on
# every `obol stack up` (see PR #527). `go test ./...` does not
# exercise Helm rendering, so this is the only pre-merge gate
# for chart parse errors.
#
# The base chart contains `{{PLACEHOLDER}}` strings (e.g.
# `{{OLLAMA_HOST_IP}}`, `{{CLUSTER_ID}}`) that are substituted
# by `internal/defaults/defaults.go::InfrastructureReplacements`
# before helmfile runs. Helm's Go-template parser would treat
# them as actions and fail, so we substitute stub values into
# a working copy first — mirroring what `obol stack init` does.
set -euo pipefail
workdir="$(mktemp -d)"
cp -R internal/embed/infrastructure/base "$workdir/base"
# Mirror internal/defaults InfrastructureReplacements with CI stubs.
find "$workdir/base" -type f -name '*.yaml' -print0 \
| xargs -0 sed -i \
-e 's/{{OLLAMA_HOST_IP}}/127.0.0.1/g' \
-e 's/{{OLLAMA_HOST}}/localhost/g' \
-e 's/{{CLUSTER_ID}}/ci-helm-smoke/g'
# Match values passed by helmfile.yaml `releases[base]`.
helm template base "$workdir/base" \
--set dataDir=/data \
--set network=mainnet \
> /dev/null

- name: helm template ./cloudflared
run: |
# The cloudflared chart has no placeholder substitution and uses
# default values from values.yaml.
set -euo pipefail
helm template cloudflared internal/embed/infrastructure/cloudflared \
> /dev/null

- name: helm lint ./base
run: |
set -euo pipefail
workdir="$(mktemp -d)"
cp -R internal/embed/infrastructure/base "$workdir/base"
find "$workdir/base" -type f -name '*.yaml' -print0 \
| xargs -0 sed -i \
-e 's/{{OLLAMA_HOST_IP}}/127.0.0.1/g' \
-e 's/{{OLLAMA_HOST}}/localhost/g' \
-e 's/{{CLUSTER_ID}}/ci-helm-smoke/g'
helm lint "$workdir/base" \
--set dataDir=/data \
--set network=mainnet

- name: helm lint ./cloudflared
run: |
set -euo pipefail
helm lint internal/embed/infrastructure/cloudflared
@bussyjd
Copy link
Copy Markdown
Collaborator Author

bussyjd commented May 24, 2026

Superseded by bundle PR #536 — closing in favor of the consolidated merge target. Original branch and history preserved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants