Skip to content

Commit f978ba0

Browse files
committed
Workflows fix
1 parent 3e8e238 commit f978ba0

File tree

3 files changed

+240
-129
lines changed

3 files changed

+240
-129
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: Claude AI review (label trigger)
2+
3+
on:
4+
pull_request_target:
5+
types: [ labeled ]
6+
7+
permissions:
8+
contents: read
9+
pull-requests: write
10+
issues: write
11+
id-token: write # Required for OIDC token generation
12+
13+
jobs:
14+
ai-label-review:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
ref: ${{ github.event.pull_request.head.ref }}
22+
repository: ${{ github.event.pull_request.head.repo.full_name }}
23+
24+
- name: Get PR diff summary
25+
id: diff
26+
run: |
27+
echo 'patch<<EOF' >> $GITHUB_OUTPUT
28+
git fetch origin ${{ github.event.pull_request.base.ref }}
29+
git --no-pager diff --unified=0 --minimal --patch origin/${{ github.event.pull_request.base.ref }}...HEAD | sed -n '1,6000p'
30+
echo 'EOF' >> $GITHUB_OUTPUT
31+
32+
- name: Generate flowforge review context
33+
id: ctx
34+
shell: bash
35+
run: |
36+
echo 'context<<EOF' >> $GITHUB_OUTPUT
37+
bash scripts/ff-claude-context.sh | sed -n '1,4000p'
38+
echo 'EOF' >> $GITHUB_OUTPUT
39+
40+
- name: Run claude code action (flowforge review)
41+
if: github.event.label.name == 'ai-review'
42+
id: claude
43+
uses: anthropics/claude-code-action@v1
44+
with:
45+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
46+
prompt: |
47+
You are the FlowForge strict code reviewer. Review the DIFF and
48+
produce severity-tagged findings and minimal patch suggestions, using these anchors:
49+
- ADR‑022 Safety & Errors, refactor-idiomatic-scala2.md, Spark/Delta best practices.
50+
51+
DIFF TO REVIEW:
52+
---
53+
${{ steps.diff.outputs.patch }}
54+
---
55+
CONTEXT:
56+
---
57+
${{ steps.ctx.outputs.context }}
58+
---
59+
claude_args: >-
60+
--allowedTools "Read" "Bash(rg:*)"
61+
--max-turns 4
62+
--verbose
63+
64+
- name: Post review via GitHub reviews API (with inline support)
65+
if: github.event.label.name == 'ai-review' && steps.claude.outputs.response != ''
66+
env:
67+
GH_TOKEN: ${{ github.token }}
68+
run: |
69+
RESP_FILE=$(mktemp)
70+
echo "${{ steps.claude.outputs.response }}" > "$RESP_FILE"
71+
REVIEW_EVENT=COMMENT
72+
if grep -q "(HIGH)" "$RESP_FILE"; then REVIEW_EVENT=REQUEST_CHANGES; fi
73+
INLINE_FILE=$(mktemp)
74+
awk '/^INLINE: /{sub(/^INLINE: /, ""); print}' "$RESP_FILE" > "$INLINE_FILE"
75+
if [ -s "$INLINE_FILE" ]; then
76+
COMMENTS_JSON=$(mktemp)
77+
PR_SHA="${{ github.event.pull_request.head.sha }}"
78+
echo '[' > "$COMMENTS_JSON"
79+
FIRST=1
80+
while IFS= read -r line; do
81+
path=$(echo "$line" | awk -F: '{print $1}')
82+
lineno=$(echo "$line" | awk -F: '{print $2}')
83+
msg=$(echo "$line" | cut -d: -f3- | sed 's/^ //')
84+
[ -z "$path" ] && continue
85+
[ -z "$lineno" ] && lineno=1
86+
if [ $FIRST -eq 0 ]; then echo ',' >> "$COMMENTS_JSON"; fi
87+
FIRST=0
88+
printf '{"path":"%s","line":%s,"side":"RIGHT","body":%s}' \
89+
"$path" "$lineno" "$(printf '%s' "$msg" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')" >> "$COMMENTS_JSON"
90+
done < "$INLINE_FILE"
91+
echo ']' >> "$COMMENTS_JSON"
92+
gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews \
93+
-f event=$REVIEW_EVENT -f body="FlowForge AI review (label) [$REVIEW_EVENT]" \
94+
-f commit_id="$PR_SHA" -F comments=@"$COMMENTS_JSON" -H "Accept: application/vnd.github+json"
95+
else
96+
gh pr review ${{ github.event.pull_request.number }} --body-file "$RESP_FILE" --event $REVIEW_EVENT
97+
fi
Lines changed: 108 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
1-
# .github/workflows/claude-code-review.yml
2-
# Non-blocking strict review: comments on the PR using gh CLI; never fails the job.
3-
name: Claude code review (strict, non-blocking)
1+
name: Claude code review (non-blocking)
42

53
on:
64
pull_request:
7-
types: [opened, synchronize, reopened, ready_for_review]
5+
types: [ opened, synchronize, reopened, ready_for_review ]
6+
paths:
7+
- '**/*.scala'
8+
- '**/*.sbt'
9+
- '.scalafix.conf'
10+
- '.scalafmt.conf'
11+
- 'project/**'
12+
- '.github/workflows/**'
13+
- 'AGENTS.md'
14+
- 'docs/adr/**'
15+
- 'docs/plan/**'
16+
17+
permissions:
18+
contents: read
19+
pull-requests: write
20+
issues: write
21+
actions: read
22+
id-token: write # Required for OIDC token generation
823

924
concurrency:
1025
group: claude-review-${{ github.event.pull_request.number }}
@@ -13,80 +28,119 @@ concurrency:
1328
jobs:
1429
claude-review:
1530
runs-on: ubuntu-latest
16-
permissions:
17-
contents: read
18-
pull-requests: write # needed for gh pr comment
19-
id-token: write
31+
continue-on-error: true
2032

2133
steps:
2234
- name: Checkout repository
2335
uses: actions/checkout@v4
2436
with:
2537
fetch-depth: 0
2638

27-
- name: Load CLAUDE.md rules
28-
id: rules
39+
- name: Get PR diff summary
40+
id: diff
41+
run: |
42+
echo 'patch<<EOF' >> $GITHUB_OUTPUT
43+
git fetch origin ${{ github.event.pull_request.base.ref }}
44+
git --no-pager diff --unified=0 --minimal --patch origin/${{ github.event.pull_request.base.ref }}...HEAD | sed -n '1,6000p'
45+
echo 'EOF' >> $GITHUB_OUTPUT
46+
47+
- name: Generate FlowForge review context
48+
id: ctx
2949
shell: bash
3050
run: |
31-
echo 'guidelines<<EOF' >> "$GITHUB_OUTPUT"
32-
sed -n '1,6000p' CLAUDE.md >> "$GITHUB_OUTPUT"
33-
echo 'EOF' >> "$GITHUB_OUTPUT"
51+
echo 'context<<EOF' >> $GITHUB_OUTPUT
52+
bash scripts/ff-claude-context.sh | sed -n '1,4000p'
53+
echo 'EOF' >> $GITHUB_OUTPUT
3454
35-
- name: Run claude (repo-wide checklist, then publish via gh CLI)
55+
- name: Run claude code action (flowforge review)
56+
if: true
3657
id: claude
3758
uses: anthropics/claude-code-action@v1
3859
with:
3960
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
4061
prompt: |
41-
You are a STRICT reviewer enforcing every rule in CLAUDE.md across the repository.
62+
You are the FlowForge strict code reviewer. Review the DIFF and
63+
produce a concise list of findings with severity labels (HIGH/MED/LOW),
64+
followed by minimal patch suggestions. Use this FlowForge checklist:
4265
43-
Scope:
44-
- Enumerate:
45-
git ls-files '*.scala' '*.sbt' '.scalafmt.conf' 'build.sbt' 'project/*.sbt' 'project/*.scala' 'project/build.properties'
46-
- Apply ALL rules to each file (not just diffs).
66+
1) ADR‑022 Safety & Errors
67+
- No try/catch/Try in main sources; use Safety/EffectSystem.
68+
- Result/ValidatedResult naming; ErrorMapper mapping present.
69+
- bracket/guarantee for resource cleanup; no manual finally.
70+
2) Idiomatic Scala (docs/plan/refactor-idiomatic-scala2.md)
71+
- Pure transforms; effectful IO via EffectSystem.
72+
- No concrete IO in main modules; avoid println; no null.
73+
- No asInstanceOf/Any; exhaustive matches; for‑comprehensions.
74+
3) Spark/Delta best practices
75+
- Avoid collect on large data; prefer Dataset/DataFrame ops.
76+
- Use F.blocking for IO; avoid driver‑side loops; partitioning sane.
77+
- Delta constraints: only NOT NULL and CHECK; no UNIQUE claims.
78+
4) CI/lint conformance
79+
- Scalafmt/scalafix friendly; no wildcard imports; organized imports.
4780
4881
Output:
49-
- One markdown report grouped by file.
50-
- For each rule: ✅ PASS or ❌ FAIL with minimal patch/diff.
51-
- A short "Top 10 Fixes" section.
52-
- Final line MUST be:
53-
CLAUDE_RULES_STATUS: PASS # or FAIL
54-
55-
PUBLISHING (MANDATORY):
56-
- Use the PR number from $PR_NUMBER.
57-
- Post your full markdown report as a single PR comment with one Bash command:
58-
gh pr comment "$PR_NUMBER" --body-file - <<'MD'
59-
<PASTE YOUR FULL MARKDOWN REPORT HERE>
60-
MD
61-
62-
Rules to enforce:
82+
- Findings: bullet list with (SEVERITY) and 1–2 line rationale.
83+
- Quick patches: fenced unified diffs with minimal edits (<= ~30 lines each).
84+
- Module plan: for modules touched in DIFF, list 2–4 next actions (e.g., replace Try with Safety, add bracket for streams, remove collect in hot path, swap println→logger).
85+
- Cross‑ref ADR names (e.g., ADR‑022) and docs (e.g., refactor-idiomatic-scala2.md) by name.
86+
87+
DIFF TO REVIEW:
6388
---
64-
${{ steps.rules.outputs.guidelines }}
89+
${{ steps.diff.outputs.patch }}
6590
---
6691
92+
CONTEXT (repository map, pattern scans, ADRs heads):
93+
---
94+
${{ steps.ctx.outputs.context }}
95+
---
6796
claude_args: >-
68-
--allowedTools
69-
"Read"
70-
"Edit"
71-
"MultiEdit"
72-
"Bash(git ls-files:*)"
73-
"Bash(rg:*)"
74-
"Bash(find:*)"
75-
"Bash(sed:*)"
76-
"Bash(xargs:*)"
77-
"Bash(gh pr view:*)"
78-
"Bash(gh pr comment:*)"
79-
--max-turns 10
97+
--allowedTools "Read" "Bash(rg:*)"
98+
--max-turns 4
8099
--verbose
81100
82-
env:
83-
GH_TOKEN: ${{ github.token }} # gh CLI auth
84-
PR_NUMBER: ${{ github.event.pull_request.number }}
85-
86-
- name: Soft gate summary (never fail)
87-
if: always()
101+
- name: Post review (inline when available)
102+
if: steps.claude.outputs.response != ''
88103
env:
89104
GH_TOKEN: ${{ github.token }}
90105
run: |
91-
echo "### Claude strict review" >> "$GITHUB_STEP_SUMMARY"
92-
echo "Report should be posted via gh pr comment on PR #${{ github.event.pull_request.number }}." >> "$GITHUB_STEP_SUMMARY"
106+
echo "Preparing review body and inline comments"
107+
RESP_FILE=$(mktemp)
108+
echo "${{ steps.claude.outputs.response }}" > "$RESP_FILE"
109+
110+
REVIEW_EVENT=COMMENT
111+
if grep -q "(HIGH)" "$RESP_FILE"; then REVIEW_EVENT=REQUEST_CHANGES; fi
112+
113+
# Parse simple inline comments of the form: INLINE: path:line: message
114+
INLINE_FILE=$(mktemp)
115+
awk '/^INLINE: /{sub(/^INLINE: /, ""); print}' "$RESP_FILE" > "$INLINE_FILE"
116+
117+
if [ -s "$INLINE_FILE" ]; then
118+
echo "Found inline comments; creating review via GitHub Reviews API"
119+
COMMENTS_JSON=$(mktemp)
120+
PR_SHA="${{ github.event.pull_request.head.sha }}"
121+
echo '[' > "$COMMENTS_JSON"
122+
FIRST=1
123+
while IFS= read -r line; do
124+
path=$(echo "$line" | awk -F: '{print $1}')
125+
lineno=$(echo "$line" | awk -F: '{print $2}')
126+
msg=$(echo "$line" | cut -d: -f3- | sed 's/^ //')
127+
[ -z "$path" ] && continue
128+
[ -z "$lineno" ] && lineno=1
129+
if [ $FIRST -eq 0 ]; then echo ',' >> "$COMMENTS_JSON"; fi
130+
FIRST=0
131+
printf '{"path":"%s","line":%s,"side":"RIGHT","body":%s}' \
132+
"$path" "$lineno" "$(printf '%s' "$msg" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')" >> "$COMMENTS_JSON"
133+
done < "$INLINE_FILE"
134+
echo ']' >> "$COMMENTS_JSON"
135+
136+
gh api \
137+
repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews \
138+
-f event=$REVIEW_EVENT \
139+
-f body="FlowForge AI review ($REVIEW_EVENT)" \
140+
-f commit_id="$PR_SHA" \
141+
-F comments=@"$COMMENTS_JSON" \
142+
-H "Accept: application/vnd.github+json"
143+
else
144+
echo "Posting single review with $REVIEW_EVENT"
145+
gh pr review ${{ github.event.pull_request.number }} --body-file "$RESP_FILE" --event $REVIEW_EVENT
146+
fi

0 commit comments

Comments
 (0)