This is an automated email from the ASF dual-hosted git repository.

villebro pushed a commit to branch main
in repository 
https://gitbox.apache.org/repos/asf/superset-kubernetes-operator.git


The following commit(s) were added to refs/heads/main by this push:
     new 6703c0f  ci(deps): matrix e2e across supported Kubernetes versions 
(#77)
6703c0f is described below

commit 6703c0f9f0908f5bb720b43668f6362390e2ec2f
Author: Ville Brofeldt <[email protected]>
AuthorDate: Tue May 26 11:11:30 2026 -0700

    ci(deps): matrix e2e across supported Kubernetes versions (#77)
    
    * ci(test): matrix e2e across supported Kubernetes versions
    
    * improve tooling for automatically bumping versions
    
    * improve automation
---
 .asf.yaml                                 |   1 +
 .github/supported-k8s.json                |  16 +++++
 .github/workflows/ci.yaml                 |  10 +++
 .github/workflows/sync-supported-k8s.yaml |  66 ++++++++++++++++++
 .github/workflows/test.yaml               |  60 ++++++++++++++++-
 .rat-excludes                             |   1 +
 AGENTS.md                                 |  15 +++++
 Makefile                                  |  24 ++++++-
 README.md                                 |  15 ++++-
 docs/index.md                             |  13 ++++
 docs/user-guide/installation.md           |  15 ++++-
 renovate.json                             |  30 ++++++++-
 scripts/render-supported-versions.sh      |  68 +++++++++++++++++++
 scripts/sync-supported-versions.sh        | 108 ++++++++++++++++++++++++++++++
 14 files changed, 434 insertions(+), 8 deletions(-)

diff --git a/.asf.yaml b/.asf.yaml
index b37ac5c..e67329f 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -60,6 +60,7 @@ github:
           - Build
           - Lint
           - Verify codegen
+          - Verify supported-versions table
           - Docker build
           - Helm lint
           - Unit & Integration
diff --git a/.github/supported-k8s.json b/.github/supported-k8s.json
new file mode 100644
index 0000000..cb5a13d
--- /dev/null
+++ b/.github/supported-k8s.json
@@ -0,0 +1,16 @@
+{
+  "supported": [
+    {
+      "minor": "1.35",
+      "node_image": 
"kindest/node:v1.35.0@sha256:452d707d4862f52530247495d180205e029056831160e22870e37e3f6c1ac31f"
+    },
+    {
+      "minor": "1.34",
+      "node_image": 
"kindest/node:v1.34.3@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48"
+    }
+  ],
+  "next": {
+    "minor": "1.36",
+    "version": "v1.36.1"
+  }
+}
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 988c4b0..5b9e7d8 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -65,6 +65,16 @@ jobs:
             exit 1
           fi
 
+  verify-supported-versions:
+    name: Verify supported-versions table
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
+      - name: Verify supported-k8s.json matches kind release and docs are in 
sync
+        run: make verify-supported-versions
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
   docker:
     name: Docker build
     runs-on: ubuntu-latest
diff --git a/.github/workflows/sync-supported-k8s.yaml 
b/.github/workflows/sync-supported-k8s.yaml
new file mode 100644
index 0000000..7494674
--- /dev/null
+++ b/.github/workflows/sync-supported-k8s.yaml
@@ -0,0 +1,66 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: Sync supported Kubernetes versions
+
+on:
+  schedule:
+    - cron: '0 6 * * *'   # daily at 06:00 UTC
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+jobs:
+  sync:
+    name: Sync supported-k8s.json
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+      pull-requests: write
+    steps:
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
+      - name: Sync supported-k8s.json and regenerate docs
+        run: make sync-supported-versions
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      - name: Open or update PR if anything changed
+        uses: 
peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # 
v8.1.1
+        with:
+          branch: chore/sync-supported-k8s
+          base: main
+          commit-message: |
+            chore(ci): sync supported Kubernetes versions
+
+            Auto-generated by .github/workflows/sync-supported-k8s.yaml.
+          title: 'chore(ci): sync supported Kubernetes versions'
+          body: |
+            Automated sync of `.github/supported-k8s.json` and the generated
+            supported-versions blocks in `README.md` and the docs.
+
+            The sync script (`scripts/sync-supported-versions.sh`) recomputes:
+
+            - `supported` — newest two Kubernetes minors with a `kindest/node`
+              image published by the currently pinned kind release.
+            - `next` — newest stable Kubernetes release if its minor isn't
+              already covered by kind; otherwise `null`.
+
+            **Heads up:** PRs opened by `GITHUB_TOKEN` don't trigger CI.
+            Close + reopen this PR (or push an empty commit) to run the
+            required checks before merging.
+          labels: |
+            dependencies
+            review:supported-k8s
+          delete-branch: true
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index a217dac..9f959ba 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -24,6 +24,11 @@ on:
 permissions:
   contents: read
 
+env:
+  # kind version + checksum are bumped in lockstep by Renovate (see 
renovate.json).
+  KIND_VERSION: v0.31.0
+  KIND_CHECKSUM: 
eb244cbafcc157dff60cf68693c14c9a75c4e6e6fedaf9cd71c58117cb93e3fa
+
 jobs:
   unit-integration:
     name: Unit & Integration
@@ -50,9 +55,55 @@ jobs:
           flags: integration
           token: ${{ secrets.CODECOV_TOKEN }}
 
+  e2e-matrix:
+    name: E2E matrix
+    runs-on: ubuntu-latest
+    outputs:
+      supported: ${{ steps.load.outputs.supported }}
+      next_version: ${{ steps.load.outputs.next_version }}
+    steps:
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
+      - id: load
+        run: |
+          {
+            echo "supported=$(jq -c .supported .github/supported-k8s.json)"
+            echo "next_version=$(jq -r '.next.version // ""' 
.github/supported-k8s.json)"
+          } >> "$GITHUB_OUTPUT"
+
   e2e:
-    name: E2E
+    name: E2E (${{ matrix.k8s.minor }})
+    needs: e2e-matrix
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        k8s: ${{ fromJSON(needs.e2e-matrix.outputs.supported) }}
+    steps:
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
+      - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # 
v6.4.0
+        with:
+          go-version-file: go.mod
+      - name: Install kind
+        run: |
+          curl -fsSLo ./kind 
"https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64";
+          echo "${KIND_CHECKSUM}  ./kind" | sha256sum -c -
+          chmod +x ./kind
+          sudo mv ./kind /usr/local/bin/kind
+          kind version
+      - name: Run E2E tests
+        run: make test-e2e
+        env:
+          KIND_NODE_IMAGE: ${{ matrix.k8s.node_image }}
+          KIND_CLUSTER: superset-op-e2e-${{ matrix.k8s.minor }}
+
+  e2e-next:
+    name: E2E (next, best-effort)
+    needs: e2e-matrix
+    if: needs.e2e-matrix.outputs.next_version != ''
     runs-on: ubuntu-latest
+    continue-on-error: true
+    env:
+      K8S_NEXT: ${{ needs.e2e-matrix.outputs.next_version }}
     steps:
       - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
       - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # 
v6.4.0
@@ -60,12 +111,15 @@ jobs:
           go-version-file: go.mod
       - name: Install kind
         run: |
-          KIND_VERSION="v0.31.0"
-          
KIND_CHECKSUM="eb244cbafcc157dff60cf68693c14c9a75c4e6e6fedaf9cd71c58117cb93e3fa"
           curl -fsSLo ./kind 
"https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64";
           echo "${KIND_CHECKSUM}  ./kind" | sha256sum -c -
           chmod +x ./kind
           sudo mv ./kind /usr/local/bin/kind
           kind version
+      - name: Build node image for newest Kubernetes release
+        run: kind build node-image --type release "${K8S_NEXT}" --image 
"kindest/node:${K8S_NEXT}-local"
       - name: Run E2E tests
         run: make test-e2e
+        env:
+          KIND_NODE_IMAGE: kindest/node:${{ env.K8S_NEXT }}-local
+          KIND_CLUSTER: superset-op-e2e-next
diff --git a/.rat-excludes b/.rat-excludes
index 9de36c4..5fd4242 100644
--- a/.rat-excludes
+++ b/.rat-excludes
@@ -47,3 +47,4 @@
 .*rat-results\.txt
 .*Dockerfile\.cross
 .*codecov\.yml
+.*supported-k8s\.json
diff --git a/AGENTS.md b/AGENTS.md
index ba11822..ea8f82e 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -137,6 +137,21 @@ All components use the reserved container name `superset` 
for the main container
 
 Superset CR names are validated via CEL to be valid DNS labels (lowercase 
alphanumeric and hyphens, `^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`, max 63 
characters). DNS-label syntax is required because the operator derives Service 
names from the parent name + component suffix. Per-component CEL rules enforce 
that the parent name is short enough for each enabled component's suffix to fit 
within the 63-char Service name limit (e.g., `-websocket-server` = 17 chars 
limits parent to 46).
 
+## Automation Principle
+
+When committed state must stay in sync with something external (upstream 
releases, generated docs, tables derived from a source of truth), prefer 
**building a CI-enforced sync mechanism** over documenting a manual checklist. 
Manual steps rot; CI failures don't.
+
+Concrete patterns to mirror:
+
+- **One source of truth, generated outputs.** Keep canonical data in a single 
small file (`.github/supported-k8s.json`, Go type definitions, etc.) and derive 
everything else — docs tables, CI matrices, defaults — from it.
+- **`make codegen` aggregates all generators.** Anything regenerable goes 
through it. CI's `Verify codegen` job runs `make codegen` and fails on diff.
+- **Upstream drift checks.** When the source-of-truth file itself must track 
something external (a pinned dependency's release notes, the latest upstream 
minor), write a `make sync-<thing>` script that queries the upstream API and 
rewrites the file, plus a `make verify-<thing>` that runs it in `--check` mode 
in CI. See `scripts/sync-supported-versions.sh` for the canonical example.
+- **Scheduled auto-sync PRs.** Pair the sync script with a daily scheduled 
GitHub Actions job that runs it and opens a PR if anything changed. Removes the 
"every open PR breaks until someone syncs" failure mode when upstream ticks.
+- **Renovate for pinned versions.** Use `customManagers` for non-standard pin 
sites (regex-matched JSON/YAML fields). Use `prBodyNotes` + `addLabels` to 
steer the reviewer toward the right follow-up command on PRs that require 
manual judgment.
+- **Sentinel-bounded generated blocks** in human-edited files (`<!-- BEGIN X 
-->` / `<!-- END X -->`) so generators can rewrite specific regions without 
disturbing surrounding hand-written content.
+
+The bar: every new "remember to also update X when Y changes" instruction is a 
bug in the tooling. Fix the tooling instead.
+
 ## PR Conventions
 
 - **Title format**: `type(scope): description` or `type: description` — 
enforced by CI (`amannn/action-semantic-pull-request`). Scope is optional but 
encouraged.
diff --git a/Makefile b/Makefile
index 71382d6..7e17781 100644
--- a/Makefile
+++ b/Makefile
@@ -128,6 +128,25 @@ docs-api: crd-ref-docs ## Generate API reference 
documentation from Go types.
        $(CRD_REF_DOCS) --source-path=api/v1alpha1 
--config=hack/api-ref-config.yaml --renderer=markdown --max-depth=15 
--output-path=docs/reference/api-reference.md
        sed -f hack/fix-api-ref-links.sed docs/reference/api-reference.md > 
docs/reference/api-reference.md.tmp && mv docs/reference/api-reference.md.tmp 
docs/reference/api-reference.md
 
+.PHONY: supported-versions
+supported-versions: ## Regenerate the supported-Kubernetes-versions table in 
README.md and docs.
+       ./scripts/render-supported-versions.sh
+
+.PHONY: sync-supported-versions
+sync-supported-versions: ## Sync .github/supported-k8s.json with the pinned 
kind release's node images.
+       ./scripts/sync-supported-versions.sh --write
+       $(MAKE) supported-versions
+
+.PHONY: verify-supported-versions
+verify-supported-versions: ## Verify supported-k8s.json matches the pinned 
kind release and docs are up to date.
+       ./scripts/sync-supported-versions.sh --check
+       $(MAKE) supported-versions
+       @if [ -n "$$(git status --porcelain README.md docs/index.md 
docs/user-guide/installation.md)" ]; then \
+               echo "Supported-versions table is stale. Run 'make 
supported-versions' and commit."; \
+               git diff README.md docs/index.md 
docs/user-guide/installation.md; \
+               exit 1; \
+       fi
+
 ##@ Helm
 
 HELM_CHART_DIR ?= charts/superset-operator
@@ -148,7 +167,7 @@ helm-lint: helm-sync-crds ## Lint the Helm chart (syncs 
CRDs first).
 ##@ Development
 
 .PHONY: codegen
-codegen: manifests generate helm-sync-crds docs-api ## Regenerate all 
generated artifacts (CRDs, DeepCopy, Helm CRDs, API docs).
+codegen: manifests generate helm-sync-crds docs-api supported-versions ## 
Regenerate all generated artifacts (CRDs, DeepCopy, Helm CRDs, API docs, 
supported-versions table).
 
 .PHONY: clean
 clean: ## Remove build artifacts, downloaded tools, and test cache.
@@ -188,7 +207,8 @@ test-integration: manifests generate fmt vet setup-envtest 
## Run integration te
 # CertManager is installed by default; skip with:
 # - CERT_MANAGER_INSTALL_SKIP=true
 KIND_CLUSTER ?= superset-kubernetes-operator-test-e2e
-KIND_NODE_IMAGE ?= kindest/node:v1.35.1
+# Keep in sync with the first entry of .github/supported-k8s.json (Renovate 
manages the digest).
+KIND_NODE_IMAGE ?= 
kindest/node:v1.35.0@sha256:452d707d4862f52530247495d180205e029056831160e22870e37e3f6c1ac31f
 
 .PHONY: setup-test-e2e
 setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist
diff --git a/README.md b/README.md
index 7ecd5ff..350da4f 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,19 @@ A Kubernetes operator for deploying and managing [Apache 
Superset](https://super
 
 The operator manages the full Superset lifecycle: database migrations, 
configuration rendering, component deployment, scaling, and networking. 
Defaults are tuned for typical deployments; presets, deployment-template 
fields, and a raw Python escape hatch let you override them where needed.
 
+## Supported Kubernetes versions
+
+<!-- BEGIN SUPPORTED-K8S -->
+<!-- generated by `make supported-versions`; do not edit -->
+- **Officially tested:** Kubernetes 1.35, 1.34
+- **Experimental (best-effort):** Kubernetes 1.36
+<!-- END SUPPORTED-K8S -->
+
+Official support covers the two most recent Kubernetes minor versions with a
+published [kind](https://kind.sigs.k8s.io/) node image. The newest Kubernetes
+release gets best-effort coverage via a non-blocking CI lane until kind ships
+its node image.
+
 ## Quick Start
 
 Install the operator via Helm:
@@ -78,4 +91,4 @@ After editing type definitions in `api/v1alpha1/`, run `make 
manifests generate`
 
 ## License
 
-Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for 
details.
\ No newline at end of file
+Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for 
details.
diff --git a/docs/index.md b/docs/index.md
index 2378ff6..e7f227d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -38,6 +38,19 @@ The operator manages the full Superset lifecycle: database 
migrations, configura
 - **Scaling and resilience** — HPA with custom metrics, PodDisruptionBudgets, 
NetworkPolicies, Prometheus ServiceMonitor
 - **Flexible install scope** — cluster-scoped (default) or namespace-scoped; 
the namespace-scoped Helm install renders no manager `ClusterRole` at all, so 
it works on restricted clusters that forbid cluster-scoped RBAC
 
+## Supported Kubernetes versions
+
+<!-- BEGIN SUPPORTED-K8S -->
+<!-- generated by `make supported-versions`; do not edit -->
+- **Officially tested:** Kubernetes 1.35, 1.34
+- **Experimental (best-effort):** Kubernetes 1.36
+<!-- END SUPPORTED-K8S -->
+
+Official support covers the two most recent Kubernetes minor versions with a
+published [kind](https://kind.sigs.k8s.io/) node image. The newest Kubernetes
+release gets best-effort coverage via a non-blocking CI lane until kind ships
+its node image.
+
 ## What it looks like
 
 A typical Superset deployment for getting started in dev mode:
diff --git a/docs/user-guide/installation.md b/docs/user-guide/installation.md
index 5657093..5d6f452 100644
--- a/docs/user-guide/installation.md
+++ b/docs/user-guide/installation.md
@@ -25,13 +25,26 @@ For development setup, see [Development 
Setup](../contributing/development-setup
 
 ## Prerequisites
 
-- Kubernetes v1.28+ cluster
+- Kubernetes cluster (see [Supported versions](#supported-kubernetes-versions))
 - Helm 3 (for Helm-based installation) or `kubectl` + `kustomize`
 - PostgreSQL or MySQL database
 - (Optional) [Valkey](https://valkey.io/) (or Redis) — required when enabling 
caching, Celery task broker, or result backend
 - (Optional) [Gateway API 
CRDs](https://gateway-api.sigs.k8s.io/guides/#installing-gateway-api) for 
HTTPRoute support — not included in Kubernetes, must be installed separately
 - (Optional) prometheus-operator CRDs for ServiceMonitor support
 
+## Supported Kubernetes versions
+
+<!-- BEGIN SUPPORTED-K8S -->
+<!-- generated by `make supported-versions`; do not edit -->
+- **Officially tested:** Kubernetes 1.35, 1.34
+- **Experimental (best-effort):** Kubernetes 1.36
+<!-- END SUPPORTED-K8S -->
+
+Official support covers the two most recent Kubernetes minor versions with a
+published [kind](https://kind.sigs.k8s.io/) node image. The newest Kubernetes
+release gets best-effort coverage via a non-blocking CI lane until kind ships
+its node image.
+
 ## 1. Install the operator
 
 ```bash
diff --git a/renovate.json b/renovate.json
index 4051d9c..db18784 100644
--- a/renovate.json
+++ b/renovate.json
@@ -2,7 +2,7 @@
   "$schema": "https://docs.renovatebot.com/renovate-schema.json";,
   "extends": ["config:recommended"],
   "minimumReleaseAge": "7 days",
-  "schedule": ["before 7am on Monday"],
+  "schedule": ["before 7am"],
   "labels": ["dependencies"],
   "packageRules": [
     {
@@ -13,6 +13,34 @@
     {
       "matchManagers": ["github-actions"],
       "pinDigests": true
+    },
+    {
+      "matchPackageNames": ["kubernetes-sigs/kind"],
+      "addLabels": ["review:supported-k8s"],
+      "prBodyNotes": [
+        ":warning: After bumping kind, run `make sync-supported-versions && 
make codegen` and commit the result to update `.github/supported-k8s.json` (and 
the generated docs). The script recomputes both `supported` (newest two 
kind-published minors) and `next` (newest Kubernetes release not yet covered by 
kind). The `Verify supported-versions table` CI check will fail until this is 
done."
+      ]
+    }
+  ],
+  "customManagers": [
+    {
+      "customType": "regex",
+      "description": "Track kindest/node image digests in 
.github/supported-k8s.json",
+      "managerFilePatterns": ["/^\\.github/supported-k8s\\.json$/"],
+      "matchStrings": [
+        
"\"node_image\":\\s*\"(?<depName>kindest/node):(?<currentValue>[^@\"]+)@(?<currentDigest>sha256:[a-f0-9]+)\""
+      ],
+      "datasourceTemplate": "docker"
+    },
+    {
+      "customType": "regex",
+      "description": "Track sigs.k8s.io/kind release pin and matching sha256 
in workflows",
+      "managerFilePatterns": ["/^\\.github/workflows/.+\\.ya?ml$/"],
+      "matchStrings": [
+        
"KIND_VERSION:\\s*(?<currentValue>v[0-9.]+)\\s*\\n\\s*KIND_CHECKSUM:\\s*(?<currentDigest>[a-f0-9]+)"
+      ],
+      "datasourceTemplate": "github-releases",
+      "depNameTemplate": "kubernetes-sigs/kind"
     }
   ]
 }
diff --git a/scripts/render-supported-versions.sh 
b/scripts/render-supported-versions.sh
new file mode 100755
index 0000000..ca7456e
--- /dev/null
+++ b/scripts/render-supported-versions.sh
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Render the supported-Kubernetes-versions block from 
.github/supported-k8s.json
+# into README.md and the docs, between sentinel comments:
+#
+#   <!-- BEGIN SUPPORTED-K8S -->
+#   ... generated block ...
+#   <!-- END SUPPORTED-K8S -->
+
+set -euo pipefail
+
+REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+SOURCE="${REPO_ROOT}/.github/supported-k8s.json"
+TARGETS=(
+  "${REPO_ROOT}/README.md"
+  "${REPO_ROOT}/docs/index.md"
+  "${REPO_ROOT}/docs/user-guide/installation.md"
+)
+
+command -v jq >/dev/null || { echo "jq is required" >&2; exit 1; }
+
+supported="$(jq -r '[.supported[].minor] | join(", ")' "${SOURCE}")"
+next="$(jq -r '.next.minor // empty' "${SOURCE}")"
+
+block="$({
+  echo '<!-- BEGIN SUPPORTED-K8S -->'
+  echo '<!-- generated by `make supported-versions`; do not edit -->'
+  echo "- **Officially tested:** Kubernetes ${supported}"
+  if [ -n "${next}" ]; then
+    echo "- **Experimental (best-effort):** Kubernetes ${next}"
+  fi
+  echo '<!-- END SUPPORTED-K8S -->'
+})"
+
+for file in "${TARGETS[@]}"; do
+  if ! grep -q '<!-- BEGIN SUPPORTED-K8S -->' "${file}"; then
+    echo "no sentinel block found in ${file}" >&2
+    exit 1
+  fi
+  tmp="$(mktemp)"
+  BLOCK="${block}" awk '
+    /<!-- BEGIN SUPPORTED-K8S -->/ {
+      print ENVIRON["BLOCK"]
+      skip = 1
+      next
+    }
+    /<!-- END SUPPORTED-K8S -->/ {
+      skip = 0
+      next
+    }
+    !skip { print }
+  ' "${file}" > "${tmp}"
+  mv "${tmp}" "${file}"
+done
diff --git a/scripts/sync-supported-versions.sh 
b/scripts/sync-supported-versions.sh
new file mode 100755
index 0000000..91bf94b
--- /dev/null
+++ b/scripts/sync-supported-versions.sh
@@ -0,0 +1,108 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Sync .github/supported-k8s.json with the kindest/node images published
+# by the currently pinned kind release (read from .github/workflows/test.yaml),
+# and compute `next` from upstream Kubernetes releases:
+#
+#   - `supported` = the two newest Kubernetes minors that the pinned kind
+#     release ships node images for (highest patch per minor).
+#   - `next`      = {minor, version} of the newest stable Kubernetes release if
+#                   its minor isn't already in `supported`; otherwise null.
+#
+# Usage:
+#   sync-supported-versions.sh [--check|--write]
+#
+# --check (default): exit non-zero with a diff if the JSON is out of sync.
+# --write:           rewrite the JSON in place.
+#
+# Set GITHUB_TOKEN to avoid GitHub API rate limits (60 req/hr unauth).
+
+set -euo pipefail
+
+REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+SOURCE="${REPO_ROOT}/.github/supported-k8s.json"
+WORKFLOW="${REPO_ROOT}/.github/workflows/test.yaml"
+
+mode="${1:---check}"
+case "${mode}" in --check|--write) ;; *) echo "usage: $0 [--check|--write]" 
>&2; exit 2 ;; esac
+
+command -v jq >/dev/null   || { echo "jq required" >&2; exit 1; }
+command -v curl >/dev/null || { echo "curl required" >&2; exit 1; }
+
+KIND_VERSION="$(awk '/^[[:space:]]*KIND_VERSION:/ {print $2; exit}' 
"${WORKFLOW}")"
+[ -n "${KIND_VERSION}" ] || { echo "could not read KIND_VERSION from 
${WORKFLOW}" >&2; exit 1; }
+
+api="https://api.github.com/repos/kubernetes-sigs/kind/releases/tags/${KIND_VERSION}";
+auth=()
+[ -n "${GITHUB_TOKEN:-}" ] && auth=(-H "Authorization: Bearer ${GITHUB_TOKEN}")
+
+body="$(curl -fsSL ${auth[@]+"${auth[@]}"} -H 'Accept: 
application/vnd.github+json' "${api}" | jq -r .body)"
+
+# Extract the highest-patch node image per Kubernetes minor, then take the
+# top two minors. Format per row: "MINOR FULL_IMAGE".
+top2="$(printf '%s\n' "${body}" \
+  | grep -oE 'kindest/node:v[0-9]+\.[0-9]+\.[0-9]+@sha256:[a-f0-9]{64}' \
+  | sort -u \
+  | sed -E 's|^kindest/node:v([0-9]+\.[0-9]+)\.([0-9]+)@.*|\1 \2 &|' \
+  | sort -k1,1V -k2,2n \
+  | awk '{ best[$1] = $3 } END { for (k in best) print k" "best[k] }' \
+  | sort -k1,1Vr \
+  | head -2)"
+
+[ -n "${top2}" ] || { echo "no kindest/node images found in release notes for 
${KIND_VERSION}" >&2; exit 1; }
+[ "$(printf '%s\n' "${top2}" | wc -l | tr -d ' ')" -eq 2 ] \
+  || { echo "expected 2 minor versions, got:" >&2; printf '%s\n' "${top2}" 
>&2; exit 1; }
+
+new_supported="$(printf '%s\n' "${top2}" \
+  | jq -R -s 'split("\n") | map(select(length>0)) | map(split(" ") | {minor: 
.[0], node_image: .[1]})')"
+
+# Determine the newest stable Kubernetes release (skip prereleases/RCs).
+k8s_api='https://api.github.com/repos/kubernetes/kubernetes/releases?per_page=30'
+newest_k8s="$(curl -fsSL ${auth[@]+"${auth[@]}"} -H 'Accept: 
application/vnd.github+json' "${k8s_api}" \
+  | jq -r '[.[] | select(.prerelease == false) | .tag_name | 
select(test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))][0] // empty')"
+[ -n "${newest_k8s}" ] || { echo "could not determine newest Kubernetes 
release" >&2; exit 1; }
+newest_k8s_minor="$(printf '%s' "${newest_k8s}" | sed -E 
's|^v([0-9]+\.[0-9]+).*|\1|')"
+
+new_json="$(
+  jq --argjson sup "${new_supported}" \
+     --arg     k8sMinor "${newest_k8s_minor}" \
+     --arg     k8sVersion "${newest_k8s}" '
+    def to_v: split(".") | map(tonumber);
+    (.supported = $sup)
+    | ([.supported[].minor | to_v] | max) as $topSupported
+    | ($k8sMinor | to_v) as $k8s
+    | if $k8s > $topSupported
+        then .next = {minor: $k8sMinor, version: $k8sVersion}
+        else .next = null
+      end
+  ' "${SOURCE}"
+)"
+
+case "${mode}" in
+  --write)
+    printf '%s\n' "${new_json}" > "${SOURCE}"
+    echo "updated ${SOURCE} from kind ${KIND_VERSION}"
+    ;;
+  --check)
+    if ! diff -u <(jq -S . "${SOURCE}") <(printf '%s\n' "${new_json}" | jq -S 
.); then
+      echo >&2
+      echo "supported-k8s.json is out of sync with kind ${KIND_VERSION}." >&2
+      echo "Run 'make sync-supported-versions' to update." >&2
+      exit 1
+    fi
+    ;;
+esac

Reply via email to