Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions .github/workflows/update-operator.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
name: Update SpiceDB Operator

# Checks daily for a new SpiceDB Operator release and opens a PR that syncs the
# chart (regenerated update-graph ConfigMap, bumped Chart.yaml, regenerated
# README). When the upstream bundle changes anything beyond the update-graph and
# the Deployment image tag (CRD / RBAC / ServiceAccount / Deployment spec), the
# PR is labeled "needs-manual-review" and the structural diff is attached so a
# human can finish the update.
#
# See scripts/update-operator.sh for the codified runbook.

on:
schedule:
# Daily at 07:00 UTC.
- cron: "0 7 * * *"
workflow_dispatch:
inputs:
target_version:
description: "Operator release tag to sync to (e.g. v1.25.1). Leave blank for latest."
required: false
type: string

permissions:
contents: write
pull-requests: write

jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

# Cheap early exit: compare the chart's current appVersion against the
# target (the workflow input, or the latest operator release). If they
# already match there is nothing to do, so we skip installing Helm /
# helm-docs, downloading bundles, and running the full sync.
- name: Check for update
id: check
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
current="$(yq '.appVersion' charts/spicedb-operator/Chart.yaml)"
[ -n "$current" ] && [ "$current" != "null" ] \
|| { echo "::error::could not parse appVersion"; exit 1; }

target="${{ inputs.target_version }}"
if [ -z "$target" ]; then
target="$(gh release view \
--repo authzed/spicedb-operator \
--json tagName --jq '.tagName')"
[ -n "$target" ] || { echo "::error::could not resolve latest release tag"; exit 1; }
fi

echo "current_version=$current" >> "$GITHUB_OUTPUT"
echo "target_version=$target" >> "$GITHUB_OUTPUT"

if [ "$current" = "$target" ]; then
echo "has_update=false" >> "$GITHUB_OUTPUT"
echo "Chart is already at ${target}; nothing to do."
else
echo "has_update=true" >> "$GITHUB_OUTPUT"
echo "Update available: ${current} -> ${target}."
fi

- name: Install Helm
if: steps.check.outputs.has_update == 'true'
uses: azure/setup-helm@v4

- name: Install helm-docs
if: steps.check.outputs.has_update == 'true'
run: |
HELM_DOCS_VERSION=1.14.2
curl -fsSL "https://github.com/norwoodj/helm-docs/releases/download/v${HELM_DOCS_VERSION}/helm-docs_${HELM_DOCS_VERSION}_Linux_x86_64.tar.gz" \
| sudo tar -xz -C /usr/local/bin helm-docs
helm-docs --version

- name: Run update script
if: steps.check.outputs.has_update == 'true'
id: update
env:
# Used by the script to authenticate the GitHub releases API call and
# avoid unauthenticated rate limits.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WORKDIR: ${{ runner.temp }}/operator-update
run: |
./scripts/update-operator.sh "${{ steps.check.outputs.target_version }}"

- name: Build PR body
if: steps.update.outputs.updated == 'true'
id: body
env:
CURRENT_VERSION: ${{ steps.update.outputs.current_version }}
TARGET_VERSION: ${{ steps.update.outputs.target_version }}
CHART_VERSION: ${{ steps.update.outputs.chart_version }}
DRIFT: ${{ steps.update.outputs.drift }}
WORKDIR: ${{ runner.temp }}/operator-update
run: |
BODY_FILE="${RUNNER_TEMP}/pr-body.md"
{
echo "Automated sync of the chart to SpiceDB Operator [\`${TARGET_VERSION}\`](https://github.com/authzed/spicedb-operator/releases/tag/${TARGET_VERSION})."
echo
echo "Generated by \`.github/workflows/update-operator.yml\` (\`scripts/update-operator.sh\`)."
echo
echo "## Changes"
echo "- \`Chart.yaml\`: appVersion \`${CURRENT_VERSION}\` → \`${TARGET_VERSION}\`, chart version → \`${CHART_VERSION}\`."
echo "- \`templates/configmap-update-graph.yaml\`: regenerated from the \`${TARGET_VERSION}\` bundle."
echo "- \`README.md\`: regenerated via helm-docs."
echo
echo "## Verification"
echo "- \`helm lint\` and \`helm template\` pass in the workflow."
echo "- The rendered update-graph is verified byte-identical to the upstream bundle."
echo

if [ "${DRIFT}" = "true" ]; then
echo "## :warning: Manual review required"
echo
echo "The upstream bundle changed resources **beyond** the update-graph ConfigMap and the Deployment image tag (CRD / RBAC / ServiceAccount / Deployment spec). The script applied the safe parts automatically, but a human must review and apply the structural changes below to the relevant templates before merging."
echo
echo "<details><summary>Structural bundle diff (current → target)</summary>"
echo
echo '```diff'
# Cap the embedded diff so the PR body stays within GitHub limits.
head -c 50000 "${WORKDIR}/drift-report.txt"
echo
echo '```'
echo
echo "</details>"
else
echo "## No structural drift"
echo
echo "Only the update-graph ConfigMap and the templated image tag changed between \`${CURRENT_VERSION}\` and \`${TARGET_VERSION}\`. No CRD / RBAC / ServiceAccount / Deployment-spec changes were needed."
fi
} > "$BODY_FILE"
echo "path=$BODY_FILE" >> "$GITHUB_OUTPUT"

if [ "${DRIFT}" = "true" ]; then
echo "labels=automated,dependencies,needs-manual-review" >> "$GITHUB_OUTPUT"
else
echo "labels=automated,dependencies" >> "$GITHUB_OUTPUT"
fi

- name: Create Pull Request
if: steps.update.outputs.updated == 'true'
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: automation/upgrade-spicedb-operator-to-${{ steps.update.outputs.target_version }}
base: main
commit-message: |
Upgrade SpiceDB Operator to ${{ steps.update.outputs.target_version }}

Automated sync of the chart to the ${{ steps.update.outputs.target_version }}
release bundle. See scripts/update-operator.sh.
title: "Upgrade SpiceDB Operator to ${{ steps.update.outputs.target_version }}"
body-path: ${{ steps.body.outputs.path }}
labels: ${{ steps.body.outputs.labels }}
delete-branch: true

- name: Fail on drift
# Surface drift as a failed check so it is visible in the PR/run list,
# even though the PR is still opened with the safe parts applied.
if: steps.update.outputs.updated == 'true' && steps.update.outputs.drift == 'true'
run: |
echo "::error::Structural drift detected — the opened PR needs manual review before merge."
exit 1
25 changes: 24 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,30 @@ helm-docs

This chart is derived from the `bundle.yaml` files published in the [SpiceDB Operator releases](https://github.com/authzed/spicedb-operator/releases). To update the chart, you diff the old and new bundle.yaml files and apply the changes to the Helm templates.

### Step-by-step process
### Automated updates

Most updates are handled automatically. The `.github/workflows/update-operator.yml` workflow runs daily (and on-demand via `workflow_dispatch`), checks for a new operator release, and — if the chart is behind — runs `scripts/update-operator.sh` to:

- regenerate `templates/configmap-update-graph.yaml` from the new bundle,
- bump `Chart.yaml` (`version` minor bump, `appVersion`, source URL),
- regenerate `README.md` via helm-docs,
- verify with `helm lint` / `helm template` (and confirm the rendered update-graph is byte-identical to the bundle),

then opens a PR via `peter-evans/create-pull-request`.

The script encodes the manual runbook below for the common case (only the update-graph ConfigMap and the templated Deployment image tag change). **Drift guard:** if the bundle diff touches anything else (CRD / RBAC / ServiceAccount / Deployment spec), the script still applies the safe parts but marks the PR `needs-manual-review`, embeds the structural diff in the PR body, and fails a check. In that case, follow the manual process below to apply the structural changes to the relevant templates before merging.

You can also run the script locally:

```shell
# Sync to the latest release
scripts/update-operator.sh

# Sync to a specific release
scripts/update-operator.sh v1.25.1
```

### Step-by-step process (manual / drift cases)

1. **Determine the current version.** Read the `appVersion` field from `charts/spicedb-operator/Chart.yaml`. This is the SpiceDB Operator release the chart currently targets (e.g., `v1.23.0`).

Expand Down
Loading