Conversation
📝 WalkthroughWalkthroughThis PR introduces an automated AI-powered GitHub PR description feature. It adds OpenAI integration via GitHub Actions to generate PR summaries, labels, and titles from code diffs. The implementation includes configuration files, utility libraries for diff processing and validation, test suites, a dry-run script, and a GitHub Actions workflow that orchestrates the entire pipeline. Changes
Sequence Diagram(s)sequenceDiagram
participant GHA as GitHub Actions
participant GH as GitHub API
participant OAI as OpenAI API
participant PR as Pull Request
GHA->>GH: Fetch PR metadata & repository labels
GHA->>GH: Retrieve diff (via compare endpoint or git)
GHA->>GHA: Filter & normalize diff files
GHA->>GHA: Mask sensitive content in diff
GHA->>OAI: Send prompt with diff & repository labels
OAI-->>GHA: Return JSON summary (title, labels, changes)
GHA->>GHA: Validate response against schema
GHA->>GHA: Render summary block & determine label/title changes
GHA->>GH: Update PR body (insert/replace summary block)
GHA->>GH: Apply labels (filtered against known labels)
GHA->>GH: Update PR title (if applicable)
GHA->>GHA: Write step summary with results
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 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: In maskSensitiveContent expand the redaction regexes to
cover common env-var and multiline secret shapes: add case-insensitive patterns
for VAR-style keys (e.g.,
/^[A-Z0-9_+-]*?(?:API[_-]?KEY|OPENAI[_-]?API[_-]?KEY|CLIENT[_-]?SECRET|REFRESH[_-]?TOKEN|PRIVATE[_-]?KEY|SECRET|TOKEN|PASSWORD|APIKEY)\s*[:=]\s*.+$/im)
and key/value forms with underscores/hyphens and mixed case, and add a multiline
PEM block matcher that redacts anything between "-----BEGIN [A-Z ]+-----" and
"-----END [A-Z ]+-----" (with s/m flags). Apply these new replace calls in
maskSensitiveContent (keep existing Authorization/Bearer handling) and add
regression tests that assert redaction for env-var lines like
"OPENAI_API_KEY=...", "client_secret=...", "refresh_token=...", "PRIVATE_KEY"
PEM blocks, and multiline secrets to prevent regressions.
- Around line 536-585: The renderSummaryBlock function currently writes
model-provided fields directly into the PR block; sanitize all AI-sourced
strings (e.g., summary.summary, summary.summaryBullets items, each
summary.changes[].description and .file, summary.impact items, summary.checklist
items, and arrays passed to renderOptionalSection like
summary.breakingChanges/relatedIssues/dependencies) by stripping any occurrences
of MARKER_START and MARKER_END, collapsing newlines into single spaces, trimming
whitespace, and normalizing/escaping pipe characters (e.g., replace '\n' with '
', remove marker substrings, and then escape '|' as '\|') before rendering so
upsertSummaryBlock cannot be prematurely terminated or inject multiline table
cells.
In @.github/scripts/pr-ai-description.mjs:
- Around line 216-237: The current loadCurrentPullRequest silently falls back to
the webhook snapshot on fetch failure which lets stale title/body be used for
PATCHes; change loadCurrentPullRequest to return null (or throw a specific
error) when fetchPullRequest fails for write-related flows so callers can detect
failure and skip applying title/body updates; update the caller applyPrUpdates
(and any other call sites such as the similar block around lines 992-1005) to
check for a null result from loadCurrentPullRequest before calling
buildPrUpdatePayload and skip the title/body patch when null, preserving other
safe updates and logging a clear warning.
- Around line 428-449: The prompt currently instructs the model to emit
"changes" for each changed file even when buildLimitedDiff() (check
diff-truncation-meta) may have omitted files, which causes hallucinated per-file
entries; update the construction that uses prMeta/repositoryLabels/diffText so
the prompt: 1) checks diff-truncation-meta (or a flag in prMeta) and, if omitted
files are indicated, explicitly tells the model to only describe files present
in diffText and not to invent any entries for missing files, 2) instructs the
model to add a top-level note (e.g., "partial_diff": true) or an explicit
sentence in the output when the diff is truncated, and 3) preserves original
identifiers like prMeta and diffText in the prompt while emphasizing "do NOT
fabricate descriptions for files not shown." Ensure the change targets the
string-building code that concatenates prMeta/repositoryLabels/diffText (the
array being .join('\n')) so the prompt text is updated accordingly.
In @.github/workflows/pr-ai-description.yml:
- Around line 39-51: The "Generate AI PR description" step (run: node
.github/scripts/pr-ai-description.mjs) will fail for Dependabot because
Dependabot PRs have a read-only GITHUB_TOKEN and no Actions secrets; update the
workflow to skip or use a read-only fallback when github.actor ==
'dependabot[bot]': add an if condition to the job/step (e.g., if: github.actor
!= 'dependabot[bot]') or modify the step to detect missing OPENAI_API_KEY/GITHUB
secrets and short-circuit (or run a non-writing readonly mode) so the script in
.github/scripts/pr-ai-description.mjs does not attempt to access OPENAI_API_KEY
or write labels/title/body when invoked by dependabot.
- Around line 23-51: The workflow currently checks out and runs PR branch code
(the "Generate AI PR description" step invoking
.github/scripts/pr-ai-description.mjs) while passing secrets (OPENAI_API_KEY,
LANGSMITH_API_KEY, GITHUB_TOKEN) — split this into two workflows: keep a safe
unprivileged pull_request workflow that does not run checked-out PR scripts or
expose secrets (remove the checkout/run of pr-ai-description.mjs, limit
permissions, run only tests/lint), and create a separate privileged workflow
triggered only on pull_request_target or workflow_run that checks out the
trusted base branch (use github.event.pull_request.base.sha when checking out)
and runs .github/scripts/pr-ai-description.mjs with the required secrets and
write-scoped GITHUB_TOKEN; ensure the privileged workflow explicitly sets
minimal permissions and avoids checking out untrusted head code.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2b3a2ef7-1e33-4ff2-8207-3352fc168185
⛔ Files ignored due to path filters (1)
yarn.lockis 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.ymlpackage.json
| 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]', | ||
| ); |
There was a problem hiding this comment.
The redaction pass misses common secret shapes.
These regexes only catch bare password|secret|token|apiKey keys plus Authorization/Bearer patterns. Values like OPENAI_API_KEY=..., client_secret=..., refresh_token=..., PRIVATE_KEY=..., or PEM blocks survive unchanged and are then sent in the masked diff payload. Please broaden the matcher before any network call and add regression cases for env-var and multiline-key formats.
🤖 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, In
maskSensitiveContent expand the redaction regexes to cover common env-var and
multiline secret shapes: add case-insensitive patterns for VAR-style keys (e.g.,
/^[A-Z0-9_+-]*?(?:API[_-]?KEY|OPENAI[_-]?API[_-]?KEY|CLIENT[_-]?SECRET|REFRESH[_-]?TOKEN|PRIVATE[_-]?KEY|SECRET|TOKEN|PASSWORD|APIKEY)\s*[:=]\s*.+$/im)
and key/value forms with underscores/hyphens and mixed case, and add a multiline
PEM block matcher that redacts anything between "-----BEGIN [A-Z ]+-----" and
"-----END [A-Z ]+-----" (with s/m flags). Apply these new replace calls in
maskSensitiveContent (keep existing Authorization/Bearer handling) and add
regression tests that assert redaction for env-var lines like
"OPENAI_API_KEY=...", "client_secret=...", "refresh_token=...", "PRIVATE_KEY"
PEM blocks, and multiline secrets to prevent regressions.
| export function renderSummaryBlock(summary) { | ||
| const lines = [MARKER_START]; | ||
|
|
||
| // Summary | ||
| lines.push('### PR Summary'); | ||
| lines.push(summary.summary); | ||
| if (summary.summaryBullets.length > 0) { | ||
| lines.push(''); | ||
| for (const bullet of summary.summaryBullets) { | ||
| lines.push(`- ${bullet}`); | ||
| } | ||
| } | ||
|
|
||
| // Changes (테이블) | ||
| lines.push(''); | ||
| lines.push('### Changes'); | ||
| lines.push(`> ${summary.changes.length} files changed`); | ||
| lines.push(''); | ||
| lines.push('| File | Changes |'); | ||
| lines.push('|------|---------|'); | ||
| for (const change of summary.changes) { | ||
| const escapedDesc = change.description.replaceAll('|', '\\|'); | ||
| lines.push(`| \`${change.file}\` | ${escapedDesc} |`); | ||
| } | ||
|
|
||
| // Impact | ||
| if (summary.impact.length > 0) { | ||
| lines.push(''); | ||
| lines.push('### Impact'); | ||
| for (const item of summary.impact) { | ||
| lines.push(`- ${item}`); | ||
| } | ||
| } | ||
|
|
||
| // Checklist (체크박스) | ||
| if (summary.checklist.length > 0) { | ||
| lines.push(''); | ||
| lines.push('### Checklist'); | ||
| for (const item of summary.checklist) { | ||
| lines.push(`- [ ] ${item}`); | ||
| } | ||
| } | ||
|
|
||
| // 선택적 섹션들 | ||
| lines.push(...renderOptionalSection('Breaking Changes', summary.breakingChanges)); | ||
| lines.push(...renderOptionalSection('Related Issues', summary.relatedIssues)); | ||
| lines.push(...renderOptionalSection('Dependencies', summary.dependencies)); | ||
|
|
||
| lines.push(MARKER_END); | ||
| return lines.join('\n'); |
There was a problem hiding this comment.
Sanitize model text before embedding your own markers.
renderSummaryBlock() writes model-provided strings directly into the block. If the model echoes <!-- pr-ai-summary:end --> or multiline table content, the next upsertSummaryBlock() can terminate early and leave stale tail content in the PR body. Strip MARKER_START/MARKER_END from all AI fields and normalize table cells to single-line text before rendering.
💡 Suggested guard
+function sanitizeSummaryText(value) {
+ return String(value)
+ .replaceAll(MARKER_START, '[pr-ai-summary:start]')
+ .replaceAll(MARKER_END, '[pr-ai-summary:end]');
+}
...
- lines.push(summary.summary);
+ lines.push(sanitizeSummaryText(summary.summary));
...
- const escapedDesc = change.description.replaceAll('|', '\\|');
- lines.push(`| \`${change.file}\` | ${escapedDesc} |`);
+ const escapedFile = sanitizeSummaryText(change.file)
+ .replaceAll(/\r?\n/g, ' ')
+ .replaceAll('`', '\\`')
+ .replaceAll('|', '\\|');
+ const escapedDesc = sanitizeSummaryText(change.description)
+ .replaceAll(/\r?\n/g, ' ')
+ .replaceAll('|', '\\|');
+ lines.push(`| \`${escapedFile}\` | ${escapedDesc} |`);🧰 Tools
🪛 ESLint
[error] 580-580: Replace ...renderOptionalSection('Breaking·Changes',·summary.breakingChanges) with ⏎····...renderOptionalSection('Breaking·Changes',·summary.breakingChanges),⏎··
(prettier/prettier)
🤖 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 536 - 585, The
renderSummaryBlock function currently writes model-provided fields directly into
the PR block; sanitize all AI-sourced strings (e.g., summary.summary,
summary.summaryBullets items, each summary.changes[].description and .file,
summary.impact items, summary.checklist items, and arrays passed to
renderOptionalSection like summary.breakingChanges/relatedIssues/dependencies)
by stripping any occurrences of MARKER_START and MARKER_END, collapsing newlines
into single spaces, trimming whitespace, and normalizing/escaping pipe
characters (e.g., replace '\n' with ' ', remove marker substrings, and then
escape '|' as '\|') before rendering so upsertSummaryBlock cannot be prematurely
terminated or inject multiline table cells.
| export async function loadCurrentPullRequest({ | ||
| githubRequest, | ||
| owner, | ||
| repo, | ||
| number, | ||
| fallbackPullRequest, | ||
| onWarn = logWarn, | ||
| }) { | ||
| try { | ||
| const latestPullRequest = await fetchPullRequest(githubRequest, owner, repo, number); | ||
|
|
||
| if (latestPullRequest && typeof latestPullRequest === 'object') { | ||
| return latestPullRequest; | ||
| } | ||
| } catch (error) { | ||
| onWarn('failed to fetch latest pull request. fallback to event payload.', { | ||
| status: error?.status, | ||
| message: error?.message ?? 'unknown-error', | ||
| }); | ||
| } | ||
|
|
||
| return fallbackPullRequest; |
There was a problem hiding this comment.
Stale event payload should not become the source of truth for a PATCH.
When the reload fails, loadCurrentPullRequest() returns the webhook snapshot. applyPrUpdates() then feeds that stale body/title into buildPrUpdatePayload() and can overwrite edits that were made after the workflow started. For write paths, return null/an error here and skip the title/body update instead of patching from the fallback snapshot.
💡 Safer write path
export async function loadCurrentPullRequest({
githubRequest,
owner,
repo,
number,
fallbackPullRequest,
onWarn = logWarn,
}) {
try {
const latestPullRequest = await fetchPullRequest(githubRequest, owner, repo, number);
if (latestPullRequest && typeof latestPullRequest === 'object') {
return latestPullRequest;
}
} catch (error) {
onWarn('failed to fetch latest pull request. fallback to event payload.', {
status: error?.status,
message: error?.message ?? 'unknown-error',
});
}
- return fallbackPullRequest;
+ return null;
} const latestPullRequest = await loadCurrentPullRequest({
githubRequest,
owner,
repo,
number: prNumber,
fallbackPullRequest: pullRequest,
});
+ if (!latestPullRequest) {
+ await writeStepSummary(
+ '- PR body/title update skipped because the latest PR body could not be reloaded.',
+ );
+ return {
+ assigneesAdded,
+ labelsAdded,
+ unknownLabelsIgnoredCount,
+ titleUpdated: false,
+ bodyUpdated: false,
+ };
+ }
const { updatedBody, nextTitle, bodyUpdated } = buildPrUpdatePayload({
currentPullRequest: latestPullRequest,
fallbackPullRequest: pullRequest,
block,
applyTitle,Also applies to: 992-1005
🧰 Tools
🪛 ESLint
[error] 225-225: Replace githubRequest,·owner,·repo,·number with ⏎······githubRequest,⏎······owner,⏎······repo,⏎······number,⏎····
(prettier/prettier)
🤖 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 216 - 237, The current
loadCurrentPullRequest silently falls back to the webhook snapshot on fetch
failure which lets stale title/body be used for PATCHes; change
loadCurrentPullRequest to return null (or throw a specific error) when
fetchPullRequest fails for write-related flows so callers can detect failure and
skip applying title/body updates; update the caller applyPrUpdates (and any
other call sites such as the similar block around lines 992-1005) to check for a
null result from loadCurrentPullRequest before calling buildPrUpdatePayload and
skip the title/body patch when null, preserving other safe updates and logging a
clear warning.
| return [ | ||
| '다음 Pull Request 정보를 기반으로 한국어 PR 요약 JSON을 생성하세요.', | ||
| '', | ||
| '규칙:', | ||
| '- title은 conventional commits 형식의 prefix를 포함하세요 (feat:, fix:, refactor:, docs:, style:, perf:, test:, build:, ci:, chore:, revert: 중 택1). 예: "feat: 사용자 인증 기능 추가"', | ||
| '- summary는 PR 전체를 요약하는 한국어 텍스트를 작성하세요.', | ||
| '- summaryBullets는 핵심 변경사항을 간결한 한국어 리스트 항목으로 작성하세요. 불필요하면 빈 배열로 두세요.', | ||
| '- changes는 변경된 각 파일별로 { file, description } 형태로 작성하세요. file은 파일 경로 원문, description은 해당 파일의 변경 내용을 한국어 문장으로 작성하세요.', | ||
| '- impact는 이 PR이 시스템에 미치는 영향을 자유 형식 한국어 텍스트 리스트로 작성하세요. 영향이 없으면 빈 배열로 두세요.', | ||
| '- checklist는 리뷰어가 꼭 확인해야 할 항목들을 한국어로 작성하세요. 애매하거나 추상적인 항목은 피하고, 객관적이고 명확한 행동 지침을 간결하게 작성하세요.', | ||
| '- breakingChanges는 하위 호환성을 깨는 변경사항을 작성하세요. 없으면 빈 배열로 두세요.', | ||
| '- relatedIssues는 관련 이슈/PR을 "#번호 설명" 형태로 작성하세요. 없으면 빈 배열로 두세요.', | ||
| '- dependencies는 추가/제거/업데이트된 패키지 의존성을 작성하세요. 없으면 빈 배열로 두세요.', | ||
| '- labels는 아래 제공된 레포 라벨 목록에서만 선택하세요. 최대 3개까지 선택할 수 있으나, 꼭 필요한 경우가 아니면 1-2개로 제한하는 것을 권장합니다.', | ||
| '- 코드 식별자/파일 경로/에러 메시지는 원문을 유지하세요.', | ||
| '', | ||
| `PR Meta:\n${JSON.stringify(prMeta, null, 2)}`, | ||
| '', | ||
| `Repository Labels:\n${JSON.stringify(repositoryLabels, null, 2)}`, | ||
| '', | ||
| `Diff:\n${diffText}`, | ||
| ].join('\n'); |
There was a problem hiding this comment.
Don't ask for per-file descriptions when the diff may be truncated.
buildLimitedDiff() can omit files, but this prompt still tells the model to emit changes for "each changed file". Once diff-truncation-meta says some files were omitted, the model has no source for those descriptions and will hallucinate entries that then get written back into the PR body.
💡 Suggested prompt tweak
- '- changes는 변경된 각 파일별로 { file, description } 형태로 작성하세요. file은 파일 경로 원문, description은 해당 파일의 변경 내용을 한국어 문장으로 작성하세요.',
+ '- changes는 Diff 섹션에 실제로 포함된 파일만 대상으로 { file, description } 형태로 작성하세요.',
+ '- diff-truncation-meta가 있으면 omittedFiles에 해당하는 파일 내용은 추측하지 말고, 필요하면 summary/impact에서 일부 파일이 생략되었다고만 명시하세요.',🤖 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 428 - 449, The prompt
currently instructs the model to emit "changes" for each changed file even when
buildLimitedDiff() (check diff-truncation-meta) may have omitted files, which
causes hallucinated per-file entries; update the construction that uses
prMeta/repositoryLabels/diffText so the prompt: 1) checks diff-truncation-meta
(or a flag in prMeta) and, if omitted files are indicated, explicitly tells the
model to only describe files present in diffText and not to invent any entries
for missing files, 2) instructs the model to add a top-level note (e.g.,
"partial_diff": true) or an explicit sentence in the output when the diff is
truncated, and 3) preserves original identifiers like prMeta and diffText in the
prompt while emphasizing "do NOT fabricate descriptions for files not shown."
Ensure the change targets the string-building code that concatenates
prMeta/repositoryLabels/diffText (the array being .join('\n')) so the prompt
text is updated accordingly.
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 1 | ||
|
|
||
| - name: Setup Node.js (24.x) | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '24.x' | ||
|
|
||
| - name: Install dependencies | ||
| run: yarn install --immutable | ||
|
|
||
| - name: Run PR AI helper unit tests | ||
| run: node --test .github/scripts/__tests__/*.spec.mjs | ||
|
|
||
| - name: Generate AI PR description | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
| OPENAI_MODEL: ${{ vars.OPENAI_MODEL }} | ||
| PR_AI_MAX_DIFF_BYTES: ${{ vars.PR_AI_MAX_DIFF_BYTES }} | ||
| PR_AI_MAX_FILES: ${{ vars.PR_AI_MAX_FILES }} | ||
| LANGSMITH_TRACING: ${{ vars.LANGSMITH_TRACING }} | ||
| LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }} | ||
| LANGSMITH_ENDPOINT: ${{ vars.LANGSMITH_ENDPOINT }} | ||
| LANGSMITH_PROJECT: ${{ vars.LANGSMITH_PROJECT }} | ||
| LANGSMITH_WORKSPACE_ID: ${{ vars.LANGSMITH_WORKSPACE_ID }} | ||
| run: node .github/scripts/pr-ai-description.mjs |
There was a problem hiding this comment.
Don't execute PR-branch code in the secret-bearing job.
This job checks out the PR branch, runs repository code, and then invokes .github/scripts/pr-ai-description.mjs with OPENAI_API_KEY, LANGSMITH_API_KEY, and write scopes on the job token. A same-repo PR can change the checked-out scripts/tests or mutate the workspace before the final step, which gives that PR a path to exfiltrate the referenced secrets or abuse the token. GitHub's runner security docs explicitly call out pull_request from a branch within the repository as a case where attackers can steal repository secrets or use the job's GITHUB_TOKEN. Split this into an unprivileged pull_request test workflow and a separate privileged workflow that only runs trusted base-branch code. (docs.github.com)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/pr-ai-description.yml around lines 23 - 51, The workflow
currently checks out and runs PR branch code (the "Generate AI PR description"
step invoking .github/scripts/pr-ai-description.mjs) while passing secrets
(OPENAI_API_KEY, LANGSMITH_API_KEY, GITHUB_TOKEN) — split this into two
workflows: keep a safe unprivileged pull_request workflow that does not run
checked-out PR scripts or expose secrets (remove the checkout/run of
pr-ai-description.mjs, limit permissions, run only tests/lint), and create a
separate privileged workflow triggered only on pull_request_target or
workflow_run that checks out the trusted base branch (use
github.event.pull_request.base.sha when checking out) and runs
.github/scripts/pr-ai-description.mjs with the required secrets and write-scoped
GITHUB_TOKEN; ensure the privileged workflow explicitly sets minimal permissions
and avoids checking out untrusted head code.
| - name: Generate AI PR description | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
| OPENAI_MODEL: ${{ vars.OPENAI_MODEL }} | ||
| PR_AI_MAX_DIFF_BYTES: ${{ vars.PR_AI_MAX_DIFF_BYTES }} | ||
| PR_AI_MAX_FILES: ${{ vars.PR_AI_MAX_FILES }} | ||
| LANGSMITH_TRACING: ${{ vars.LANGSMITH_TRACING }} | ||
| LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }} | ||
| LANGSMITH_ENDPOINT: ${{ vars.LANGSMITH_ENDPOINT }} | ||
| LANGSMITH_PROJECT: ${{ vars.LANGSMITH_PROJECT }} | ||
| LANGSMITH_WORKSPACE_ID: ${{ vars.LANGSMITH_WORKSPACE_ID }} | ||
| run: node .github/scripts/pr-ai-description.mjs |
There was a problem hiding this comment.
Dependabot PRs will fail here.
pull_request runs triggered by Dependabot get a read-only GITHUB_TOKEN and no Actions secrets. This step requires OPENAI_API_KEY, and the script later tries to write labels/title/body back to the PR, so Dependabot updates will fail instead of being skipped. Add an explicit github.actor != 'dependabot[bot]' guard or a read-only fallback path. (docs.github.com)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/pr-ai-description.yml around lines 39 - 51, The "Generate
AI PR description" step (run: node .github/scripts/pr-ai-description.mjs) will
fail for Dependabot because Dependabot PRs have a read-only GITHUB_TOKEN and no
Actions secrets; update the workflow to skip or use a read-only fallback when
github.actor == 'dependabot[bot]': add an if condition to the job/step (e.g.,
if: github.actor != 'dependabot[bot]') or modify the step to detect missing
OPENAI_API_KEY/GITHUB secrets and short-circuit (or run a non-writing readonly
mode) so the script in .github/scripts/pr-ai-description.mjs does not attempt to
access OPENAI_API_KEY or write labels/title/body when invoked by dependabot.
PR Summary
GitHub Actions 기반 PR 설명 자동생성 도구를 추가합니다. OpenAI(langsmith 래퍼 포함)를 사용해 변경된 코드 diff를 요약하고 검증된 JSON 스키마로 PR 본문에 삽입하거나 PR 제목/라벨/담당자를 업데이트하는 스크립트와 유닛 테스트, dry-run 도구 및 CodeRabbit 설정을 포함합니다. 민감 정보 마스킹, diff 절단, 라벨 필터링 등 안전장치가 구현되어 있으며 CI 워크플로우와 package.json 의존성 업데이트가 포함됩니다.
Changes
.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.ymlpackage.jsonImpact
Checklist
Bearer ${githubToken}' 또는 적절한 형식으로 구현되어 있는지 확인Dependencies