Skip to content

ci: PR AI 설명 워크플로우 및 스크립트 추가#9

Merged
chanwoo7 merged 13 commits intomainfrom
ci/pr-ai-description-v3
Mar 15, 2026
Merged

ci: PR AI 설명 워크플로우 및 스크립트 추가#9
chanwoo7 merged 13 commits intomainfrom
ci/pr-ai-description-v3

Conversation

@chanwoo7
Copy link
Member

@chanwoo7 chanwoo7 commented Mar 15, 2026

PR Summary

GitHub Actions 워크플로우와 PR AI 요약 생성 관련 스크립트/라이브러리/테스트를 추가합니다.
PR 본문에 AI가 생성한 요약 블록을 삽입하고 라벨/담당자/제목(optional)을 자동으로 적용하는 기능을 제공합니다.
로컬 dry-run 스크립트와 유닛 테스트를 포함하여 안전한 실행과 오류 변환/로깅을 처리하도록 구현했습니다.

  • .github/workflows/pr-ai-description.yml로 PR 이벤트 트리거 워크플로우 추가
  • AI 요약 생성 로직을 담은 .github/scripts/pr-ai-description.mjs 및 라이브러리 pr-ai-description-lib.mjs 추가
  • dry-run용 pr-ai-dry-run.mjs와 유닛 테스트 추가로 로컬 검증 및 CI 테스트 제공
  • CodeRabbit 설정(.coderabbit.yaml)으로 파일별 리뷰 지침 추가
  • package.jsonlangsmithopenai 의존성 추가

Changes

8 files changed

File Changes
.coderabbit.yaml CodeRabbit 설정 파일을 추가했습니다. 파일/경로별 리뷰 지침과 pre-merge 체크 설정을 포함하여 NestJS, GraphQL, Prisma, CI 스크립트 등 도메인별 리뷰 가이드를 정의합니다.
.github/scripts/__tests__/pr-ai-description-lib.spec.mjs PR AI 라이브러리의 유닛 테스트를 추가했습니다. 파일 필터링, diff 절단, 스키마 검증, 블록 삽입 동작 등을 검증합니다.
.github/scripts/__tests__/pr-ai-description.spec.mjs 전체 PR AI 스크립트의 유닛 테스트를 추가했습니다. OpenAI 응답 처리, 에러 변환, PR 업데이트 페이로드 빌드 등 통합 흐름을 검증합니다.
.github/scripts/pr-ai-description-lib.mjs AI 요약 JSON 스키마, diff 처리 유틸, 민감정보 마스킹, 라벨 필터링, PR 본문 블록 렌더링/업서트 등 핵심 유틸 함수를 구현한 라이브러리를 추가했습니다.
.github/scripts/pr-ai-description.mjs PR 이벤트를 받아 diff를 수집하고 OpenAI에 프롬프트를 전달하여 검증된 JSON 요약을 받고 PR 본문/제목/라벨/담당자를 업데이트하는 메인 워크플로우 스크립트를 추가했습니다. 요청/응답 타임아웃, 오류 매핑, Langsmith 추적 연동 등을 포함합니다.
.github/scripts/pr-ai-dry-run.mjs PR AI 동작을 실제 PR 변경 없이 콘솔 출력으로 검증할 수 있는 dry-run 스크립트를 추가했습니다. 로컬 브랜치/PR 모드 모두 지원합니다.
.github/workflows/pr-ai-description.yml PR 생성/동기화 시 동작하는 GitHub Actions 워크플로우를 추가했습니다. 체크아웃, Node 셋업, 유닛 테스트 실행 후 pr-ai-description.mjs를 실행하도록 구성되어 있습니다.
package.json langsmithopenai 의존성을 추가하여 OpenAI 호출 및 Langsmith 트레이싱 래퍼를 사용하도록 변경했습니다.

Impact

  • PR이 생성/동기화될 때 자동으로 AI 요약 블록이 PR 본문에 삽입될 수 있습니다. (본문 변경 가능)
  • AI가 제안한 title이 기존 제목과 다르면 PR 제목을 업데이트할 수 있습니다. (라벨 ai-title-lock이 있으면 변경되지 않음)
  • 레포 라벨이 자동으로 추가되고 담당자(chanwoo7)가 추가될 수 있습니다. 퍼미션이 없으면 건너뜁니다.
  • OpenAI / Langsmith API 키가 필요하며, 잘못된 키/요금제/쿼터로 인해 워크플로우 실패 가능성이 있습니다.
  • 포크 PR은 정책상 스킵되도록 되어 있어 포크에서 동작하지 않습니다.

Checklist

  • 워크플로우의 GITHUB_TOKEN / OPENAI_API_KEY / LANGSMITH_API_KEY 등 시크릿이 올바르게 설정되어 있는지 확인하세요
  • .github/workflows/pr-ai-description.yml의 env 섹션에 자리표시자([REDACTED] secrets.GITHUB_TOKEN }} 등)가 남아있는지 확인하고 올바르게 수정하세요
  • 추가된 유닛 테스트(.github/scripts/__tests__/*.spec.mjs)가 CI에서 통과하는지 확인하세요 (node --test 실행)
  • 로컬에서 pr-ai-dry-run.mjs를 실행해 실제 PR 업데이트 없이 출력된 AI 요약과 블록 형식을 검증하세요
  • 스크립트 내부에 민감정보가 노출되지 않았는지(maskSensitiveContent 동작 포함) 확인하세요 (로그/스텝 서머리 확인)
  • package.json 의존성 추가(langsmith, openai) 후 로컬/CI에서 설치 및 빌드가 정상 동작하는지 확인하세요
  • 워크플로우 권한(permissions: contents/read, pull-requests: write, issues: write)이 의도와 맞는지 검토하세요
  • 레포 라벨 목록과 AI가 제안하는 라벨 매핑이 올바르게 동작하는지 확인하세요 (대소문자/중복 처리)
  • AI가 생성한 JSON이 스키마에 맞지 않을 때의 오류 처리가 적절한지(로그/스텝 요약) 확인하세요

Dependencies

  • langsmith: ^0.5.7
  • openai: ^6.27.0

Summary by CodeRabbit

  • New Features

    • AI 기반 PR 설명 자동 생성 및 PR 본문/제목/라벨 제안 기능 추가
    • 워크플로우로 실행되는 자동화 스크립트와 별도 Dry-Run(수정 미적용) 실행 모드 제공
    • 저장소 전반의 자동 코드리뷰/테스트/보안 지침을 설정하는 구성 추가
  • Tests

    • PR AI 설명 관련 포괄적 유닛/통합 테스트 추가 (필터링, 트렁케이션, 검증, 블록 삽입 등)
  • Chores

    • AI 통합을 위한 런타임 의존성 추가

@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

Warning

Rate limit exceeded

@chanwoo7 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 56 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 84bd5aae-c7b5-48e0-aac9-b7a65228581d

📥 Commits

Reviewing files that changed from the base of the PR and between 0627432 and 976aa76.

📒 Files selected for processing (2)
  • .github/scripts/pr-ai-description-lib.mjs
  • .github/scripts/pr-ai-dry-run.mjs
📝 Walkthrough

워크스루

OpenAI 및 GitHub API를 이용해 PR의 변경(diff)을 분석하고 구조화된 요약(제목, 레이블, 본문)을 생성해 PR을 자동으로 업데이트하는 기능과 이를 위한 설정·유틸·스크립트·테스트·워크플로우를 추가합니다.

변경사항

Cohort / File(s) 요약
설정
\.coderabbit.yaml
코드리뷰 규칙, 경로별 검사 지침, 민감정보 마스킹 및 사전 병합 검사 설정 추가.
핵심 라이브러리
.github/scripts/pr-ai-description-lib.mjs
Diff 필터링/글롭, 트런케이션, 민감정보 마스킹, JSON 스키마 검증, 요약 블록 생성/삽입, 레이블 필터링 등 공개 API 다수 추가(~700줄).
메인 스크립트
.github/scripts/pr-ai-description.mjs
GitHub + OpenAI 연동 메인 워크플로우: PR 로드, diff 수집(비상시 로컬 git), 프롬프트 생성, OpenAI 호출, 결과 검증 및 PR 업데이트 로직(~1100줄).
드라이런
.github/scripts/pr-ai-dry-run.mjs
PR 업데이트 없이 콘솔 출력으로 흐름 검증하는 드라이런 유틸; PR/브랜치 기반 모드, 동일한 필터·프롬프트 로직 포함(~440줄).
테스트
.github/scripts/__tests__/pr-ai-description-lib.spec.mjs, .github/scripts/__tests__/pr-ai-description.spec.mjs
라이브러리·스크립트 동작 검증용 단위/통합 테스트 추가: 필터링, 트런케이션, 스키마 검증, 블록 삽입, 에러 매핑 등(~470줄).
워크플로우 및 의존성
.github/workflows/pr-ai-description.yml, package.json
PR 이벤트 트리거 워크플로우 추가 및 OpenAI/LangSmith 패키지 의존성 명시.

시퀀스 다이어그램

sequenceDiagram
    participant GH as GitHub API
    participant Script as PR AI Script
    participant LM as LangSmith
    participant AI as OpenAI API
    participant Git as Git (Fallback)

    Script->>GH: 레이블 목록 조회
    GH-->>Script: 레이블 반환

    Script->>GH: PR 정보 조회 (base/head SHA)
    GH-->>Script: PR 정보

    rect rgba(100, 149, 237, 0.5)
        Note over Script,Git: Diff 수집
        Script->>GH: Compare API로 diff 요청
        alt API 성공
            GH-->>Script: Diff 데이터
        else API 실패 또는 제외조건 충족
            Script->>Git: 로컬 git diff 생성
            Git-->>Script: Diff 데이터
        end
    end

    rect rgba(144, 238, 144, 0.5)
        Note over Script: Diff 전처리
        Script->>Script: 민감정보 마스킹
        Script->>Script: 파일 필터링 및 트런케이션
    end

    Script->>Script: AI 프롬프트 구성
    Script->>LM: 추적 기록 시작
    Script->>AI: Chat Completion 요청 (JSON 스키마 응답)
    AI-->>Script: 구조화된 요약 응답
    Script->>LM: 추적 기록 완료
    Script->>Script: 응답 스키마 검증
    rect rgba(255, 182, 193, 0.5)
        Note over Script,GH: PR 업데이트 적용
        Script->>GH: 제목/본문 패치
        GH-->>Script: 확인
        Script->>GH: 담당자 할당 / 레이블 추가
        GH-->>Script: 확인
    end
Loading

예상 코드 리뷰 소요시간

🎯 4 (복잡) | ⏱️ ~55분

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목 'ci: PR AI 설명 워크플로우 및 스크립트 추가'는 변경 내용의 핵심을 명확하게 요약합니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ci/pr-ai-description-v3
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added ⚙️ Setting 개발환경 세팅 ✅ Test 테스트 관련 labels Mar 15, 2026
@github-actions github-actions bot changed the title Ci/pr ai description v3 ci: PR AI 설명 워크플로우 및 스크립트 추가 Mar 15, 2026
@github-actions github-actions bot added the ⭐️ Feature 기능 개발 label Mar 15, 2026
@chanwoo7 chanwoo7 added 🌏 Deploy 배포 관련 and removed ⭐️ Feature 기능 개발 labels Mar 15, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/scripts/pr-ai-description-lib.mjs:
- Around line 401-416: maskSensitiveContent 함수의 현재 정규식이 제한적이어서 OPENAI_API_KEY,
DATABASE_URL, client_secret, AWS_SECRET_ACCESS_KEY 같은 흔한 env/키/URL 패턴을 마스킹하지
못합니다; maskSensitiveContent를 수정해 env-var 스타일 키(예:
/[A-Z0-9_]*(_KEY|_SECRET|_TOKEN|_PASSWORD|API_KEY|ACCESS_KEY|CLIENT_SECRET)/i),
키=값 및 키:값 할당 패턴, URL에 포함된 사용자정보(예: https?://user:pass@host)와 .env 형식(예:
/^(\w+)=.+$/m) 등을 추가로 검사해 값 부분을 '[REDACTED]'로 바꾸고, 기존
Authorization/Bearer/password/secret/token/apiKey 규칙과 결합하여 포괄적으로 처리하세요; 또한
민감파일(.env, .env.*, *credentials*, key files)은
.github/scripts/pr-ai-description.mjs 및 .github/scripts/pr-ai-dry-run.mjs에서 전송 전
diff 대상에서 제외하도록 배포 스크립트 또는 업로드 로직에서 파일 이름 필터링을 추가하세요.
- Around line 556-558: The PR table breaks when change.description contains line
breaks; before escaping pipes, normalize newlines in summary.changes entries
(e.g., in the loop that creates escapedDesc) by replacing \r\n, \r, and \n with
a single '<br>' or a space, then escape '|' and push the row; update the logic
around summary.changes / change.description and the escapedDesc construction
used in lines.push so all newlines are normalized prior to rendering the
Markdown table (validateAiSummaryJson() can still allow multiline input).

In @.github/scripts/pr-ai-description.mjs:
- Around line 358-375: The current fetch with '--depth=1' then running git diff
between baseSha and headSha can fail in shallow checkouts because merge-base may
be missing; update the logic around runGitCommand/fetch and the subsequent
range/nameStatus steps to detect when git merge-base (or git diff) fails and, as
a fallback, deepen the shallow clone (e.g. retry runGitCommand(['fetch',
'--deepen', <N>, 'origin']) or re-fetch without depth) in a loop until git
merge-base baseSha headSha succeeds or a max depth/attempts is reached, then
recompute range and call runGitCommand([... 'diff' ...]) so nameStatus is
produced from a repo that contains the merge base.

In @.github/scripts/pr-ai-dry-run.mjs:
- Around line 229-233: Wrap the call that builds the PR stub's user login (the
runGit(['config', 'user.name']) invocation inside the pr object) in a try/catch
(or otherwise call git config --get user.name and handle non-zero exit) so a
missing git user doesn't cause the script to exit; if the git call throws or
returns nothing, set user.login to 'local' (i.e., ensure the construction of
pr.user uses a safe getter with fallback to 'local' instead of relying on ||
after a failing runGit call).
- Around line 153-173: runWithPr() currently passes the raw compare result
straight to normalizeDiffEntries(), bypassing the same missing-patch and
maxFiles checks used in the real workflow; update runWithPr() to reuse the
shared diff-collection flow: call fetchCompareDiff(baseSha, headSha), then run
buildExcludeGlobs() -> filterDiffFiles() and perform the same "no included
files" / "missing patch" / "too many files" checks and git-fallback or abort
logic used by the production diff collector before calling
normalizeDiffEntries(), so generateAndPrint() receives the same diffEntries as
the real workflow; adjust fetchCompareDiff(), filterDiffFiles(), or expose a
single collectDiffEntries() helper if needed to avoid duplicating logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b3f0b9bd-434a-42d0-8243-0d968b3f2614

📥 Commits

Reviewing files that changed from the base of the PR and between 64808a2 and c8d5d73.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • .coderabbit.yaml
  • .github/scripts/__tests__/pr-ai-description-lib.spec.mjs
  • .github/scripts/__tests__/pr-ai-description.spec.mjs
  • .github/scripts/pr-ai-description-lib.mjs
  • .github/scripts/pr-ai-description.mjs
  • .github/scripts/pr-ai-dry-run.mjs
  • .github/workflows/pr-ai-description.yml
  • package.json

Comment on lines +401 to +416
export function maskSensitiveContent(text) {
if (typeof text !== 'string' || text.length === 0) {
return '';
}

return text
.replaceAll(/(Authorization\s*[:=]\s*)([^\n\r]+)/gi, '$1[REDACTED]')
.replaceAll(/Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi, 'Bearer [REDACTED]')
.replaceAll(
/(\"?(?:password|secret|token|apiKey)\"?\s*[:=]\s*)\"([^\"\n\r]*)\"/gi,
'$1"[REDACTED]"',
)
.replaceAll(
/(\"?(?:password|secret|token|apiKey)\"?\s*[:=]\s*)([^\s,\n\r]+)/gi,
'$1[REDACTED]',
);
Copy link

@coderabbitai coderabbitai bot Mar 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

현재 마스킹 규칙으로는 비밀값이 그대로 외부 서비스로 전송될 수 있습니다.

지금 정규식은 Authorization, Bearer, 그리고 키 이름이 정확히 password|secret|token|apiKey인 경우만 가립니다. 그래서 OPENAI_API_KEY=..., DATABASE_URL=..., client_secret=..., AWS_SECRET_ACCESS_KEY=... 같은 흔한 형태는 그대로 남고, 이 값들은 이후 .github/scripts/pr-ai-description.mjs.github/scripts/pr-ai-dry-run.mjs에서 OpenAI/LangSmith로 전송됩니다. env-var/URL/credential 파일 패턴까지 마스킹 범위를 넓히고, .env*나 키 파일은 아예 diff 대상에서 제외하는 방어선도 같이 두는 편이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/scripts/pr-ai-description-lib.mjs around lines 401 - 416,
maskSensitiveContent 함수의 현재 정규식이 제한적이어서 OPENAI_API_KEY, DATABASE_URL,
client_secret, AWS_SECRET_ACCESS_KEY 같은 흔한 env/키/URL 패턴을 마스킹하지 못합니다;
maskSensitiveContent를 수정해 env-var 스타일 키(예:
/[A-Z0-9_]*(_KEY|_SECRET|_TOKEN|_PASSWORD|API_KEY|ACCESS_KEY|CLIENT_SECRET)/i),
키=값 및 키:값 할당 패턴, URL에 포함된 사용자정보(예: https?://user:pass@host)와 .env 형식(예:
/^(\w+)=.+$/m) 등을 추가로 검사해 값 부분을 '[REDACTED]'로 바꾸고, 기존
Authorization/Bearer/password/secret/token/apiKey 규칙과 결합하여 포괄적으로 처리하세요; 또한
민감파일(.env, .env.*, *credentials*, key files)은
.github/scripts/pr-ai-description.mjs 및 .github/scripts/pr-ai-dry-run.mjs에서 전송 전
diff 대상에서 제외하도록 배포 스크립트 또는 업로드 로직에서 파일 이름 필터링을 추가하세요.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai
diff에 OPENAI_API_KEY 실제값 같은 게 나오려면 누군가 시크릿을 코드에 하드코딩해서 커밋해야 함.
.env는 이미 gitignore 대상이고, 이 함수는 만약 남아있을 경우의 2차 방어선이어야 함.
현재 패턴으로 실무적으로 충분하다고 생각하는데 어떻게 생각함?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chanwoo7 맞습니다. 지적하신 점이 타당합니다.

말씀대로 실제 비밀값이 diff에 등장하려면 누군가가 코드에 직접 하드코딩해서 커밋해야 하고, .env는 gitignore로 이미 배제되어 있으니 이 함수가 처리해야 하는 현실적인 케이스는 상당히 좁습니다. 현재 패턴(Authorization, Bearer, password/secret/token/apiKey 키워드)은 코드 상에서 가장 흔히 실수로 커밋되는 형태를 잘 커버하고 있습니다.

굳이 보완이 필요한 edge case를 꼽자면:

  • DB 연결 문자열(postgresql://user:pass@host) 같은 URL 내 userinfo
  • .env.example 등 실값을 실수로 채운 채 커밋된 파일

하지만 이것도 2차 방어선의 범위를 어디까지 잡느냐의 설계 판단이고, 현재 구현으로도 실무적으로 합리적이라는 의견에 동의합니다. 현재 패턴을 유지하겠습니다.


✏️ Learnings added
Learnt from: chanwoo7
Repo: CaQuick/caquick-be PR: 9
File: .github/scripts/pr-ai-description-lib.mjs:409-424
Timestamp: 2026-03-15T16:10:56.277Z
Learning: In .github/scripts/pr-ai-description-lib.mjs, the maskSensitiveContent function is intentionally designed as a secondary defense layer only. The primary protection is .gitignore (which excludes .env files). The current regex patterns covering Authorization, Bearer, and password/secret/token/apiKey keywords are considered sufficient by the maintainer for this secondary role. Do not flag missing env-var style patterns (e.g., OPENAI_API_KEY, DATABASE_URL) as a critical issue.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +358 to +375
runGitCommand([
'fetch',
'--no-tags',
'--prune',
'--depth=1',
'origin',
baseSha,
headSha,
]);

const range = `${baseSha}...${headSha}`;
const nameStatus = runGitCommand([
'diff',
'--no-color',
'--diff-algorithm=histogram',
'--name-status',
range,
]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

git fallback이 shallow history에서는 복구 경로가 되지 못합니다.

여기서는 --depth=1baseSha/headSha만 가져온 뒤 git diff ${baseSha}...${headSha}를 실행합니다. 현재 워크플로도 shallow checkout이라 merge-base가 히스토리에 없는 경우가 많아서, compare API가 빠진 PR에서 오히려 no merge base로 다시 실패할 수 있습니다. fallback 경로라면 merge-base를 계산할 수 있을 만큼 히스토리를 더 가져오거나 git merge-base가 성공할 때까지 deepen해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/scripts/pr-ai-description.mjs around lines 358 - 375, The current
fetch with '--depth=1' then running git diff between baseSha and headSha can
fail in shallow checkouts because merge-base may be missing; update the logic
around runGitCommand/fetch and the subsequent range/nameStatus steps to detect
when git merge-base (or git diff) fails and, as a fallback, deepen the shallow
clone (e.g. retry runGitCommand(['fetch', '--deepen', <N>, 'origin']) or
re-fetch without depth) in a loop until git merge-base baseSha headSha succeeds
or a max depth/attempts is reached, then recompute range and call
runGitCommand([... 'diff' ...]) so nameStatus is produced from a repo that
contains the merge base.

Comment on lines +153 to +173
log(`diff 수집 중... (${baseSha.slice(0, 8)}...${headSha.slice(0, 8)})`);
const compareFiles = fetchCompareDiff(baseSha, headSha);

const excludeGlobs = buildExcludeGlobs();
const { included } = filterDiffFiles(compareFiles, excludeGlobs);

if (included.length === 0) {
throw new Error('필터링 후 diff 파일이 없습니다.');
}

const diffEntries = normalizeDiffEntries(included);
const repositoryLabels = fetchRepositoryLabels();

return generateAndPrint({
pr,
diffEntries,
diffSource: 'compare',

repositoryLabels,
...options,
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

PR 모드 dry-run이 실제 워크플로와 다른 diff를 요약합니다.

runWithPr()는 compare 결과를 바로 normalizeDiffEntries()로 넘겨서 patch 누락 감지와 maxFiles 상한을 모두 건너뜁니다. 실제 워크플로의 .github/scripts/pr-ai-description.mjs는 compare missing patch / too many files를 감지한 뒤 git fallback 또는 중단을 결정하므로, large diff나 binary 파일이 섞인 PR에서 dry-run만 불완전한 요약을 만들 수 있습니다. 공통 diff 수집 로직으로 묶지 않으면 두 경로가 계속 어긋납니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/scripts/pr-ai-dry-run.mjs around lines 153 - 173, runWithPr()
currently passes the raw compare result straight to normalizeDiffEntries(),
bypassing the same missing-patch and maxFiles checks used in the real workflow;
update runWithPr() to reuse the shared diff-collection flow: call
fetchCompareDiff(baseSha, headSha), then run buildExcludeGlobs() ->
filterDiffFiles() and perform the same "no included files" / "missing patch" /
"too many files" checks and git-fallback or abort logic used by the production
diff collector before calling normalizeDiffEntries(), so generateAndPrint()
receives the same diffEntries as the real workflow; adjust fetchCompareDiff(),
filterDiffFiles(), or expose a single collectDiffEntries() helper if needed to
avoid duplicating logic.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
.github/scripts/pr-ai-description-lib.mjs (3)

6-14: ⚠️ Potential issue | 🔴 Critical

민감 파일 패턴이 기본 제외 목록에서 빠져 있습니다.

Line 6-14의 기본 제외 목록만으로는 .env, 키 파일, credential 파일이 diff에 포함되어 외부 서비스로 전달될 수 있습니다.

수정 예시
 export const DEFAULT_EXCLUDE_GLOBS = [
+  '.env',
+  '.env.*',
+  '**/*credentials*',
+  '**/*.pem',
+  '**/*.key',
   'yarn.lock',
   'package-lock.json',
   'pnpm-lock.yaml',
   '**/*.snap',
   'dist/**',
   'coverage/**',
   '**/*.map',
 ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/scripts/pr-ai-description-lib.mjs around lines 6 - 14, The
DEFAULT_EXCLUDE_GLOBS array currently omits common sensitive files so secrets
like .env, key/cert files, and credential stores may be included in diffs;
update the DEFAULT_EXCLUDE_GLOBS constant to add patterns that exclude
environment and credential files (e.g., .env, .env.*, *.key, *.pem, *.p12,
*.jks, *.crt, *.der, *.secret, credentials files and common credential paths
like .aws/credentials and .git-credentials) so these sensitive patterns are
never exported by the PR AI description script.

556-558: ⚠️ Potential issue | 🟡 Minor

changes.description 개행 미정규화로 Markdown 표가 깨질 수 있습니다.

Line 557은 |만 escape하고 있어서 \r/\n이 들어오면 row가 분리됩니다. 표 렌더링 안정성을 위해 개행을 먼저 정규화해 주세요.

수정 예시
   for (const change of summary.changes) {
-    const escapedDesc = change.description.replaceAll('|', '\\|');
+    const escapedDesc = change.description
+      .replaceAll('\r\n', '\n')
+      .replaceAll('\r', '\n')
+      .replaceAll('\n', '<br>')
+      .replaceAll('|', '\\|');
     lines.push(`| \`${change.file}\` | ${escapedDesc} |`);
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/scripts/pr-ai-description-lib.mjs around lines 556 - 558, Normalize
newlines in change.description before escaping pipes: in the loop that builds
the table (the variable escapedDesc and the lines.push call), replace CR/LF/R
characters (e.g., replaceAll(/\r\n|\r|\n/g, ' ')) on change.description first,
then perform the replaceAll('|', '\\|') and any other escaping before pushing
the row; this ensures embedded newlines won't split the Markdown table rows.

401-416: ⚠️ Potential issue | 🔴 Critical

민감정보 마스킹 규칙이 여전히 너무 좁습니다.

Line 401-416의 현재 패턴은 OPENAI_API_KEY, AWS_SECRET_ACCESS_KEY, DATABASE_URL(credential 포함 URL) 같은 흔한 형태를 놓칠 수 있습니다. 외부 전송 전 마스킹 누락 위험이 큽니다.

보강 예시
 export function maskSensitiveContent(text) {
   if (typeof text !== 'string' || text.length === 0) {
     return '';
   }

   return text
     .replaceAll(/(Authorization\s*[:=]\s*)([^\n\r]+)/gi, '$1[REDACTED]')
     .replaceAll(/Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi, 'Bearer [REDACTED]')
+    .replaceAll(
+      /\b([A-Z0-9_]*(?:API_KEY|ACCESS_KEY|SECRET|TOKEN|PASSWORD|PRIVATE_KEY|CLIENT_SECRET))\b\s*[:=]\s*([^\s,\n\r]+)/g,
+      '$1=[REDACTED]',
+    )
+    .replaceAll(/(https?:\/\/)([^\/\s:@]+):([^@\/\s]+)@/gi, '$1[REDACTED]:[REDACTED]@')
     .replaceAll(
       /(\"?(?:password|secret|token|apiKey)\"?\s*[:=]\s*)\"([^\"\n\r]*)\"/gi,
       '$1"[REDACTED]"',
     )
     .replaceAll(
       /(\"?(?:password|secret|token|apiKey)\"?\s*[:=]\s*)([^\s,\n\r]+)/gi,
       '$1[REDACTED]',
     );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/scripts/pr-ai-description-lib.mjs around lines 401 - 416, The
current maskSensitiveContent function uses patterns that miss common env var
names and credential-containing URLs; update its replacement rules to also match
typical secret key names (e.g., OPENAI_API_KEY, AWS_SECRET_ACCESS_KEY, .*_KEY,
.*_SECRET, .*_TOKEN, api_key, api-key, client_secret), credential-bearing URLs
(e.g., scheme://user:password@host or DB connection strings containing
user:pass), and query/string parameters (e.g., ?password=, &api_key=); add or
extend replaceAll calls in maskSensitiveContent to include regexes that catch
these variants (env var keys, key=value in query strings, and user:pass in URLs)
and consistently replace the secret portion with [REDACTED] or "[REDACTED]"
while preserving surrounding labels so existing callers still get the masked
text.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/scripts/pr-ai-description-lib.mjs:
- Line 253: The reported issue is Prettier formatting mismatches causing ESLint
failures; run the project's Prettier formatter (e.g., prettier --write) over the
code that contains the ternary using typeof file.previous_filename === 'string'
? file.previous_filename : undefined and the other listed similar spots (around
the occurrences at the other noted line ranges) so the ternaries/expressions and
surrounding commas/line breaks match the repo's Prettier config; ensure the
exact expressions (e.g., file.previous_filename usage) are reformatted rather
than manually reformatting to avoid style drift and re-run lint to confirm the
ESLint errors are resolved.
- Line 78: The normalizePath function currently only replaces double-backslash
sequences, leaving single backslashes in Windows paths which breaks glob
matching; update the replacement to target any single backslash globally (e.g.,
use a global regex like /\\/g or replaceAll('\\','/') on filePath) so
filePath.replaceAll/replace converts all '\' to '/' before stripping a leading
'./', ensuring consistent normalized paths across platforms.

---

Duplicate comments:
In @.github/scripts/pr-ai-description-lib.mjs:
- Around line 6-14: The DEFAULT_EXCLUDE_GLOBS array currently omits common
sensitive files so secrets like .env, key/cert files, and credential stores may
be included in diffs; update the DEFAULT_EXCLUDE_GLOBS constant to add patterns
that exclude environment and credential files (e.g., .env, .env.*, *.key, *.pem,
*.p12, *.jks, *.crt, *.der, *.secret, credentials files and common credential
paths like .aws/credentials and .git-credentials) so these sensitive patterns
are never exported by the PR AI description script.
- Around line 556-558: Normalize newlines in change.description before escaping
pipes: in the loop that builds the table (the variable escapedDesc and the
lines.push call), replace CR/LF/R characters (e.g., replaceAll(/\r\n|\r|\n/g, '
')) on change.description first, then perform the replaceAll('|', '\\|') and any
other escaping before pushing the row; this ensures embedded newlines won't
split the Markdown table rows.
- Around line 401-416: The current maskSensitiveContent function uses patterns
that miss common env var names and credential-containing URLs; update its
replacement rules to also match typical secret key names (e.g., OPENAI_API_KEY,
AWS_SECRET_ACCESS_KEY, .*_KEY, .*_SECRET, .*_TOKEN, api_key, api-key,
client_secret), credential-bearing URLs (e.g., scheme://user:password@host or DB
connection strings containing user:pass), and query/string parameters (e.g.,
?password=, &api_key=); add or extend replaceAll calls in maskSensitiveContent
to include regexes that catch these variants (env var keys, key=value in query
strings, and user:pass in URLs) and consistently replace the secret portion with
[REDACTED] or "[REDACTED]" while preserving surrounding labels so existing
callers still get the masked text.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 45315a8b-b4aa-4eac-a6f8-f9897455a64a

📥 Commits

Reviewing files that changed from the base of the PR and between c8d5d73 and 0627432.

📒 Files selected for processing (1)
  • .github/scripts/pr-ai-description-lib.mjs

};

function normalizePath(filePath) {
return filePath.replaceAll('\\\\', '/').replace(/^\.\//, '');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

normalizePath의 역슬래시 치환이 불완전합니다.

Line 78은 \\(두 글자)만 치환해서 Windows 경로(\)가 그대로 남을 수 있습니다. 로컬 dry-run/Windows 환경에서 glob 매칭이 어긋날 수 있습니다.

수정 예시
 function normalizePath(filePath) {
-  return filePath.replaceAll('\\\\', '/').replace(/^\.\//, '');
+  return filePath.replaceAll('\\', '/').replace(/^\.\//, '');
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/scripts/pr-ai-description-lib.mjs at line 78, The normalizePath
function currently only replaces double-backslash sequences, leaving single
backslashes in Windows paths which breaks glob matching; update the replacement
to target any single backslash globally (e.g., use a global regex like /\\/g or
replaceAll('\\','/') on filePath) so filePath.replaceAll/replace converts all
'\' to '/' before stripping a leading './', ensuring consistent normalized paths
across platforms.

@chanwoo7 chanwoo7 merged commit a3566e9 into main Mar 15, 2026
2 checks passed
@chanwoo7 chanwoo7 deleted the ci/pr-ai-description-v3 branch March 15, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🌏 Deploy 배포 관련 ⚙️ Setting 개발환경 세팅 ✅ Test 테스트 관련

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant