Skip to content

ci: add deterministic Helm chart publication - #46

Merged
rusenask merged 5 commits into
masterfrom
feat/chart-release-pipeline
Sep 9, 2026
Merged

rusenask merged 5 commits into
masterfrom
feat/chart-release-pipeline

Conversation

@rusenask

@rusenask rusenask commented Sep 9, 2026 •

Copy link
Copy Markdown
Member

Summary

  • replace Cloud Build chart publication with a reusable, manually gated GitHub Actions workflow
  • create reproducible chart archives and collision-safe, generation-preconditioned GCS/index publication
  • configure operator 0.8.0 / chart 0.7.0 after discovering the immutable 0.6.0 publication
  • remove legacy packaging scripts and document the release boundary
  • record Phase 14 evidence and Phase 15 progress

External configuration completed

  • disabled Cloud Build trigger Webhook-Relay-Operator-Chart
  • created protected release environment with independent reviewer and tag/branch policy
  • created GitHub OIDC provider and bucket-scoped github-operator-charts service account
  • configured environment variables without storing a Google Cloud key

Validation

  • go test ./.scripts
  • make chart-release-test HELM_BIN=helm
  • make chart-package HELM_BIN=helm
  • make chart-release-check HELM_BIN=helm
  • helm lint / helm template
  • go test ./...
  • actionlint v1.7.7

Note

Medium Risk
Changes production chart delivery (GCS/index writes) and release automation; mitigations include gated publish, OIDC, immutability checks, and no overwrite of existing chart bytes.

Overview
Replaces Cloud Build and ad-hoc Helm packaging with a deterministic, collision-safe chart release pipeline on GitHub Actions.

A new reusable Helm Chart Release workflow packages the chart (Go-based reproducible .tgz, SHA-256, metadata), runs remote verification, uploads a candidate artifact, and optionally publishes to gs://charts.webhookrelay.com behind the protected release environment using Workload Identity (no stored GCP keys). Publication uses generation preconditions on chart objects and index.yaml, refuses byte-level overwrites of existing versions, and re-validates the published artifact and index.

CI now runs go test ./.scripts, make chart-package, chart-release-check, and chart-release-test alongside lint/render. Makefile exposes the same targets locally; .test/chart-release.sh exercises retry and collision behavior against a local HTTP repo.

Chart versions move to 0.7.0 (chart) / 0.8.0 (operator image defaults); e2e expects webhookrelay-operator-0.7.0.tgz. Legacy scripts (repo_sync.sh, gen_packages.sh, cloudbuild.yaml, etc.) are removed. docs/releasing.md and README/modernization-plan updates document immutable 0.5.0 / 0.6.0 charts and the maintainer release boundary.

Reviewed by Cursor Bugbot for commit 2f0eff6. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2f0eff6. Configure here.

Comment thread .github/workflows/chart-release.yml Outdated

publish:
name: Publish immutable chart
if: inputs.publish

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manual runs always enter publish

High Severity

if: inputs.publish does not treat workflow_dispatch booleans as booleans. Unchecked publish arrives as the string false, which is truthy, so the publish job still starts and waits on the release environment even when the run was validation-only.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2f0eff6. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved by requiring the reusable-workflow boolean to equal true explicitly. Manual dispatch has no publication input and remains validation-only.

Comment thread .scripts/chart-release.sh Outdated
go run "${REPO_ROOT}/.scripts/chart-artifact.go" verify-index "${index_dir}/index.yaml" \
"${CHART_VERSION}" "${digest}" "${REPOSITORY_URL}/${ARTIFACT_NAME}"
gcloud storage cp "${index_dir}/index.yaml" "gs://${CHART_BUCKET}/index.yaml" \
--if-generation-match="${index_generation}" --content-type=application/yaml

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Index precondition uses a stale snapshot

Medium Severity

publish_chart downloads index.yaml over HTTP first, then later reads the GCS generation and uploads a merge of that earlier snapshot with --if-generation-match. A newer index written in between still matches the observed generation, so the upload can overwrite it and drop entries.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2f0eff6. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved. Publication now reads index.yaml directly from GCS at the exact generation later used by the upload precondition; it no longer merges a public HTTP snapshot. The integration fixture also exercises a concurrent generation change.

@rusenask
rusenask merged commit d693c08 into master Sep 9, 2026
3 checks passed
@rusenask
rusenask deleted the feat/chart-release-pipeline branch September 9, 2026 14:47
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.

1 participant