1515# Gates (automatic trigger — all must pass or job never starts):
1616# 1. Review state == changes_requested, or commented with body
1717# 2. PR author == yenkins-admin or tychtjan
18- # 3. Same repo (no forks)
18+ # 3. Reviewer is a gooddata org member (checked at runtime)
19+ # 4. Same repo (no forks)
1920#
2021# Additional protections:
2122# - Claude output is never exposed in PR comments (stays in workflow artifacts)
2223# - Claude prompt includes strict security guardrails
24+ # - --dangerously-skip-permissions is required for headless CI; real protection
25+ # comes from ephemeral runner + limited PAT scope + prompt guardrails
2326#
2427# SECRETS (gooddata-python-sdk repo settings)
2528# -------------------------------------------
@@ -69,19 +72,24 @@ jobs:
6972 pull-requests : write
7073
7174 steps :
72- # ── Org membership check (manual trigger only) ────────────
75+ # ── Org membership check ────────────────────── ────────────
7376 - name : Verify org membership
74- if : github.event_name == 'workflow_dispatch'
7577 env :
7678 GH_TOKEN : ${{ secrets.TOKEN_GITHUB_YENKINS_ADMIN }}
77- ACTOR : ${{ github.actor }}
7879 run : |
79- HTTP_STATUS=$(gh api "orgs/gooddata/members/${ACTOR}" --silent -i 2>/dev/null | head -1 | awk '{print $2}')
80+ # For manual trigger, check the actor; for review trigger, check the reviewer
81+ if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
82+ CHECK_USER="${{ github.actor }}"
83+ else
84+ CHECK_USER="${{ github.event.review.user.login }}"
85+ fi
86+
87+ HTTP_STATUS=$(gh api "orgs/gooddata/members/${CHECK_USER}" --silent -i 2>/dev/null | head -1 | awk '{print $2}')
8088 if [ "$HTTP_STATUS" != "204" ]; then
81- echo "ERROR: ${ACTOR } is not a member of the gooddata organization"
89+ echo "ERROR: ${CHECK_USER } is not a member of the gooddata organization"
8290 exit 1
8391 fi
84- echo "${ACTOR } is a gooddata org member — proceeding"
92+ echo "${CHECK_USER } is a gooddata org member — proceeding"
8593
8694 # ── Resolve PR metadata ────────────────────────────────────
8795 - name : Resolve PR details
@@ -131,16 +139,15 @@ jobs:
131139 REPO : ${{ github.repository }}
132140 REVIEW_ID : ${{ github.event.review.id }}
133141 EVENT_NAME : ${{ github.event_name }}
142+ REVIEW_BODY : ${{ github.event.review.body }}
134143 run : |
135144 mkdir -p review-context
136145
137146 # Review body (only available from event trigger)
138147 if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
139148 echo "(manual trigger — no review body)" > review-context/review-body.txt
140149 else
141- cat > review-context/review-body.txt << 'EOF'
142- ${{ github.event.review.body }}
143- EOF
150+ printenv REVIEW_BODY > review-context/review-body.txt
144151 fi
145152
146153 # Inline comments for specific review (only for event trigger)
@@ -338,41 +345,56 @@ jobs:
338345
339346 # ── Apply fixes ──────────────────────────────────────────
340347 - name : Apply fixes with Claude
348+ id : claude
341349 if : steps.extract.outputs.has_comments == 'true'
342350 env :
343351 ANTHROPIC_API_KEY : ${{ secrets.ANTHROPIC_API_KEY }}
344352 run : |
353+ touch claude-output.txt
354+ EXIT_CODE=0
345355 claude --dangerously-skip-permissions \
346356 --model sonnet \
347357 --max-turns 30 \
348358 --output-format text \
349359 -p "$(cat review-context/prompt.md)" \
350- > claude-output.txt 2>&1 || true
360+ > claude-output.txt 2>&1 || EXIT_CODE=$?
351361
352- echo "=== Claude finished ==="
362+ echo "=== Claude finished (exit code: ${EXIT_CODE}) ==="
353363 echo "Lines of output: $(wc -l < claude-output.txt)"
354364
365+ if [ "$EXIT_CODE" -ne 0 ]; then
366+ echo "claude_failed=true" >> "$GITHUB_OUTPUT"
367+ echo "::warning::Claude exited with code ${EXIT_CODE}"
368+ else
369+ echo "claude_failed=false" >> "$GITHUB_OUTPUT"
370+ fi
371+
355372 # ── Commit and push ──────────────────────────────────────
356373 - name : Commit and push fixes
357374 id : push
358- if : steps.extract.outputs.has_comments == 'true'
375+ if : steps.extract.outputs.has_comments == 'true' && steps.claude.outputs.claude_failed != 'true'
359376 env :
360377 PR_BRANCH : ${{ steps.pr.outputs.pr_branch }}
361378 run : |
362- if git diff --quiet && git diff --cached --quiet; then
379+ if git diff --quiet && git diff --cached --quiet && [ -z "$(git ls-files --others --exclude-standard)" ] ; then
363380 echo "No changes made by Claude"
364381 echo "pushed=false" >> "$GITHUB_OUTPUT"
365382 exit 0
366383 fi
367384
368- git add -u
369- git commit -m "$(cat <<'EOF'
370- fix: address review feedback
385+ # Stage all changes but exclude workflow artifacts
386+ git add -A
387+ git reset -- review-context/ claude-output.txt
388+
389+ # Log changed files for audit
390+ echo "=== Files being committed ==="
391+ git diff --cached --name-status
392+ git diff --cached --name-status > review-context/committed-files.txt
371393
372- Applied fixes based on code review feedback.
373- Automated by sdk-review-fix workflow.
374- EOF
375- ) "
394+ git commit -m "fix: address review feedback
395+
396+ Applied fixes based on code review feedback.
397+ Automated by sdk-review-fix workflow. "
376398 git push origin "$PR_BRANCH"
377399
378400 echo "pushed=true" >> "$GITHUB_OUTPUT"
@@ -392,34 +414,43 @@ jobs:
392414 RUN_URL="${{ github.server_url }}/${REPO}/actions/runs/${{ github.run_id }}"
393415
394416 gh pr comment "$PR" \
395- --body "$(cat <<EOF
396- ### Review fixes applied
417+ --body "### Review fixes applied
418+
419+ Addressed feedback from @${REVIEWER} in [\`${SHA:0:7}\`](https://github.com/${REPO}/commit/${SHA}).
420+
421+ _[Workflow run](${RUN_URL}) • Claude output available in workflow artifacts_"
422+
423+ - name : Post Claude failure notice
424+ if : steps.claude.outputs.claude_failed == 'true'
425+ env :
426+ GH_TOKEN : ${{ secrets.TOKEN_GITHUB_YENKINS_ADMIN }}
427+ run : |
428+ PR="${{ steps.pr.outputs.pr_number }}"
429+ REVIEWER="${{ steps.pr.outputs.reviewer }}"
430+ RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
431+
432+ gh pr comment "$PR" \
433+ --body "### Review fix failed
397434
398- Addressed feedback from @${REVIEWER} in [\`${SHA:0:7}\`](https://github.com/${REPO}/commit/${SHA}) .
435+ Claude encountered an error while processing review feedback from @${REVIEWER}. Manual intervention is needed .
399436
400- _[Workflow run](${RUN_URL}) • Claude output available in workflow artifacts_
401- EOF
402- )"
437+ _[Workflow run](${RUN_URL}) • Check workflow logs for details_"
403438
404439 - name : Post no-changes notice
405- if : steps.extract.outputs.has_comments == 'true' && steps.push.outputs.pushed == 'false'
440+ if : steps.extract.outputs.has_comments == 'true' && steps.push.outputs.pushed == 'false' && steps.claude.outputs.claude_failed != 'true'
406441 env :
407442 GH_TOKEN : ${{ secrets.TOKEN_GITHUB_YENKINS_ADMIN }}
408443 run : |
409- REPO="${{ github.repository }}"
410444 PR="${{ steps.pr.outputs.pr_number }}"
411445 REVIEWER="${{ steps.pr.outputs.reviewer }}"
412- RUN_URL="${{ github.server_url }}/${REPO }/actions/runs/${{ github.run_id }}"
446+ RUN_URL="${{ github.server_url }}/${{ github.repository } }/actions/runs/${{ github.run_id }}"
413447
414448 gh pr comment "$PR" \
415- --body "$(cat <<EOF
416- ### No changes applied
449+ --body "### No changes applied
417450
418- Claude analyzed the review feedback from @${REVIEWER} but made no file changes. Manual intervention may be needed.
451+ Claude analyzed the review feedback from @${REVIEWER} but made no file changes. Manual intervention may be needed.
419452
420- _[Workflow run](${RUN_URL}) • Claude output available in workflow artifacts_
421- EOF
422- )"
453+ _[Workflow run](${RUN_URL}) • Claude output available in workflow artifacts_"
423454
424455 # ── Artifacts (Claude output stays here, not in PR) ──────
425456 - name : Upload artifacts
@@ -430,4 +461,5 @@ jobs:
430461 path : |
431462 review-context/
432463 claude-output.txt
464+ if-no-files-found : warn
433465 retention-days : 14
0 commit comments