Skip to content

Commit f495ee3

Browse files
**ci: extract rollback safety check to dedicated workflow** (#35522)
- Created a new `.github/workflows/ai_claude-rollback-safety.yml` for managing rollback safety checks as a standalone workflow. - Moved `claude-rollback-safety-check` logic from `ai_claude-orchestrator.yml` to the new file for better separation of concerns. - Introduced a `security-check` job to ensure only authorized dotCMS organization members can trigger the workflow. - Added a `preflight-clear-stale-labels` step to remove outdated rollback-related labels for accurate re-evaluation. - Enhanced concurrency management with `cancel-in-progress` for up-to-date rollback analysis on push events. - Updated `ai_claude-orchestrator.yml` to reference the new workflow location. ref: #35396 Signed-off-by: erickgonzalez <erick.gonzalez@dotcms.com>
1 parent f95f564 commit f495ee3

2 files changed

Lines changed: 133 additions & 58 deletions

File tree

.github/workflows/ai_claude-orchestrator.yml

Lines changed: 2 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -120,61 +120,5 @@ jobs:
120120
secrets:
121121
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
122122

123-
# Rollback safety analysis — runs on every PR push
124-
claude-rollback-safety-check:
125-
needs: security-check
126-
# Cancel in-progress check when a new push arrives — always analyze latest state
127-
concurrency:
128-
group: claude-rollback-${{ github.event.pull_request.number }}
129-
cancel-in-progress: true
130-
if: |
131-
needs.security-check.outputs.authorized == 'true' &&
132-
github.event_name == 'pull_request'
133-
permissions:
134-
contents: write
135-
id-token: write
136-
pull-requests: write
137-
issues: write
138-
uses: dotCMS/ai-workflows/.github/workflows/claude-orchestrator.yml@v2.1.0
139-
with:
140-
trigger_mode: automatic
141-
prompt: |
142-
You are a dotCMS rollback-safety analyst. Determine whether the changes in this PR are safe to roll back to the previous release.
143-
144-
STEP 1 — Read the rollback-unsafe categories reference:
145-
cat docs/core/ROLLBACK_UNSAFE_CATEGORIES.md
146-
147-
STEP 2 — Get the full PR diff:
148-
git diff ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}
149-
150-
STEP 3 — Analyze the diff against EVERY category in the reference document.
151-
Focus on: database migrations (runonce tasks), Elasticsearch mapping changes,
152-
data model changes, API contract changes, and any structural storage changes.
153-
Ignore pure UI, test-only, or documentation changes unless they touch an unsafe category.
154-
155-
STEP 4a — If the changes match one or more unsafe categories, post this comment on the PR
156-
using: gh pr comment ${{ github.event.pull_request.number }} --body "..."
157-
158-
Format:
159-
Pull Request Unsafe to Rollback!!!
160-
- Category: <category ID and name, e.g. "C-1 — Structural Data Model Change">
161-
- Risk Level: <🔴 CRITICAL / 🟠 HIGH / 🟡 MEDIUM / 🟢 LOW>
162-
- Why it's unsafe: <specific explanation tied to the actual code changed>
163-
- Code that makes it unsafe: <file path(s) and the specific lines or block>
164-
- Alternative (if possible): <the safer alternative from the reference, adapted to this change>
165-
166-
If multiple categories match, repeat the block for each one.
167-
168-
Then add the label: gh pr edit ${{ github.event.pull_request.number }} --add-label "AI: Not Safe To Rollback"
169-
170-
STEP 4b — If the changes do NOT match any unsafe category:
171-
Only add the label: gh pr edit ${{ github.event.pull_request.number }} --add-label "AI: Safe To Rollback"
172-
No comment needed.
173-
174-
Be specific: quote actual file names and code lines, not generic descriptions.
175-
claude_args: '--allowedTools "Bash(git diff*),Bash(git log*),Bash(cat docs/core/ROLLBACK_UNSAFE_CATEGORIES.md),Bash(gh pr comment*),Bash(gh pr edit*)"'
176-
timeout_minutes: 15
177-
runner: ubuntu-latest
178-
enable_mention_detection: false
179-
secrets:
180-
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
123+
# Note: rollback safety analysis has moved to its own workflow:
124+
# .github/workflows/ai_claude-rollback-safety.yml
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
name: Claude AI Rollback Safety Check
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize]
6+
7+
jobs:
8+
# Security gate: Check if user is dotCMS organization member
9+
#
10+
# REQUIREMENTS FOR CLAUDE ACCESS:
11+
# 1. Must be a member of the dotCMS organization
12+
# 2. Membership must be set to PUBLIC visibility
13+
#
14+
# TROUBLESHOOTING: If blocked, visit https://github.com/orgs/dotCMS/people
15+
# and ensure your membership is public (click "Make public" if needed)
16+
security-check:
17+
runs-on: ubuntu-latest
18+
permissions:
19+
contents: read # Allow repository checkout
20+
# Note: Organization membership checking uses fine-grained token
21+
# so no additional GITHUB_TOKEN permissions needed for that API
22+
outputs:
23+
authorized: ${{ steps.membership-check.outputs.is_member }}
24+
steps:
25+
- name: Checkout repository
26+
uses: actions/checkout@v4
27+
28+
- name: Check organization membership
29+
id: membership-check
30+
uses: ./.github/actions/security/org-membership-check
31+
with:
32+
username: ${{ github.event.pull_request.user.login || github.actor }}
33+
34+
- name: Log security decision
35+
run: |
36+
if [ "${{ steps.membership-check.outputs.is_member }}" = "true" ]; then
37+
echo "✅ Access granted: User is a dotCMS organization member"
38+
else
39+
echo "❌ Access denied: User failed dotCMS organization membership check"
40+
echo ""
41+
echo "📋 TROUBLESHOOTING: If you are a dotCMS team member:"
42+
echo " 1. Visit https://github.com/orgs/dotCMS/people"
43+
echo " 2. Ensure your membership is set to 'Public'"
44+
echo " 3. If you're not listed, contact an organization owner"
45+
echo ""
46+
echo "::warning::Unauthorized user attempted to trigger Claude workflow: ${{ github.event.pull_request.user.login || github.actor }}"
47+
fi
48+
49+
# Preflight: clear stale AI rollback labels so each push is re-evaluated from scratch,
50+
# and skip the whole AI evaluation when the PR author has already classified the change
51+
# via a "Human: ..." label (skip cascades to claude-rollback-safety-check via needs/success()).
52+
preflight-clear-stale-labels:
53+
name: Clear stale AI rollback labels
54+
needs: security-check
55+
if: |
56+
needs.security-check.outputs.authorized == 'true' &&
57+
!contains(github.event.pull_request.labels.*.name, 'Human: Safe To Rollback') &&
58+
!contains(github.event.pull_request.labels.*.name, 'Human: Not Safe To Rollback')
59+
runs-on: ubuntu-latest
60+
permissions:
61+
pull-requests: write
62+
issues: write
63+
steps:
64+
- name: Remove stale AI rollback labels so this push is re-evaluated
65+
env:
66+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
67+
PR_NUMBER: ${{ github.event.pull_request.number }}
68+
REPO: ${{ github.repository }}
69+
run: |
70+
# `gh pr edit --remove-label` returns non-zero if the label is not on the PR.
71+
# Swallow that so we don't need a pre-check fetch; any other failure is cosmetic.
72+
for label in "AI: Not Safe To Rollback" "AI: Safe To Rollback"; do
73+
gh pr edit "$PR_NUMBER" --repo "$REPO" --remove-label "$label" || true
74+
done
75+
76+
# Rollback safety analysis — runs on every PR push
77+
claude-rollback-safety-check:
78+
needs: [security-check, preflight-clear-stale-labels]
79+
# Cancel in-progress check when a new push arrives — always analyze latest state
80+
concurrency:
81+
group: claude-rollback-${{ github.event.pull_request.number }}
82+
cancel-in-progress: true
83+
if: needs.security-check.outputs.authorized == 'true'
84+
permissions:
85+
contents: write
86+
id-token: write
87+
pull-requests: write
88+
issues: write
89+
uses: dotCMS/ai-workflows/.github/workflows/claude-orchestrator.yml@v2.1.0
90+
with:
91+
trigger_mode: automatic
92+
prompt: |
93+
You are a dotCMS rollback-safety analyst. Determine whether the changes in this PR are safe to roll back to the previous release.
94+
95+
STEP 1 — Read the rollback-unsafe categories reference:
96+
cat docs/core/ROLLBACK_UNSAFE_CATEGORIES.md
97+
98+
STEP 2 — Get the full PR diff:
99+
git diff ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}
100+
101+
STEP 3 — Analyze the diff against EVERY category in the reference document.
102+
Focus on: database migrations (runonce tasks), Elasticsearch mapping changes,
103+
data model changes, API contract changes, and any structural storage changes.
104+
Ignore pure UI, test-only, or documentation changes unless they touch an unsafe category.
105+
106+
STEP 4a — If the changes match one or more unsafe categories, post this comment on the PR
107+
using: gh pr comment ${{ github.event.pull_request.number }} --body "..."
108+
109+
Format:
110+
Pull Request Unsafe to Rollback!!!
111+
- Category: <category ID and name, e.g. "C-1 — Structural Data Model Change">
112+
- Risk Level: <🔴 CRITICAL / 🟠 HIGH / 🟡 MEDIUM / 🟢 LOW>
113+
- Why it's unsafe: <specific explanation tied to the actual code changed>
114+
- Code that makes it unsafe: <file path(s) and the specific lines or block>
115+
- Alternative (if possible): <the safer alternative from the reference, adapted to this change>
116+
117+
If multiple categories match, repeat the block for each one.
118+
119+
Then add the label: gh pr edit ${{ github.event.pull_request.number }} --add-label "AI: Not Safe To Rollback"
120+
121+
STEP 4b — If the changes do NOT match any unsafe category:
122+
Only add the label: gh pr edit ${{ github.event.pull_request.number }} --add-label "AI: Safe To Rollback"
123+
No comment needed.
124+
125+
Be specific: quote actual file names and code lines, not generic descriptions.
126+
claude_args: '--allowedTools "Bash(git diff*),Bash(git log*),Bash(cat docs/core/ROLLBACK_UNSAFE_CATEGORIES.md),Bash(gh pr comment*),Bash(gh pr edit*)"'
127+
timeout_minutes: 15
128+
runner: ubuntu-latest
129+
enable_mention_detection: false
130+
secrets:
131+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

0 commit comments

Comments
 (0)