Skip to content

Commit f5cab36

Browse files
authored
Merge pull request #369 from netwrix/doc-review-diff-only
Doc review diff only
2 parents 4730863 + 3d2f570 commit f5cab36

2 files changed

Lines changed: 87 additions & 11 deletions

File tree

.github/workflows/claude-documentation-fixer.yml

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,44 @@ jobs:
9797
--allowedTools "Read,Write,Edit,Bash(vale:*),Bash(gh pr view:*),Bash(gh pr diff:*),Bash(git config:*),Bash(git add:*),Bash(git commit:*),Bash(git push:*),Bash(git status:*),Bash(git diff:*)"
9898
--append-system-prompt "${{ steps.read-prompt.outputs.prompt }}"
9999
100+
- name: Record last comment ID
101+
id: pre-claude
102+
if: steps.pr-info.outputs.is_fork == 'false' && steps.cmd-type.outputs.is_preexisting == 'true'
103+
env:
104+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
105+
run: |
106+
LAST_ID=$(gh api repos/${{ github.repository }}/issues/${{ steps.pr-info.outputs.number }}/comments \
107+
--jq 'if length > 0 then .[-1].id else 0 end' 2>/dev/null || echo "0")
108+
echo "last_comment_id=$LAST_ID" >> "$GITHUB_OUTPUT"
109+
100110
- name: Detect preexisting issues
101111
if: steps.pr-info.outputs.is_fork == 'false' && steps.cmd-type.outputs.is_preexisting == 'true'
102112
uses: anthropics/claude-code-action@v1
103113
with:
104114
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
105115
github_token: ${{ secrets.GITHUB_TOKEN }}
106116
show_full_output: false
117+
prompt: |
118+
Detect preexisting issues in PR ${{ steps.pr-info.outputs.number }}.
119+
120+
Follow these steps in order:
121+
122+
1. Use `gh pr diff ${{ steps.pr-info.outputs.number }}` to identify which lines are part of the PR diff.
123+
124+
2. Use `gh pr view ${{ steps.pr-info.outputs.number }}` to get the list of changed files, then read the full content of each changed markdown file.
125+
126+
3. Run all three review passes on each file per your instructions, but report ONLY issues on lines that are NOT part of the PR diff.
127+
128+
4. You MUST write your results ONLY to `/tmp/preexisting-issues.md` — always, even if there are no issues. Do not post a comment. Do not write anything else. Use this exact format:
129+
130+
## Preexisting issues
131+
### path/to/file.md
132+
- Line N: issue. Suggested change: '...'
133+
134+
Or if there are no issues:
135+
136+
## Preexisting issues
137+
None.
107138
claude_args: |
108139
--model claude-sonnet-4-5-20250929
109140
--allowedTools "Read,Write,Edit,Bash(gh pr view:*),Bash(gh pr diff:*)"
@@ -115,6 +146,7 @@ jobs:
115146
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
116147
PR_NUMBER: ${{ steps.pr-info.outputs.number }}
117148
REPO: ${{ github.repository }}
149+
LAST_COMMENT_ID: ${{ steps.pre-claude.outputs.last_comment_id }}
118150
run: |
119151
python3 << 'PYTHON_EOF'
120152
import os
@@ -123,6 +155,7 @@ jobs:
123155
124156
pr_number = os.environ['PR_NUMBER']
125157
repo = os.environ['REPO']
158+
last_comment_id = int(os.environ.get('LAST_COMMENT_ID', '0'))
126159
127160
FOOTER = (
128161
"\n\n---\n\n"
@@ -157,8 +190,28 @@ jobs:
157190
else:
158191
clean_body = '## Preexisting issues\nNone.' + FOOTER
159192
160-
subprocess.run(
161-
['gh', 'pr', 'comment', pr_number, '--repo', repo, '--body', clean_body],
162-
check=True,
193+
# Find the action's auto-posted comment (ID > last recorded, posted by a bot)
194+
result = subprocess.run(
195+
['gh', 'api', f'repos/{repo}/issues/{pr_number}/comments'],
196+
capture_output=True, text=True, check=True,
163197
)
198+
comments = json.loads(result.stdout)
199+
new_bot_comments = [c for c in comments
200+
if c['id'] > last_comment_id
201+
and c['user']['login'].endswith('[bot]')]
202+
203+
if new_bot_comments:
204+
# Replace the action's auto-comment in-place with our formatted output
205+
target_id = new_bot_comments[-1]['id']
206+
subprocess.run(
207+
['gh', 'api', f'repos/{repo}/issues/comments/{target_id}',
208+
'-X', 'PATCH', '--input', '-'],
209+
input=json.dumps({'body': clean_body}),
210+
capture_output=True, text=True, check=True,
211+
)
212+
else:
213+
subprocess.run(
214+
['gh', 'pr', 'comment', pr_number, '--repo', repo, '--body', clean_body],
215+
check=True,
216+
)
164217
PYTHON_EOF

.github/workflows/claude-documentation-reviewer.yml

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -251,16 +251,39 @@ jobs:
251251
return valid
252252
253253
def normalize_review_body(body):
254-
"""Convert any heading-formatted issue lines to bullet points."""
255-
lines = []
256-
for line in body.split('\n'):
257-
# Convert "### Line N: ..." or "#### Line N: ..." etc. to "- Line N: ..."
254+
"""Normalize issue formatting to single-line bullet points."""
255+
src = body.split('\n')
256+
result = []
257+
i = 0
258+
while i < len(src):
259+
line = src[i]
260+
261+
# Convert heading format: ### Line N: ... → - Line N: ...
258262
m = re.match(r'^#{1,6}\s+(Line \d+:.+)$', line)
259263
if m:
260-
lines.append(f'- {m.group(1)}')
261-
else:
262-
lines.append(line)
263-
return '\n'.join(lines)
264+
result.append(f'- {m.group(1)}')
265+
i += 1
266+
continue
267+
268+
# Convert bold format: **Line N: title** + sub-bullets → - Line N: single line
269+
m = re.match(r'^\*\*(Line \d+:.*?)\*\*\s*$', line)
270+
if m:
271+
title = m.group(1).rstrip('.')
272+
i += 1
273+
parts = []
274+
while i < len(src) and re.match(r'^\s*[-*]\s+', src[i]):
275+
sub = re.sub(r'^\s*[-*]\s+', '', src[i])
276+
sub = re.sub(r'^(Issue|Fix|Description|Suggested change):\s*', '', sub, flags=re.IGNORECASE)
277+
if sub.strip():
278+
parts.append(sub.strip().rstrip('.'))
279+
i += 1
280+
combined = f'- {title}. {". ".join(parts)}.' if parts else f'- {title}.'
281+
result.append(combined)
282+
continue
283+
284+
result.append(line)
285+
i += 1
286+
return '\n'.join(result)
264287
265288
# Read the review summary Claude wrote
266289
summary_path = '/tmp/review-summary.md'

0 commit comments

Comments
 (0)