Skip to content

Automate SpiceDB Operator updates#24

Open
typedrat wants to merge 4 commits into
bushelpowered:mainfrom
synapdeck:typedrat/automate-operator-updates
Open

Automate SpiceDB Operator updates#24
typedrat wants to merge 4 commits into
bushelpowered:mainfrom
synapdeck:typedrat/automate-operator-updates

Conversation

@typedrat

Copy link
Copy Markdown

Adds an update automation that codifies the manual runbook in CLAUDE.md, so routine operator bumps land as ready-to-review PRs instead of hand work.

What's included

  • scripts/update-operator.sh — deterministic, idempotent regeneration of the chart for a given operator release:
    • resolves current (Chart.yaml appVersion) vs target version (arg or latest GitHub release),
    • downloads both release bundle.yaml files,
    • regenerates templates/configmap-update-graph.yaml from the target bundle (byte-for-byte block extraction),
    • bumps Chart.yaml (minor version bump, appVersion, source URL),
    • regenerates README.md via helm-docs (supports helm-docs or nix run nixpkgs#helm-docs),
    • verifies with helm lint + helm template, and cross-checks that the rendered update-graph is byte-identical to the bundle (hard failure if not).
  • .github/workflows/update-operator.yml — daily (and workflow_dispatch) check that runs the script and, if the chart changed, opens/updates a PR via peter-evans/create-pull-request using the default GITHUB_TOKEN.
  • CLAUDE.md — documents the automation and reframes the step-by-step section as the manual/drift fallback.

Drift guard

Off-the-shelf tools (Dependabot/Renovate) can bump a version string but can't regenerate the 3,400-line update-graph ConfigMap or apply structural CRD/RBAC changes. This automation handles the common case (only the ConfigMap + templated image tag change) end to end.

When the bundle diff touches anything else (CRD / RBAC / ServiceAccount / Deployment spec), the script:

  1. still applies the safe parts (ConfigMap + version bumps),
  2. classifies the remainder as drift,
  3. the workflow labels the PR needs-manual-review, embeds the structural diff in the PR body, and fails a check so it's visible.

A human then applies the structural changes per the manual runbook before merging.

Verification (local)

  • v1.24.0 → v1.25.1: script output is byte-identical to the hand-done upgrade in Upgrade SpiceDB Operator to v1.25.1 #23 (ConfigMap, Chart.yaml, README all match).
  • No-op (already at target): exits cleanly, zero changes.
  • v1.20.1 → v1.21.0: correctly flags drift — detects the new policy/poddisruptionbudgets RBAC rule that release added.
  • shellcheck clean; workflow YAML parses.

Notes

  • Uses the same GITHUB_TOKEN mechanism as the existing release.yml. Caveat: PRs opened by GITHUB_TOKEN don't trigger other workflows — no impact today since there's no PR-triggered CI in this repo.
  • This PR is independent of Upgrade SpiceDB Operator to v1.25.1 #23 (the v1.25.1 chart bump); it only adds tooling.

typedrat added 4 commits June 15, 2026 11:57
Add scripts/update-operator.sh and a daily GitHub Action that codify the
manual update runbook from CLAUDE.md.

The script resolves the current vs target operator version, downloads both
release bundles, regenerates templates/configmap-update-graph.yaml, bumps
Chart.yaml (minor version bump, appVersion, source URL), regenerates
README.md via helm-docs, and verifies with helm lint/template plus a
byte-identical cross-check of the rendered update-graph against the bundle.

Drift guard: any bundle change outside the update-graph ConfigMap and the
templated Deployment image tag (CRD/RBAC/ServiceAccount/Deployment spec) is
classified as drift. The safe parts are still applied, but the PR is labeled
needs-manual-review with the structural diff embedded, and a check fails so a
human finishes the structural changes before merge.

The workflow runs daily and on workflow_dispatch, authenticates with the
default GITHUB_TOKEN, and opens/updates the PR via peter-evans/create-pull-request.

Verified locally: reproducing the v1.24.0 -> v1.25.1 sync produces output
byte-identical to the hand-done upgrade, the no-op case (already at target)
makes no changes, and the v1.20.1 -> v1.21.0 case correctly flags drift
(new policy/poddisruptionbudgets RBAC rule).
Add a lightweight 'Check for update' step right after checkout that parses
the chart's current appVersion and resolves the target version (workflow
input or latest operator release), then compares them. When they match, the
job short-circuits: Helm/helm-docs installation, bundle downloads, and the
full sync script are all skipped via 'if: steps.check.outputs.has_update'.

The resolved target is passed explicitly into the script so the heavy step
targets the exact version the check decided on (no second API call, no race
if a release lands between steps). Downstream PR steps already gate on
steps.update.outputs.updated, which is empty when the update step is skipped,
so the no-op path ends green with no PR.
GitHub-hosted runners ship yq and gh preinstalled. Replace the grep/sed
appVersion parse with 'yq .appVersion' and the curl+grep/sed releases API
call with 'gh release view --json tagName'. gh auto-authenticates from
GITHUB_TOKEN in the environment.

The standalone scripts/update-operator.sh keeps its curl-based resolution so
it has no dependency on gh being installed/authed for local runs.
The workflow runs on GitHub-hosted runners, so assume curl/helm/yq/gh are
available and use the right tool for each job:

- read appVersion/version with 'yq .field' instead of grep/sed
- resolve the latest release with 'gh release view' instead of curl + grep/sed
  on the REST API (gh auto-authenticates from GITHUB_TOKEN)
- drop the nix helm-docs fallback (CI installs the binary)

Deliberately KEPT as line-targeted sed:
- update-graph extraction (awk verbatim capture): yq reserializes the block
  scalar, reordering entries and changing whitespace, which breaks the
  byte-identical-to-bundle guarantee the cross-check enforces.
- Chart.yaml field edits: 'yq -i' reflows the whole document (re-indents the
  sources list, strips blank lines between commented sections), producing a
  noisy diff. sed touches only the three intended lines.

Added a comment on the Chart.yaml edit explaining why, so it isn't 'cleaned
up' into yq -i later. Verified the refactored script still produces output
byte-identical to the hand-done v1.25.1 upgrade (all three files); shellcheck
clean.
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