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
Contributor

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
ContributorAuthor

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

@bussyjdbussyjd closed this May 24, 2026
@OisinKyne
OisinKyne deleted the ci/helm-template-smoke branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd@github-advanced-security
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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
Contributor

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
ContributorAuthor

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

@bussyjdbussyjd closed this May 24, 2026
@OisinKyne
OisinKyne deleted the ci/helm-template-smoke branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd@github-advanced-security
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
Contributor

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
ContributorAuthor

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

@bussyjdbussyjd closed this May 24, 2026
@OisinKyne
OisinKyne deleted the ci/helm-template-smoke branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd@github-advanced-security
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
Contributor

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
ContributorAuthor

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

@bussyjdbussyjd closed this May 24, 2026
@OisinKyne
OisinKyne deleted the ci/helm-template-smoke branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd@github-advanced-security
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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
Contributor

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
ContributorAuthor

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

@bussyjdbussyjd closed this May 24, 2026
@OisinKyne
OisinKyne deleted the ci/helm-template-smoke branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd@github-advanced-security
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
Contributor

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
ContributorAuthor

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

@bussyjdbussyjd closed this May 24, 2026
@OisinKyne
OisinKyne deleted the ci/helm-template-smoke branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd@github-advanced-security
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
Contributor

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
ContributorAuthor

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

@bussyjdbussyjd closed this May 24, 2026
@OisinKyne
OisinKyne deleted the ci/helm-template-smoke branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd@github-advanced-security
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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
Contributor

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
ContributorAuthor

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

@bussyjdbussyjd closed this May 24, 2026
@OisinKyne
OisinKyne deleted the ci/helm-template-smoke branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd@github-advanced-security