Skip to content

Commit 282c484

Browse files
authored
Merge pull request #63 from DaleStudy/ai-analysis-any-solution-pr
fix: run AI analysis on solution PRs without a WEEK title
2 parents f3fdd50 + ef1cb4b commit 282c484

3 files changed

Lines changed: 59 additions & 11 deletions

File tree

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ wrangler.jsonc Worker 설정과 공개 값(vars)
5454
| `pull_request` (`opened`, `reopened`, `synchronize`) | Week 검사, AI 풀이 분석 check run(`tagPatterns`), 학습 현황 댓글(`postLearningStatus`) |
5555
| `issue_comment`, `pull_request_review_comment` (`created`) | `@dalestudy` 멘션에 응답한다. `approve`·`승인` 이면 PR 을 승인하고, 그 밖에는 AI 리뷰를 단다 |
5656

57-
Week 검사는 제목이 `WEEK NN Solutions` 형태인 풀이 PR 만 대상으로 하고, 경고 댓글은 봇이 단 것만 지운다.
57+
Week 검사는 제목이 `WEEK NN Solutions` 형태인 풀이 PR 만 대상으로 하고, 경고 댓글은 봇이 단 것만 지운다. AI 풀이 분석과 학습 현황 댓글은 제목과 상관없이 돌리고(`[user] Onboarding` 같은 PR 도 포함), 풀이 파일이 없으면 두 핸들러가 AI 를 부르지 않고 건너뛴다.
5858

5959
### AI 작업을 별도 invocation 으로 나누기
6060

‎handlers/webhooks.js‎

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,11 @@ async function handleProjectsV2ItemEvent(payload, env) {
211211

212212
/**
213213
* Pull Request 이벤트 처리
214-
* - opened/reopened: Week 설정 체크 + 알고리즘 패턴 태깅(분석하지 않았거나 변경된 파일만)
215-
* - synchronize: 알고리즘 패턴 태깅만 (분석하지 않았거나 변경된 파일만, Week 체크 스킵)
214+
* - opened/reopened: Week 설정 체크(제목이 WEEK NN 형태인 풀이 PR만) + AI 풀이 분석
215+
* - synchronize: AI 풀이 분석만 (Week 체크 스킵)
216+
*
217+
* AI 풀이 분석과 학습 현황은 제목과 상관없이 돌린다. Onboarding PR 처럼 WEEK 가 없는
218+
* 제목에도 풀이 파일이 있을 수 있고, 풀이 파일이 없으면 두 핸들러가 AI 를 부르지 않고 건너뛴다.
216219
*/
217220
async function handlePullRequestEvent(payload, env, ctx) {
218221
const action = payload.action;
@@ -230,17 +233,13 @@ async function handlePullRequestEvent(payload, env, ctx) {
230233
const repoName = payload.repository.name;
231234
const prNumber = pr.number;
232235

233-
// 풀이 제출 PR이 아니면 스킵 (early exit - GitHub API 호출 전에)
234-
if (!isSolutionPR(pr.title)) {
235-
console.log(`Skipping PR #${prNumber}: not a solution PR`);
236-
return corsResponse({ message: "Ignored: not a solution PR" });
237-
}
238-
239236
const appToken = await generateGitHubAppToken(env);
240237
let weekValue = null;
241238

242-
// Week 체크는 opened/reopened일 때만 (synchronize는 이미 설정됐을 가능성 높음)
243-
if (action === "opened" || action === "reopened") {
239+
// Week 체크는 풀이 PR 이 opened/reopened 일 때만 (synchronize 는 이미 설정됐을 가능성 높음)
240+
if (!isSolutionPR(pr.title)) {
241+
console.log(`Skipping Week check for PR #${prNumber}: not a WEEK solution PR`);
242+
} else if (action === "opened" || action === "reopened") {
244243
console.log(`New PR ${action}: #${prNumber}`);
245244

246245
// 잠시 대기 후 체크 (프로젝트 추가 시간 고려)

‎handlers/webhooks.test.js‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,52 @@ describe("handlePullRequestEvent — AI 핸들러 디스패치", () => {
383383
});
384384

385385
});
386+
387+
describe("handlePullRequestEvent — WEEK 가 없는 제목", () => {
388+
const onboardingPayload = {
389+
action: "opened",
390+
organization: { login: "DaleStudy" },
391+
repository: {
392+
name: "leetcode-study",
393+
owner: { login: "DaleStudy" },
394+
},
395+
pull_request: {
396+
number: 43,
397+
title: "[testuser] Onboarding",
398+
labels: [],
399+
head: { sha: "head-sha" },
400+
user: { login: "testuser" },
401+
},
402+
};
403+
404+
beforeEach(() => {
405+
vi.clearAllMocks();
406+
globalThis.fetch = vi.fn().mockResolvedValue({
407+
ok: true,
408+
json: () => Promise.resolve({}),
409+
});
410+
});
411+
412+
it("Week 체크는 건너뛰지만 AI 핸들러는 디스패치한다", async () => {
413+
const ctx = { waitUntil: vi.fn() };
414+
const env = {
415+
AI: FAKE_AI,
416+
INTERNAL_SECRET: "fake-secret",
417+
WORKER_URL: "https://worker.test",
418+
};
419+
420+
const response = await handleWebhook(
421+
makeRequest("pull_request", onboardingPayload),
422+
env,
423+
ctx
424+
);
425+
426+
expect(response.status).toBe(200);
427+
expect(handleWeekComment).not.toHaveBeenCalled();
428+
expect(ctx.waitUntil).toHaveBeenCalledTimes(2);
429+
430+
const fetchedUrls = globalThis.fetch.mock.calls.map(([url]) => url);
431+
expect(fetchedUrls).toContain("https://worker.test/internal/tag-patterns");
432+
expect(fetchedUrls).toContain("https://worker.test/internal/learning-status");
433+
});
434+
});

0 commit comments

Comments
 (0)