feat(workflows): Add Claude AI SDK breaking change check workflow#36728
Conversation
- Introduced a new GitHub Actions workflow to automatically check for breaking changes in SDK compatibility during pull requests. - The workflow verifies if the user is a member of the dotCMS organization and logs the security decision. - Implemented a job to analyze the pull request for SDK breaking changes based on defined categories, ensuring that any breaking changes are flagged appropriately. - Updated the MinSdkVersion.java documentation to reflect the new automated bump procedure for SDK compatibility. - Added a comprehensive reference document for SDK breaking change categories to guide developers in assessing changes.
|
Claude finished @KevinDavilaDotCMS's task in 8m 58s —— View job Claude Code Review
This is a well-structured feature (#36698). The New Issues
Notes (non-issue)
|
Automate
MinSdkVersion.VALUEmaintenance via the release pipelineCloses #36698
Problem
Follow-up to #36609 / #36678 (the SDK compatibility handshake). That work shipped
MinSdkVersion.java— a constant read bySdkVersionWebInterceptorand sent to every@dotcms/*SDK consumer via theX-DotCMS-Min-SDKresponse header — but left its maintenancefully manual: a developer had to remember to edit the constant by hand in their PR whenever a
change broke SDK compatibility ("Option A" from the original ticket: set it to whatever
@dotcms/clientversion was currentlylateston npm at PR time).That's fragile in two ways:
SDK-side fix — the version that's
latestat PR time necessarily predates that fix.This PR replaces the manual edit with a release-pipeline-driven mechanism with a safety net at
both ends: PR-time detection and release-time enforcement.
What this ships
SDK Breaking Changelabel + AI detector (ai_claude-sdk-breaking-change.yml) — runs onevery PR, evaluates the diff against a new reference doc, and labels PRs that break SDK
compatibility.
docs/core/SDK_BREAKING_CHANGE_CATEGORIES.md— the reference doc the AI (and humanreviewers) use to judge what counts as SDK-breaking, grounded in the actual
@dotcms/clientsurface (GraphQL page/content API, REST responses, the UVE
postMessageprotocol, thecompatibility headers themselves).
bump_min_sdk_versionrelease input oncicd_6-release.yml— "does this releaseinclude an SDK-breaking change?"
release carries the label but the operator left the checkbox unchecked.
push) bumping
MinSdkVersion.VALUEto the release version and pings Slack for human review.MinSdkVersion.javadescribing this procedure.Design decisions worth knowing about for review
Why the bump happens after the release succeeds, not during
release-prepare.The obvious-looking approach — folding the bump into
release-prepare's existing automatedcommit (the same one that already updates
LICENSEand.mvn/maven.config) — is wrong for thisvalue specifically. That commit only ever lives on the disposable
release-${version}branch,which is never merged back to
main. That's fine forLICENSE, becauseupdate-license-date.shrecomputes the date fresh from "today" on every run — it never dependson prior state.
MinSdkVersion.VALUEis the opposite: it's a ratchet that must persist forwardacross releases. If it only ever lived on the release branch, the next release-prepare run would
silently start again from
main's stale value.That in turn means timing matters: the bump can only safely happen once we know the release
shipped (build + deployment both green). If we bumped
maineagerly inrelease-prepareand therelease then failed downstream,
mainwould advertise a stricter compatibility floor for adotCMS version that customers never actually received — breaking currently-valid SDK installs for
nothing.
Why it's a PR, never a direct push to
main.Confirmed via the exact historical precedent for this same problem: the now-retired
cicd_manual-release-sdks.ymlhad to persist a version bump (core-web/libs/sdk/VERSION) ontomainafter a release, and its solution was a dedicated "Open post-release PR to bump VERSION onmain" step — never a direct push. The PR it produced (#36563) was opened by
github-actions[bot]but merged by a human. There's no auto-merge anywhere in this repo's workflows to lean on instead.
Why one label, not four.
An earlier draft of this mechanism mirrored
ai_claude-rollback-safety.yml's label schemeexactly:
AI: X/AI: Not X/Human: X/Human: Not X. For rollback-safety that split earnsits keep — a "clear stale AI labels on every push" preflight needs to not clobber a human's
deliberate override. For this mechanism we simplified to a single label,
SDK Breaking Change:removes it.
action, available to anyone with write access. No separate
Human: ...label needed.AI, and PRs from external contributors, where the AI never runs at all (the org-membership
security gate blocks it).
decision on the next push, one label to reason about.
How it works end to end
1. PR time
Every PR triggers
ai_claude-sdk-breaking-change.yml:security-check— gates on dotCMS org membership (same mechanism as rollback-safety).claude-sdk-breaking-change-check— readsdocs/core/SDK_BREAKING_CHANGE_CATEGORIES.md, diffsthe PR, and either:
SDK Breaking Change, orA human reviewer can add or remove the label manually at any time regardless of the AI's verdict.
2. Release time — the checkbox is forgotten
Say a PR merged with the
SDK Breaking Changelabel (correctly, from the AI or a human), twomore unrelated PRs merge after it, and then someone cuts a release without checking
bump_min_sdk_version. The very first job,verify-branch, runs a new step(
Validate bump_min_sdk_version against merged PR labels) that:resolves the previous standard release tag,
pulls every PR merged since that tag (via squash-merge commit messages, same convention the
existing
release-qa-statustool relies on),checks each one's labels,
and if any carries
SDK Breaking Changewhilebump_min_sdk_versionisfalse, fails therelease immediately, before
release-prepareeven creates a branch or tag:The operator sees the error, realizes what happened, and re-runs with the checkbox checked.
If the checkbox is checked but no labeled PR is found in range, the release still proceeds — it
just prints a non-fatal warning, since a human explicitly opted in and may know something the
labels don't capture:
LTS releases (
_lts_v##) skip this validation entirely —@dotcms/*SDKs track the@latestnpmcadence, not LTS patch cadence.
3. Release time — the release fails partway through
Say the checkbox was checked correctly, but the release fails during
buildordeploymentfor unrelated reasons. The new
bump-min-sdk-versionjob is gated with a strictsuccess()onneeds: [release-prepare, build, deployment]— not thealways() && !failure() && !cancelled()idiom used by sibling jobs. If any of those three didn't fully succeed, this job doesn't run at
all:
MinSdkVersion.VALUEis not touched.Nothing needs to be rolled back, because nothing was ever written. Once the operator fixes the
issue and re-runs the same release successfully, the bump job runs then instead.
4. Release time — the release succeeds
Once
release-prepare,build, anddeploymentare all green,bump-min-sdk-versionruns:Checks out fresh
main.Idempotency check update with latest SVN #1: if
MinSdkVersion.VALUEalready equals this release's version(already-bumped, or a retry after a previous bump PR already merged), it no-ops — no duplicate
commit, no duplicate PR.
Otherwise, creates branch
sdk/bump-min-sdk-version-<version>offmain, edits the constant,and verifies the edit actually took effect (guards against the
VALUE = "..."literalsilently failing to match if the file is ever reformatted — in which case it fails loudly
instead of quietly opening an empty PR).
Commits, force-pushes the branch.
Idempotency check Test Branch and Commit #2: reuses an already-open PR for the same branch/version instead of
opening a duplicate on a retried run.
Opens the PR (title:
chore(sdk): bump MinSdkVersion.VALUE to <version>, body explains why),and posts to Slack (
log-sdk-libschannel):If the job itself fails partway (e.g.
gh pr createerrors), a separate failure message goesto the same channel instead. The PR does not auto-merge — a human always reviews and merges
it manually, same as the retired SDK-VERSION-bump precedent.
Test scenarios walked through during design (not yet live-fire tested — see Known limitations)
SDK Breaking Changemanually; no separate label needed, no re-add risk since the AI never removes/re-adds on its ownSDK Breaking Changemanually; the release-time gate treats it identically to an AI-applied labelgrep -qxF 'SDK Breaking Change') — no distinction by originbump_min_sdk_versionon a release that includes a labeled PRverify-branch, before any release branch/tag is created::error::message traced abovebump_min_sdk_versionbut release fails inbuild/deploymentbump-min-sdk-versionjob doesn't run at all — nothing written, nothing to roll backsuccess()gate semantics (cross-checked against this repo's ownpromote-latestjob, which relies on the same implicit behavior)main, Slack notification fires tolog-sdk-libsgrep/gh pr listdedupe logicVALUE = "..."literal gets reformatted some daysed+ verify logic — confirmed the mismatch is caughtnextnpm dist-tag publish job (cicd_3-trunk.yml)MinSdkVersion.javafalls under thebackendchange-detection filter, notsdk_libs, so merging the bump PR never triggerspublish-sdk-next.github/filters.yamlKnown limitations / accepted risk (not fixed here, flagged for awareness)
PRs (different branch names, keyed by version) — no collision, but a human merging both should
merge the later version last.
(different branch name). The validation step does emit a non-fatal
::warning::if it finds analready-open
sdk/bump-min-sdk-version-*PR againstmain, nudging the operator to merge itfirst.
release-qa-statustool assume squash-merge commit messages (... (#12345)) to map commits backto PR numbers. If the repo's merge policy ever changes, both silently stop finding PR numbers —
fails safe (never blocks a release), but a real breaking PR could then slip through undetected.
Pre-existing repo-wide assumption, not new to this PR.
reasoned through carefully and unit-tested locally where possible — e.g. the
sed/grepverification logic). A parallel attempt to validate this end-to-end in the disposable
core-workflow-testfork hit an org-level branch-protection wall (a required status check thatneither
--adminmerge nor a direct push could bypass with the available token permissions), sothe AI-labeling behavior, the release-time gate, and the bump job's real GitHub Actions execution
should be watched closely on this repo's first real usage.
Files changed
.github/workflows/cicd_6-release.yml— newbump_min_sdk_versioninput, new validation stepin
verify-branch, newbump-min-sdk-versionjob..github/workflows/ai_claude-sdk-breaking-change.yml(new).docs/core/SDK_BREAKING_CHANGE_CATEGORIES.md(new).dotCMS/src/main/java/com/dotcms/rest/config/MinSdkVersion.java— Javadoc updated to describethe automated procedure.
One-time setup already done
The
SDK Breaking Changelabel has been created on this repo (color#b60205).