Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ wrangler.jsonc Worker 설정과 공개 값(vars)
| `pull_request` (`opened`, `reopened`, `synchronize`) | Week 검사, AI 풀이 분석 check run(`tagPatterns`), 학습 현황 댓글(`postLearningStatus`) |
| `issue_comment`, `pull_request_review_comment` (`created`) | `@dalestudy` 멘션에 응답한다. `approve`·`승인` 이면 PR 을 승인하고, 그 밖에는 AI 리뷰를 단다 |

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

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

Expand Down
12 changes: 9 additions & 3 deletions handlers/webhooks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,10 @@ describe("handlePullRequestEvent — WEEK 가 없는 제목", () => {
});
});

it("Week 체크는 건너뛰지만 AI 핸들러는 디스패치한다", async () => {
it.each([
["[testuser] Onboarding", true],
["docs: update README", false],
])("%s: Week 검사 여부는 %s이고 AI 핸들러는 디스패치한다", async (title, checksWeek) => {
const ctx = { waitUntil: vi.fn() };
const env = {
AI: FAKE_AI,
Expand All @@ -418,13 +421,16 @@ describe("handlePullRequestEvent — WEEK 가 없는 제목", () => {
};

const response = await handleWebhook(
makeRequest("pull_request", onboardingPayload),
makeRequest("pull_request", {
...onboardingPayload,
pull_request: { ...onboardingPayload.pull_request, title },
}),
env,
ctx
);

expect(response.status).toBe(200);
expect(handleWeekComment).not.toHaveBeenCalled();
expect(handleWeekComment).toHaveBeenCalledTimes(checksWeek ? 1 : 0);
expect(ctx.waitUntil).toHaveBeenCalledTimes(2);

const fetchedUrls = globalThis.fetch.mock.calls.map(([url]) => url);
Expand Down
6 changes: 3 additions & 3 deletions utils/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

import { ALLOWED_ORG } from "./constants.js";

// "[dale] WEEK 07 Solutions" 같은 풀이 제출 PR 제목.
// 제목 규칙이 강제되지 않아 "Solutions"를 빼먹는 PR이 있으므로 주차만 확인한다.
const SOLUTION_TITLE_PATTERN = /week\s*\d+/i;
// "[dale] WEEK 07 Solutions", "[dale] Onboarding" 같은 풀이 제출 PR 제목.
// 제목 규칙이 강제되지 않아 "Solutions"를 빼먹는 PR이 있으므로 주차 또는 Onboarding을 확인한다.
const SOLUTION_TITLE_PATTERN = /week\s*\d+|onboarding/i;

/**
* Organization 검증
Expand Down
6 changes: 6 additions & 0 deletions utils/validation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ describe("isSolutionPR", () => {
expect(isSolutionPR("[user] WEEK15 Solutions")).toBe(true);
});

it("Onboarding이 포함된 제목도 풀이 PR로 인식한다", () => {
expect(isSolutionPR("[user] Onboarding")).toBe(true);
expect(isSolutionPR("[user] onboarding")).toBe(true);
expect(isSolutionPR("[user] ONBOARDING Solutions")).toBe(true);
});

it("봇 테스트나 저장소 정리 PR은 제외한다", () => {
expect(isSolutionPR("260806 봇 댓글 기능 테스트")).toBe(false);
expect(isSolutionPR("bot test")).toBe(false);
Expand Down
Loading