Skip to content

Fix Cedar upstream-claim evaluation on VirtualMCPServer #21

Fix Cedar upstream-claim evaluation on VirtualMCPServer

Fix Cedar upstream-claim evaluation on VirtualMCPServer #21

Workflow file for this run

name: API Compatibility
# This workflow guards the stability of the v1beta1 operator API surface.
#
# Phase 1 (current): advisory only. `crd-schema-check` runs with
# continue-on-error: true so breaking-change findings are surfaced via the
# job's step summary but do not block merges. Remove continue-on-error in
# Phase 2 to start enforcing.
on:
pull_request:
paths:
- 'cmd/thv-operator/api/**'
# files/crds is the source of truth — controller-gen emits here, and
# crd-helm-wrapper copies from here into templates/. Any drift in
# templates/ is caught by operator-ci.yml's generate-crds job, so
# watching templates/ would be redundant. values.yaml and the
# crd-helm-wrapper only affect Helm conditionals and annotations the
# checker ignores; the workflow's explicit --set flags force every
# CRD to render regardless of those files.
- 'deploy/charts/operator-crds/files/crds/**'
# Self-exercise the workflow when the workflow itself changes.
- '.github/workflows/api-compat.yml'
permissions:
contents: read
jobs:
crd-schema-check:
name: CRD Schema Compatibility
runs-on: ubuntu-latest
# Phase 1: advisory only. Remove in Phase 2 to enforce.
continue-on-error: true
# Expected runtime is ~3 minutes (checkout + go/helm setup + helm pull +
# go install + two renders + checker). 10 minutes is a cheap upper bound
# that protects against a hung helm pull or go install without being
# disruptive to legitimate slow runs.
timeout-minutes: 10
steps:
- name: Checkout PR HEAD
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version: 'stable'
cache: true
- name: Set up Helm
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1
- name: Resolve baseline source
id: baseline
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
# Baseline is always the most recent published release chart from
# OCI. If the pull fails (no releases, OCI outage, or the chart
# for the most recent tag is not yet published), the job fails —
# an API-touching PR must be checked against a real released
# baseline or not at all. Falling back to origin/main was
# rejected in review: it can silently compare against an
# already-broken baseline once a break lands on main.
LATEST_TAG="$(gh release list --repo "$GITHUB_REPOSITORY" --limit 1 --json tagName --jq '.[0].tagName')"
if [ -z "$LATEST_TAG" ]; then
echo "::error::No releases found for $GITHUB_REPOSITORY; cannot establish an API compatibility baseline."
exit 1
fi
LATEST_VERSION="${LATEST_TAG#v}"
echo "Pulling published chart for $LATEST_TAG..."
mkdir -p /tmp/baseline-release
helm pull "oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds" \
--version "$LATEST_VERSION" \
--untar --untardir /tmp/baseline-release
echo "source=release $LATEST_TAG" >> "$GITHUB_OUTPUT"
echo "chart-dir=/tmp/baseline-release/toolhive-operator-crds" >> "$GITHUB_OUTPUT"
- name: Render baseline CRDs
env:
# Route step outputs through env vars so bash quotes them instead
# of the runner substituting them directly into the script body.
# Current values are hardcoded literals, so not injectable today —
# this is defense-in-depth against a future edit that routes a
# PR-controlled string through these outputs.
CHART_DIR: ${{ steps.baseline.outputs.chart-dir }}
run: |
set -euo pipefail
mkdir -p /tmp/baseline-crds
# All three feature flags must be set so the Helm wrapper emits every
# CRD (see deploy/charts/operator-crds/crd-helm-wrapper). Missing a
# flag silently drops CRDs from the comparison.
# The yq filter is defensive — the chart currently only templates
# CRDs, but this keeps the invariant explicit if non-CRD resources
# are ever added.
helm template toolhive-operator-crds "$CHART_DIR" \
--set crds.install.server=true \
--set crds.install.registry=true \
--set crds.install.virtualMcp=true \
| yq 'select(.kind == "CustomResourceDefinition")' \
> /tmp/baseline-crds/all.yaml
echo "Rendered $(grep -c '^kind: CustomResourceDefinition' /tmp/baseline-crds/all.yaml || echo 0) baseline CRDs"
- name: Render HEAD CRDs
run: |
set -euo pipefail
mkdir -p /tmp/head-crds
helm template toolhive-operator-crds deploy/charts/operator-crds \
--set crds.install.server=true \
--set crds.install.registry=true \
--set crds.install.virtualMcp=true \
| yq 'select(.kind == "CustomResourceDefinition")' \
> /tmp/head-crds/all.yaml
echo "Rendered $(grep -c '^kind: CustomResourceDefinition' /tmp/head-crds/all.yaml || echo 0) HEAD CRDs"
- name: Install crd-schema-checker
# SHA-pinned: openshift/crd-schema-checker has no release tags at the
# time of writing, so @latest is the only other option. Pinning makes
# CI deterministic and mitigates supply-chain risk (upstream compromise
# would otherwise execute attacker code on the runner with GITHUB_TOKEN
# in env). Bump via a deliberate PR after verifying the new output
# locally. SHA pinned on 2026-04-21.
run: go install github.com/openshift/crd-schema-checker/cmd/crd-schema-checker@3fee146022bfe6f4adf84998de35d7267b864bef
- name: Check CRD schema compatibility
id: checker
env:
# See Render baseline CRDs step for rationale on env-based interpolation.
BASELINE_SOURCE: ${{ steps.baseline.outputs.source }}
run: |
# NoBools and NoMaps are OpenShift API-style conventions, not
# compat-breaking rules. They fire on fields we legitimately use
# (e.g. embeddingservers.spec.modelCache.enabled) and drown out
# real findings. Re-enable only if upstream clarifies breaking-
# change semantics for them.
set +e
crd-schema-checker check-manifests \
--existing-crd-filename /tmp/baseline-crds/all.yaml \
--new-crd-filename /tmp/head-crds/all.yaml \
--disabled-validators=NoBools,NoMaps \
2>&1 | tee /tmp/checker-output.txt
CHECK_EXIT=${PIPESTATUS[0]}
set -e
if [ "$CHECK_EXIT" -eq 0 ]; then
STATUS="Compatible"
else
STATUS="Incompatible or Unknown"
fi
{
echo "## API Compatibility — CRD Schema Check (Phase 1: advisory)"
echo ""
echo "**Baseline source**: $BASELINE_SOURCE"
echo "**Status**: $STATUS"
echo ""
echo "<details><summary>crd-schema-checker output</summary>"
echo ""
echo '```'
cat /tmp/checker-output.txt
echo '```'
echo ""
echo "</details>"
} >> "$GITHUB_STEP_SUMMARY"
exit "$CHECK_EXIT"