-
Notifications
You must be signed in to change notification settings - Fork 223
167 lines (148 loc) · 7.12 KB
/
api-compat.yml
File metadata and controls
167 lines (148 loc) · 7.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
name: API Compatibility
# This workflow guards the stability of the v1beta1 operator API surface.
#
# A breaking CRD schema change (field removal, type change, required-field
# addition, etc.) fails this check and blocks the PR. If the break is
# intentional — almost exclusively for graduation to v1beta2 — apply the
# `api-break-allowed` label to skip the check. See CONTRIBUTING.md → "API
# Stability" for the full rubric.
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, so they can't change what we compare.
- '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
# Skip the check entirely when `api-break-allowed` is applied — a
# required check that is skipped (rather than failed) counts as passing
# for branch protection, so this is the escape hatch for intentional
# breaks. Do not remove the label guard without a replacement path.
if: ${{ !contains(github.event.pull_request.labels.*.name, 'api-break-allowed') }}
# Expected runtime is ~1 minute (checkout + go setup + git fetch tag +
# go install + per-CRD checker loop). 10 minutes is a cheap upper
# bound that protects against a hung go install or git fetch.
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: Resolve baseline tag
id: baseline
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
# Baseline is the most recent release tag. Tags are immutable, so
# comparing against the tag gives us a stable, released reference
# without needing to render the Helm chart or pull from OCI.
# Falling back to origin/main would 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
# Fetch just the tag, shallow — no need to unshallow the repo.
git fetch origin "refs/tags/$LATEST_TAG:refs/tags/$LATEST_TAG" --depth=1
echo "tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
- 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:
# Route step outputs through env vars so bash quotes them instead
# of the runner substituting them directly into the script body.
# Defense-in-depth against a future edit that routes a
# PR-controlled string through these outputs.
BASELINE_TAG: ${{ steps.baseline.outputs.tag }}
run: |
set -euo pipefail
# 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.
DISABLED_VALIDATORS="NoBools,NoMaps"
CRD_DIR="deploy/charts/operator-crds/files/crds"
mkdir -p /tmp/api-compat
: > /tmp/api-compat/output.txt
OVERALL_EXIT=0
# Detect CRD files removed between baseline and HEAD — a removed
# CRD is a break that the checker can't report (it needs both
# inputs present). Compare the set of filenames directly.
BASELINE_FILES=$(git ls-tree --name-only "$BASELINE_TAG" -- "$CRD_DIR/" | sed "s|$CRD_DIR/||" | sort)
HEAD_FILES=$(ls "$CRD_DIR" | sort)
REMOVED=$(comm -23 <(echo "$BASELINE_FILES") <(echo "$HEAD_FILES") || true)
if [ -n "$REMOVED" ]; then
{
echo "ERROR: CRD files removed from HEAD (present at $BASELINE_TAG):"
echo "$REMOVED" | sed 's/^/ - /'
} | tee -a /tmp/api-compat/output.txt
OVERALL_EXIT=1
fi
# For each CRD present on HEAD, fetch the baseline version from the
# tag and run the checker. New CRDs (HEAD-only) are additive and
# skipped — note that in the output so reviewers see the full
# inventory.
for crd in "$CRD_DIR"/*.yaml; do
fname=$(basename "$crd")
rel="$CRD_DIR/$fname"
if ! git show "$BASELINE_TAG:$rel" > /tmp/api-compat/baseline.yaml 2>/dev/null; then
echo " (new CRD on HEAD, skipping: $fname)" >> /tmp/api-compat/output.txt
continue
fi
set +e
crd-schema-checker check-manifests \
--existing-crd-filename /tmp/api-compat/baseline.yaml \
--new-crd-filename "$crd" \
--disabled-validators="$DISABLED_VALIDATORS" \
>> /tmp/api-compat/output.txt 2>&1
RC=$?
set -e
[ "$RC" -ne 0 ] && OVERALL_EXIT=1
done
# Surface the combined output in the step log too, not only in the
# summary — some reviewers check the raw log first.
cat /tmp/api-compat/output.txt
if [ "$OVERALL_EXIT" -eq 0 ]; then
STATUS="Compatible"
else
STATUS="Incompatible or Unknown"
fi
{
echo "## API Compatibility — CRD Schema Check"
echo ""
echo "**Baseline**: $BASELINE_TAG"
echo "**Status**: $STATUS"
echo ""
echo "<details><summary>crd-schema-checker output</summary>"
echo ""
echo '```'
cat /tmp/api-compat/output.txt
echo '```'
echo ""
echo "</details>"
} >> "$GITHUB_STEP_SUMMARY"
exit "$OVERALL_EXIT"