Skip to content

Automate MIN_SDK_VERSION bump via release pipeline + AI-based SDK-breaking-change detection #36698

Description

@KevinDavilaDotCMS

Description

Follow-up to #36609 (SDK compatibility handshake), which shipped MinSdkVersion.java as a manually-edited Java constant. The bump procedure documented there ("Option A": developer sets MinSdkVersion.VALUE to whichever @dotcms/client version is currently published as latest on npm at PR time) works correctly for backend-only breaking changes, but has a real gap when a breaking change also requires an SDK-side code fix: the "latest published version at PR time" necessarily predates that fix, so setting the floor to it would incorrectly mark an unfixed SDK version as "supported."

This issue proposes replacing the manual, PR-time Java-constant-edit procedure with a release-pipeline-driven mechanism, so:

  • Developers never write a version number by hand or guess a not-yet-existent future date.
  • The value only ever gets set at the moment it is actually known (when a release is cut).
  • Forgetting to bump it is a loud pipeline failure, not a silent gap.

Proposed solution

1. New release-pipeline input (no separate date field — reuses release_version)

Add a new workflow_dispatch boolean input to cicd_6-release.yml:

  • bump_min_sdk_version (boolean, default false) — "Does this release include a change that breaks SDK compatibility?"

Important design correction (found while scoping this issue): the bump must land on main, on its own — it must not be folded into cicd_comp_release-prepare-phase.yml's existing automated commit (the one that also touches .mvn/maven.config and LICENSE). That commit only ever lives on the disposable release-${release_version} branch, which is never merged back into main (verified: no step anywhere in the release pipeline merges a release branch back to main). That's fine for LICENSE and maven.config, because update-license-date.sh recomputes the Change Date fresh from the current date on every run — it never depends on prior state, so it doesn't matter that it never reaches main. MinSdkVersion.VALUE is the opposite: it's a ratchet that must persist across releases. If its bump only ever lived on the release branch, the very next release-prepare run would start again from main's stale value, silently undoing the bump.

This also means timing matters, not just placement: the mechanism to update main must only fire after the release has actually succeeded (build + deployment both green), not eagerly in release-prepare. If release-prepare bumped main immediately and the release then failed downstream, main would advertise a stricter MIN_SDK_VERSION for a dotCMS version that was never actually shipped — breaking currently-valid customer SDK installs for no reason. Concretely: this needs to be a new step/job gated on needs: [release-prepare, build, deployment] with if: success() (likely in cicd_6-release.yml's release job, or a new job right after it).

Confirmed (not just suspected): main cannot be pushed to directly — this must go through a PR. Checked the precedent this repo already used for an almost identical problem: the now-retired cicd_manual-release-sdks.yml had to persist a version bump (core-web/libs/sdk/VERSION) onto main after a release, and its solution was a dedicated step, Open post-release PR to bump VERSION on main — it committed to its own branch, force-pushed that branch, then ran gh pr create --base main (reusing an existing PR via gh pr list --head ... --json url if a previous run had already opened one still pending). Verified via the actual resulting PR (#36563, "chore(sdk): bump SDK version to 1.7.1 after 1.7.0 release"): opened by github-actions[bot], but merged by a human, not auto-merged — confirmed no auto-merge mechanism exists anywhere in this repo's workflows.

This means our new mechanism should follow the exact same shape:

  • Create a branch, commit the MinSdkVersion.java bump there, push it, and gh pr create --base main (with the same dedupe-against-an-existing-open-PR check as the retired workflow, in case a previous release run already opened one that's still unmerged).
  • Because a human has to merge this PR, an open-but-unmerged bump PR is itself the same kind of silent gap this issue is trying to eliminate — if nobody notices it, the ratchet never actually advances even though the release believes it handled it. So this step must also post a Slack notification (reusing the existing notify-slack action already used elsewhere in these pipelines) explicitly asking someone to review and merge the PR, not just open it quietly.
  • Idempotency still holds the same way as before: nothing lands on main until a human merges the PR, so a failed/retried release before that point leaves main untouched — there's nothing to roll back. The new step should still no-op (skip opening a duplicate PR) if MinSdkVersion.VALUE on main already matches this release's version, or if a PR bumping to this same version is already open.

No separate free-text version field is needed regardless: MIN_SDK_VERSION, when it needs to move, is always equal to the release doing the bumping — never a separately-typed, potentially-mistyped, or future-guessed value.

2. AI-based label detection (safety net, mirrors the existing rollback-safety mechanism)

Add a new workflow, ai_claude-sdk-breaking-change.yml, structurally mirroring the existing ai_claude-rollback-safety.yml:

  • Same org-membership security gate.
  • Same "human override" escape hatch: a Human: SDK Breaking Change / Human: Not SDK Breaking label on the PR skips the AI evaluation.
  • Same stale-label-clearing preflight (re-evaluates fresh on every push).
  • Calls the same shared reusable workflow (dotCMS/ai-workflows/.github/workflows/claude-orchestrator.yml@v3) with a new prompt.
  • The prompt evaluates the PR diff against a new reference document, docs/core/SDK_BREAKING_CHANGE_CATEGORIES.md (to be authored as part of this issue, modeled on the existing docs/core/ROLLBACK_UNSAFE_CATEGORIES.md), listing concrete categories of SDK-breaking changes (e.g. removed/renamed GraphQL field the SDK depends on, changed postMessage editor protocol shape, changed REST response shape the SDK's TS types model, etc.).
  • Applies label AI: SDK Breaking Change when a match is found (with an explanatory PR comment, same format as the rollback-safety check), or no label / a "clean" label otherwise.

This mechanism is entirely decoupled from the release pipeline timing change above — it runs per-PR, independent of when/whether a release is ever cut.

Important: treat the AI's label as a strong-assist signal for human PR review, not an infallible autopilot decision — semantic "does this break the SDK contract" judgment is inherently less mechanical than the rollback-safety categories (DB migrations, ES mappings, etc.), so false negatives are a real risk the human reviewer should stay alert to.

3. Release-time validation (closes the "forgot to check" gap)

Add a validation step to the release pipeline (likely in cicd_comp_release-prepare-phase.yml or a dedicated step in cicd_6-release.yml) that:

  • Queries merged PRs between the previous release tag and the current release commit (the same kind of tag-range query the existing .github/scripts/release-qa-status script already does for the QA-coverage Slack warning).
  • Checks whether any of those PRs carry the AI: SDK Breaking Change (or human-confirmed) label.
  • If such a PR exists and bump_min_sdk_version was left false for this run, fail the release explicitly with a clear error message (mirroring the existing strict, no-silent-fallthrough validation pattern already used for the skip_latest input in cicd_6-release.yml's verify-branch job).

Unlike the actual MinSdkVersion.java bump, this validation only reads PR/label state and doesn't mutate anything — it can safely stay early in the pipeline (e.g. in release-prepare or verify-branch) to fail fast, before spending a full build+deployment cycle on a misconfigured release.

Out of scope

References

Acceptance Criteria

  • cicd_6-release.yml has a new bump_min_sdk_version boolean input (default false)
  • When true, a step gated on the release having fully succeeded (build + deployment both green) opens a PR against main with the MinSdkVersion.java VALUE bump — not folded into release-prepare's release-branch commit, and not a direct push
  • That step posts a Slack notification asking a human to review and merge the bump PR (reusing the existing notify-slack action)
  • The step is idempotent: it reuses an already-open bump PR from a previous run instead of opening a duplicate, and no-ops entirely if main's MinSdkVersion.VALUE already matches this release's version
  • If the release fails before reaching this step, main is left completely untouched with respect to MinSdkVersion.VALUE (nothing to roll back)
  • New workflow ai_claude-sdk-breaking-change.yml exists, mirroring ai_claude-rollback-safety.yml's security gate, human-override labels, and stale-label-clearing preflight
  • New reference doc docs/core/SDK_BREAKING_CHANGE_CATEGORIES.md defines concrete, evaluable categories of SDK-breaking changes
  • AI workflow applies AI: SDK Breaking Change label (with explanatory comment) when a match is found against the reference doc
  • Release pipeline fails explicitly (not silently), early (before build/deployment), if a merged PR since the last release tag carries the breaking-change label but bump_min_sdk_version was left false
  • MinSdkVersion.java's Javadoc (from SDK compatibility handshake: server advertises version + minimum supported SDK, SDKs warn on mismatch #36609) is updated to describe this pipeline-driven procedure (bumped via an automated PR against main, opened after a successful release, merged by a human) instead of the superseded manual "Option A" (developer edits the constant directly in their PR)

Priority

High

Additional Context

Purely a CI/CD process-automation improvement for maintaining MIN_SDK_VERSION going forward — does not touch the already-shipped, already-tested runtime handshake feature from #36609.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions