Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions .github/workflows/ai_claude-sdk-breaking-change.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
name: Claude AI SDK Breaking Change Check

on:
pull_request:
types: [opened, synchronize]

jobs:
# Security gate: Check if user is dotCMS organization member
#
# REQUIREMENTS FOR CLAUDE ACCESS:
# 1. Must be a member of the dotCMS organization
# 2. Membership must be set to PUBLIC visibility
#
# TROUBLESHOOTING: If blocked, visit https://github.com/orgs/dotCMS/people
# and ensure your membership is public (click "Make public" if needed)
security-check:
runs-on: ubuntu-latest
permissions:
contents: read # Allow repository checkout
# Note: Organization membership checking uses fine-grained token
# so no additional GITHUB_TOKEN permissions needed for that API
outputs:
authorized: ${{ steps.membership-check.outputs.is_member }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Check organization membership
id: membership-check
uses: ./.github/actions/security/org-membership-check
with:
username: ${{ github.event.pull_request.user.login || github.actor }}

- name: Log security decision
run: |
if [ "${{ steps.membership-check.outputs.is_member }}" = "true" ]; then
echo "✅ Access granted: User is a dotCMS organization member"
else
echo "❌ Access denied: User failed dotCMS organization membership check"
echo ""
echo "📋 TROUBLESHOOTING: If you are a dotCMS team member:"
echo " 1. Visit https://github.com/orgs/dotCMS/people"
echo " 2. Ensure your membership is set to 'Public'"
echo " 3. If you're not listed, contact an organization owner"
echo ""
echo "::warning::Unauthorized user attempted to trigger Claude workflow: ${{ github.event.pull_request.user.login || github.actor }}"
fi

# Preflight: clear stale AI SDK-breaking-change labels so each push is re-evaluated from
# scratch, and skip the whole AI evaluation when the PR author has already classified the
# change via a "Human: ..." label (skip cascades to claude-sdk-breaking-change-check via
# needs/success()).
preflight-clear-stale-labels:
name: Clear stale AI SDK breaking-change labels
needs: security-check
if: |
needs.security-check.outputs.authorized == 'true' &&
!contains(github.event.pull_request.labels.*.name, 'Human: SDK Breaking Change') &&
!contains(github.event.pull_request.labels.*.name, 'Human: Not SDK Breaking Change')
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: write
steps:
- name: Remove stale AI SDK breaking-change labels so this push is re-evaluated
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
# `gh pr edit --remove-label` returns non-zero if the label is not on the PR.
# Swallow that so we don't need a pre-check fetch; any other failure is cosmetic.
for label in "AI: Not SDK Breaking Change" "AI: SDK Breaking Change"; do
gh pr edit "$PR_NUMBER" --repo "$REPO" --remove-label "$label" || true
done

# SDK breaking-change analysis — runs on every PR push
claude-sdk-breaking-change-check:
needs: [security-check, preflight-clear-stale-labels]
# Cancel in-progress check when a new push arrives — always analyze latest state
concurrency:
group: claude-sdk-breaking-${{ github.event.pull_request.number }}
cancel-in-progress: true
if: needs.security-check.outputs.authorized == 'true'
permissions:
contents: write
id-token: write
pull-requests: write
issues: write
uses: dotCMS/ai-workflows/.github/workflows/claude-orchestrator.yml@v3
with:
model_id: ${{ vars.BEDROCK_MODEL_ID }}
bedrock_role_arn: ${{ vars.BEDROCK_ROLE_ARN }}
trigger_mode: automatic
prompt: |
You are a dotCMS SDK-compatibility analyst. Determine whether the changes in this PR
break compatibility for `@dotcms/*` SDK consumers (`@dotcms/client`, `@dotcms/react`,
`@dotcms/angular`, `@dotcms/uve`) — i.e. whether an SDK version built against the
server contract *before* this change would misbehave against the server *after* this
change.

STEP 1 — Read the SDK breaking-change categories reference:
cat docs/core/SDK_BREAKING_CHANGE_CATEGORIES.md

STEP 2 — Get the full PR diff:
git diff ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}

STEP 3 — Analyze the diff against EVERY category in the reference document.
Focus on: GraphQL schema surface reachable via the page API's graphql.page /
graphql.content extension (new required fields/args, removed/renamed types or
fields), REST response shape changes to /api/v1/page/*, /api/v1/content, and
/api/v1/nav, changes to the UVE/editor postMessage protocol (message names or
payload shapes in DotCMSUVEAction / __DOTCMS_UVE_EVENT__), and changes to the
SdkVersionWebInterceptor / X-DotCMS-Version / X-DotCMS-Min-SDK headers or the
compareVersions() comparison contract in sdk-compatibility.ts. Ignore pure admin-UI
(dotcms-ui) changes, test-only changes, or documentation changes unless they touch
one of the above surfaces.

STEP 4a — If the changes break SDK compatibility, post this comment on the PR
using: gh pr comment ${{ github.event.pull_request.number }} --body "..."

Format:
SDK Breaking Change Detected!!!
- Category: <category ID and name, e.g. "G-1 — Removing or Renaming a Reachable GraphQL Type/Field">
- Why it breaks compatibility: <specific explanation tied to the actual code changed>
- Code that makes it breaking: <file path(s) and the specific lines or block>
- Safer alternative (if possible): <the safer alternative from the reference, adapted to this change>

If multiple categories match, repeat the block for each one.

Then add the label: gh pr edit ${{ github.event.pull_request.number }} --add-label "AI: SDK Breaking Change"

STEP 4b — If the changes do NOT break SDK compatibility:
Only add the label: gh pr edit ${{ github.event.pull_request.number }} --add-label "AI: Not SDK Breaking Change"
No comment needed.

Be specific: quote actual file names and code lines, not generic descriptions.
claude_args: '--allowedTools "Bash(git diff*),Bash(git log*),Bash(cat docs/core/SDK_BREAKING_CHANGE_CATEGORIES.md),Bash(gh pr comment*),Bash(gh pr edit*)"'
timeout_minutes: 15
runner: ubuntu-latest
enable_mention_detection: false
15 changes: 13 additions & 2 deletions .github/workflows/cicd_3-trunk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ jobs:

- name: 'Publish SDK packages to NPM (next)'
id: deploy-javascript-sdk
# Notify-but-don't-fail: cicd_comp_finalize-phase.yml scans every job in the run
# via the GitHub API (not just finalize's own `needs`), so without this a
# transient npm/registry failure here would mark the whole trunk workflow red
# over an internal dev/QA publish. The failure Slack step below still fires
# (it checks the step's own outcome, not the job-level failure()/success()
# context functions, which continue-on-error would otherwise mask).
continue-on-error: true
uses: ./.github/actions/core-cicd/deployment/deploy-javascript-sdk
with:
ref: ${{ github.sha }}
Expand All @@ -195,15 +202,19 @@ jobs:
slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }}

- name: 'Slack Notification (SDK next failure)'
if: failure()
# Checks the publish step's own outcome directly rather than the job-level
# failure()/success() context functions — those reflect conclusion (which
# continue-on-error masks to "success"), not outcome, so failure() would never
# fire here once the step above has continue-on-error: true.
if: steps.deploy-javascript-sdk.outcome == 'failure'
continue-on-error: true
uses: ./.github/actions/core-cicd/notification/notify-slack
with:
channel-id: "log-sdk-libs"
payload: |
> :red_circle: *SDK `next` publish FAILED!*
>
> The automated SDK `next` publish failed while trying to publish version `${{ steps.next-version.outputs.version }}`.
> The automated SDK `next` publish failed while trying to publish version `${{ steps.next-version.outputs.version || 'unknown — the version-compute step itself failed' }}`.
> <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|View commit> · <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View workflow run — check logs for details>
slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }}

Expand Down
Loading
Loading