Skip to content

Commit 156e9c7

Browse files
committed
fix: AI PR 리뷰 워크플로우 완전 수정
1 parent 9398d3f commit 156e9c7

File tree

1 file changed

+108
-89
lines changed

1 file changed

+108
-89
lines changed

.github/workflows/ai-pr-review.yml

Lines changed: 108 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -8,112 +8,131 @@ jobs:
88
ai-review:
99
runs-on: ubuntu-latest
1010
if: github.actor != 'dependabot[bot]'
11+
permissions:
12+
contents: read
13+
pull-requests: write
14+
issues: write
1115

1216
steps:
1317
- name: Checkout code
1418
uses: actions/checkout@v4
1519
with:
1620
fetch-depth: 0
1721

18-
- name: Get changed files
19-
id: changed-files
20-
run: |
21-
# Get the list of changed files
22-
CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep -E '\.(kt|kts|java|sql|yml|yaml|properties|md)$' | head -20)
23-
echo "files<<CHANGEDFILES" >> $GITHUB_OUTPUT
24-
echo "$CHANGED_FILES" >> $GITHUB_OUTPUT
25-
echo "CHANGEDFILES" >> $GITHUB_OUTPUT
26-
27-
- name: Read changed files content
28-
id: file-content
29-
run: |
30-
CONTENT=""
31-
if [ -n "${{ steps.changed-files.outputs.files }}" ]; then
32-
while IFS= read -r file; do
33-
if [ -f "$file" ] && [ -s "$file" ]; then
34-
echo "Processing: $file"
35-
CONTENT="${CONTENT}
36-
37-
--- File: $file ---
38-
"
39-
FILE_CONTENT=$(head -c 8000 "$file")
40-
CONTENT="${CONTENT}${FILE_CONTENT}"
41-
fi
42-
done <<< "${{ steps.changed-files.outputs.files }}"
43-
fi
44-
45-
echo "content<<FILECONTENT" >> $GITHUB_OUTPUT
46-
echo "$CONTENT" >> $GITHUB_OUTPUT
47-
echo "FILECONTENT" >> $GITHUB_OUTPUT
48-
4922
- name: AI Code Review
50-
id: ai-review
51-
run: |
52-
if [ -z "${{ steps.file-content.outputs.content }}" ]; then
53-
echo "No relevant files changed"
54-
echo "review=No Kotlin/configuration files were changed in this PR." >> $GITHUB_OUTPUT
55-
exit 0
56-
fi
57-
58-
REVIEW=$(curl -s -X POST "https://openrouter.ai/api/v1/chat/completions" \
59-
-H "Authorization: Bearer ${{ secrets.OPENROUTER_API_KEY }}" \
60-
-H "Content-Type: application/json" \
61-
-d '{
62-
"model": "x-ai/grok-4-fast:free",
63-
"messages": [
64-
{
65-
"role": "system",
66-
"content": "코드 리뷰어입니다. 다음 규칙만 검토하세요:\n\n## 검토 항목\n1. **글로벌 익셉션 처리**: @ControllerAdvice 사용, 표준 에러 응답\n2. **ApiResponse 사용**: 모든 API는 ApiResponse<T>로 감싸서 응답\n3. **Kotlin 최적화**: data class, null safety, when 표현식, 확장함수 등\n4. **ktlint 규칙**: 포맷팅, 네이밍 컨벤션\n\n## 응답 형식\n### ✅ 준수사항\n- [잘 지켜진 부분]\n\n### ❌ 위반사항 \n- [파일:라인] 문제점과 수정방법\n\n**점수**: X/10"
67-
},
68-
{
69-
"role": "user",
70-
"content": "다음 PR의 변경사항을 리뷰해주세요:\n\nPR 제목: ${{ github.event.pull_request.title }}\nPR 설명: ${{ github.event.pull_request.body }}\n\n변경된 파일들:\n${{ steps.file-content.outputs.content }}"
71-
}
72-
],
73-
"temperature": 0.3
74-
}')
75-
76-
# Extract the review content
77-
REVIEW_CONTENT=$(echo "$REVIEW" | jq -r '.choices[0].message.content // "리뷰 생성에 실패했습니다."')
78-
79-
echo "review<<EOF" >> $GITHUB_OUTPUT
80-
echo "$REVIEW_CONTENT" >> $GITHUB_OUTPUT
81-
echo "EOF" >> $GITHUB_OUTPUT
82-
83-
- name: Post review comment
8423
uses: actions/github-script@v7
8524
with:
8625
script: |
87-
const review = `${{ steps.ai-review.outputs.review }}`;
26+
const OPENROUTER_API_KEY = '${{ secrets.OPENROUTER_API_KEY }}';
8827
89-
// Find existing review comment
90-
const comments = await github.rest.issues.listComments({
28+
const diff = await github.rest.repos.compareCommits({
9129
owner: context.repo.owner,
9230
repo: context.repo.repo,
93-
issue_number: context.issue.number,
31+
base: context.payload.pull_request.base.sha,
32+
head: context.payload.pull_request.head.sha
9433
});
9534
96-
const botComment = comments.data.find(comment =>
97-
comment.user.login === 'github-actions[bot]' &&
98-
comment.body.includes('🤖 AI 코드 리뷰')
35+
const filesToReview = diff.data.files.filter(file =>
36+
file.patch &&
37+
!file.filename.includes('test/') &&
38+
!file.filename.includes('build/') &&
39+
(file.filename.endsWith('.kt') ||
40+
file.filename.endsWith('.kts') ||
41+
file.filename.endsWith('.java') ||
42+
file.filename.endsWith('.yml') ||
43+
file.filename.endsWith('.yaml') ||
44+
file.filename.endsWith('.md'))
9945
);
10046
101-
const commentBody = "## 🤖 AI 코드 리뷰\n\n" + review + "\n\n---\n<details>\n<summary>💡 리뷰 정보</summary>\n\n- 모델: Grok-4-fast\n- 리뷰 시간: " + new Date().toLocaleString('ko-KR', {timeZone: 'Asia/Seoul'}) + "\n- PR: #${{ github.event.number }}\n\n</details>";
47+
if (filesToReview.length === 0) {
48+
console.log('No files to review');
49+
return;
50+
}
10251
103-
if (botComment) {
104-
// Update existing comment
105-
await github.rest.issues.updateComment({
106-
owner: context.repo.owner,
107-
repo: context.repo.repo,
108-
comment_id: botComment.id,
109-
body: commentBody
110-
});
111-
} else {
112-
// Create new comment
113-
await github.rest.issues.createComment({
114-
owner: context.repo.owner,
115-
repo: context.repo.repo,
116-
issue_number: context.issue.number,
117-
body: commentBody
52+
console.log(`Found ${filesToReview.length} files to review`);
53+
54+
let overallReview = `## 🤖 AI 코드 리뷰\n\n`;
55+
56+
let allChanges = '';
57+
const filesSummary = [];
58+
59+
for (const file of filesToReview) {
60+
filesSummary.push(`- ${file.filename} (+${file.additions}/-${file.deletions})`);
61+
allChanges += `\n### ${file.filename}\n`;
62+
allChanges += `\`\`\`diff\n${file.patch}\n\`\`\`\n`;
63+
}
64+
65+
try {
66+
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
67+
method: 'POST',
68+
headers: {
69+
'Authorization': `Bearer ${OPENROUTER_API_KEY}`,
70+
'Content-Type': 'application/json'
71+
},
72+
body: JSON.stringify({
73+
model: 'x-ai/grok-4-fast:free',
74+
messages: [{
75+
role: 'system',
76+
content: `코드 리뷰어입니다. 다음 규칙만 검토하세요:
77+
78+
## 검토 항목
79+
1. **글로벌 익셉션 처리**: @ControllerAdvice 사용, 표준 에러 응답
80+
2. **ApiResponse 사용**: 모든 API는 ApiResponse<T>로 감싸서 응답
81+
3. **Kotlin 최적화**: data class, null safety, when 표현식, 확장함수 등
82+
4. **ktlint 규칙**: 포맷팅, 네이밍 컨벤션
83+
84+
## 응답 형식
85+
### ✅ 준수사항
86+
- [잘 지켜진 부분]
87+
88+
### ❌ 위반사항
89+
- [파일:라인] 문제점과 수정방법
90+
91+
**점수**: X/10`
92+
}, {
93+
role: 'user',
94+
content: `다음 PR의 변경사항을 리뷰해주세요:
95+
96+
PR 제목: ${{ github.event.pull_request.title }}
97+
PR 설명: ${{ github.event.pull_request.body }}
98+
99+
변경된 파일:
100+
${filesSummary.join('\n')}
101+
102+
전체 변경사항:
103+
${allChanges}`
104+
}],
105+
temperature: 0.3
106+
})
118107
});
119-
}
108+
109+
if (response.ok) {
110+
const result = await response.json();
111+
overallReview += result.choices[0].message.content + '\n\n';
112+
console.log('✅ API review completed successfully!');
113+
} else {
114+
const errorText = await response.text();
115+
console.error('API request failed:', response.status, errorText);
116+
overallReview += `> ⚠️ 리뷰 실패: ${response.status} - ${errorText}\n\n`;
117+
}
118+
} catch (error) {
119+
console.error('Error during review:', error);
120+
overallReview += `> ⚠️ 리뷰 실패: ${error.message}\n\n`;
121+
}
122+
123+
overallReview += `---\n`;
124+
overallReview += `<details>\n<summary>💡 리뷰 정보</summary>\n\n`;
125+
overallReview += `- 모델: Grok-4-fast\n`;
126+
overallReview += `- 리뷰 시간: ${new Date().toLocaleString('ko-KR', {timeZone: 'Asia/Seoul'})}\n`;
127+
overallReview += `- PR: #${{ github.event.number }}\n\n`;
128+
overallReview += `</details>`;
129+
130+
await github.rest.pulls.createReview({
131+
owner: context.repo.owner,
132+
repo: context.repo.repo,
133+
pull_number: context.payload.pull_request.number,
134+
body: overallReview,
135+
event: 'COMMENT'
136+
});
137+
138+
console.log(`✅ Review completed for ${filesToReview.length} files!`);

0 commit comments

Comments
 (0)