-
Notifications
You must be signed in to change notification settings - Fork 5.4k
231 lines (207 loc) · 10.6 KB
/
claude-review-translations.yml
File metadata and controls
231 lines (207 loc) · 10.6 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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
name: Claude Translation Review
on:
workflow_dispatch:
inputs:
pr_number:
description: "PR number to review"
required: true
type: number
language:
description: "Language code(s) to review (comma-separated, e.g. 'hi' or 'hi,bn')"
required: false
type: string
full:
description: "Re-review the entire PR (ignore prior-review SHA, force full diff)"
required: false
default: false
type: boolean
model:
description: "Claude model for analysis"
required: false
default: "opus"
type: choice
options:
- opus
- sonnet
- haiku
fix:
description: "Automatically fix critical translation issues"
required: false
default: false
type: boolean
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
pull_request:
types: [opened]
jobs:
review-translations:
# Runs when:
# 1. Comment contains @claude /review-translations (from authorized user), OR
# 2. PR is opened with title starting with "i18n:" (automatic), OR
# 3. Manually dispatched from Actions tab with a PR number
if: |
github.event_name == 'workflow_dispatch' ||
(
github.event_name == 'issue_comment' &&
contains(github.event.comment.body, '@claude') &&
contains(github.event.comment.body, '/review-translations') &&
contains(fromJSON('["minimalsm","pettinarip","wackerow","nloureiro","konopkja","mnelsonBT","lukassim"]'), github.event.comment.user.login) &&
github.event.issue.pull_request
) ||
(
github.event_name == 'pull_request_review_comment' &&
contains(github.event.comment.body, '@claude') &&
contains(github.event.comment.body, '/review-translations') &&
contains(fromJSON('["minimalsm","pettinarip","wackerow","nloureiro","konopkja","mnelsonBT","lukassim"]'), github.event.comment.user.login)
) ||
(
github.event_name == 'pull_request' &&
startsWith(github.event.pull_request.title, 'i18n:') &&
startsWith(github.event.pull_request.head.ref, 'intl/pending-') &&
github.event.pull_request.head.repo.full_name == github.repository &&
contains(fromJSON('["minimalsm","pettinarip","wackerow","nloureiro","konopkja","mnelsonBT","lukassim"]'), github.event.pull_request.user.login)
)
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get PR number
id: pr
env:
# All values moved to env block to prevent shell injection
EVENT_NAME: ${{ github.event_name }}
INPUT_PR_NUMBER: ${{ github.event.inputs.pr_number }}
PR_NUMBER: ${{ github.event.pull_request.number }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
run: |
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
PR_NUM="$INPUT_PR_NUMBER"
elif [[ "$EVENT_NAME" == "pull_request" || "$EVENT_NAME" == "pull_request_review_comment" ]]; then
PR_NUM="$PR_NUMBER"
else
PR_NUM="$ISSUE_NUMBER"
fi
# Validate PR number is numeric to prevent injection in downstream usage
if [[ ! "$PR_NUM" =~ ^[0-9]+$ ]]; then
echo "Error: PR number must be numeric, got: $PR_NUM"
exit 1
fi
echo "number=$PR_NUM" >> $GITHUB_OUTPUT
- name: Extract flags from comment
id: parse
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_LANGUAGE: ${{ github.event.inputs.language }}
INPUT_FULL: ${{ github.event.inputs.full }}
INPUT_MODEL: ${{ github.event.inputs.model }}
INPUT_FIX: ${{ github.event.inputs.fix }}
COMMENT_BODY: ${{ github.event.comment.body }}
run: |
# For automatic triggers (pull_request), use defaults (incremental scope)
if [[ "$EVENT_NAME" == "pull_request" ]]; then
echo "language_flag=" >> $GITHUB_OUTPUT
echo "full_flag=" >> $GITHUB_OUTPUT
echo "model=opus" >> $GITHUB_OUTPUT
echo "fix_flag=" >> $GITHUB_OUTPUT
exit 0
fi
# For manual dispatch, read directly from inputs
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
if [[ -n "$INPUT_LANGUAGE" && "$INPUT_LANGUAGE" =~ ^[a-zA-Z,-]+$ ]]; then
echo "language_flag=--language=${INPUT_LANGUAGE}" >> $GITHUB_OUTPUT
else
echo "language_flag=" >> $GITHUB_OUTPUT
fi
# INPUT_MODEL is a choice type, safe values enforced by GitHub
echo "model=${INPUT_MODEL}" >> $GITHUB_OUTPUT
if [[ "$INPUT_FULL" == "true" ]]; then
echo "full_flag=--full" >> $GITHUB_OUTPUT
else
echo "full_flag=" >> $GITHUB_OUTPUT
fi
if [[ "$INPUT_FIX" == "true" ]]; then
echo "fix_flag=--fix" >> $GITHUB_OUTPUT
else
echo "fix_flag=" >> $GITHUB_OUTPUT
fi
exit 0
fi
# COMMENT_BODY is passed via env to prevent shell injection.
# Extract --language flag if present
if [[ "$COMMENT_BODY" =~ --language=([a-zA-Z,-]+) ]]; then
echo "language_flag=--language=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT
else
echo "language_flag=" >> $GITHUB_OUTPUT
fi
# Extract --full flag if present (override incremental, re-review entire PR)
if [[ "$COMMENT_BODY" =~ --full([[:space:]]|$) ]]; then
echo "full_flag=--full" >> $GITHUB_OUTPUT
else
echo "full_flag=" >> $GITHUB_OUTPUT
fi
# Extract --model flag if present (default to opus per skill spec)
if [[ "$COMMENT_BODY" =~ --model=(opus|sonnet|haiku) ]]; then
echo "model=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT
else
echo "model=opus" >> $GITHUB_OUTPUT
fi
# Extract --fix flag if present
if [[ "$COMMENT_BODY" =~ --fix ]]; then
echo "fix_flag=--fix" >> $GITHUB_OUTPUT
else
echo "fix_flag=" >> $GITHUB_OUTPUT
fi
- name: Post acknowledgment
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.pr.outputs.number }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
gh pr comment "$PR_NUMBER" --body "$(cat <<EOF
:globe_with_meridians: **Translation review started.** [View progress]($RUN_URL)
EOF
)"
- name: Run Claude Translation Review
uses: anthropics/claude-code-action@v1
timeout-minutes: 120
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--model ${{ steps.parse.outputs.model }}
--allowedTools "Task,Glob,Grep,LS,Read,Edit,WebFetch,Bash(git status:*),Bash(git diff:*),Bash(git log:*),Bash(git fetch:*),Bash(git worktree:*),Bash(git add:*),Bash(git commit:*),Bash(gh api:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh pr comment:*),Bash(gh pr review:*)"
prompt: |
Execute the /review-translations command for PR #${{ steps.pr.outputs.number }}.
Arguments: --pr=${{ steps.pr.outputs.number }} ${{ steps.parse.outputs.language_flag }} ${{ steps.parse.outputs.full_flag }} ${{ steps.parse.outputs.fix_flag }}
Follow the instructions in .claude/commands/review-translations.md for Phase 0 (scope detection -- read prior review SHA from gh api .../reviews unless --full was passed), Phase 1 (parallel agents), and Phase 2 (collecting results).
IMPORTANT workflow modifications for GitHub Actions context:
1. Use parallel Task agents (ONE agent per language) as specified in the skill.
2. SUBMIT THE REVIEW AS A PROPER PR REVIEW, NOT AN ISSUE COMMENT.
This is mandatory: the next /review-translations invocation reads each PR Review's GitHub-attached `commit_id` to determine incremental scope. An issue comment (`gh pr comment`) does not carry a `commit_id` and would break the incremental flow.
Write the body to a temp file (to avoid heredoc issues), then submit via `gh pr review`, which auto-attaches the current PR HEAD SHA:
gh pr review ${{ steps.pr.outputs.number }} --comment --body-file /tmp/pr-review-body.md
Default to `--comment`. Use `--approve` instead only when the review turned up ZERO critical issues (none found, or all auto-fixed in this same run). Never use `--request-changes`.
3. Body length: aim for a SINGLE review body. If it exceeds GitHub's body limit (~65k chars), keep the per-language summary table + scoring inside the Review body, and post ONLY the verbose per-language detail dumps as follow-up issue comments via `gh pr comment` (NOT additional Reviews -- supplemental detail does not need its own SHA marker).
4. The review body MUST include a `**Fixes:**` line near the top, with one of:
- `Critical fixes applied: {N}` -- if --fix was passed AND fixes were committed (NOTE: this GitHub Actions context CANNOT auto-fix on the PR branch because we are not running in the PR branch's worktree; for now this case will not occur from CI)
- `No fixes applied (review-only)` -- always the value when run from this workflow
- `No critical issues found` -- when there are no critical issues at all
This standardizes the format with the local /review-translations command.
5. Auto-fix behavior - check if --fix flag is present in Arguments above:
- If --fix flag IS present: this CI workflow is not currently set up to commit fixes back to the PR branch. Note this in the review body and recommend running locally: `/review-translations --pr=${{ steps.pr.outputs.number }} --fix`
- If --fix flag is NOT present: Do NOT apply fixes. At the end of your review body, if there are critical issues, include:
---
**To apply fixes**, run locally:
```
/review-translations --pr=${{ steps.pr.outputs.number }} --fix
```
6. Do NOT prompt for user input with AskUserQuestion - this is fully automated.