Dependabot PR 트리아지 자동화: 그룹 설정, 분류 스크립트, Copilot 앱 자동화 요청 작성
patch·minor·major와 CI 상태로 나누는 규칙을 먼저 코드로 만들고, Copilot 자동화에 맡기는 순서

핵심 요약
- dependabot.yml의 groups로 patch·minor를 묶고 major는 개별 PR로 남겨 PR 수부터 줄인다.
- "patch·minor + CI 통과면 병합 후보" 같은 규칙을 먼저 코드로 만들어 자동화 결과의 기준으로 쓴다.
- 트리아지는 CI가 끝난 뒤에 실행해야 한다. PR 생성 직후에는 모든 PR이 대기 상태다.
- 보안 민감 패키지와 NEUTRAL 결론 처리는 기계적 규칙과 별도로 정한다.
Dependabot을 켜 두면 의존성 업데이트 PR이 매주 쌓이고, 리뷰어는 비슷한 PR을 하나씩 열어 CI 결과를 확인한다. GitHub은 Copilot 앱의 자동화로 이 트리아지를 맡기는 방법을 공식 블로그에 소개했다. 다만 자동화에 맡기기 전에 "무엇을 안전하다고 볼지" 규칙이 팀 안에서 정해져 있어야 결과를 믿을 수 있다. 이 글은 PR 수를 줄이는 설정, 규칙을 코드로 만든 분류 스크립트, 자동화 요청 작성을 순서대로 정리한다.
1단계: groups 설정으로 PR 수 자체를 줄이기
dependabot.yml의 groups는 여러 의존성 업데이트를 PR 하나로 묶는다. update-types에는 major, minor, patch를, dependency-type에는 production이나 development를 쓸 수 있다. 위험이 낮은 patch·minor는 묶고, major는 개별 PR로 남기는 구성이 일반적이다.
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
groups:
production-minor-patch:
dependency-type: "production"
patterns: ["*"]
update-types: ["minor", "patch"]
dev-dependencies:
dependency-type: "development"
patterns: ["*"]
update-types: ["minor", "patch"]
문서에 따르면 버전 업데이트 PR이 5개 열려 있으면 그중 일부가 병합되거나 닫힐 때까지 새 PR을 만들지 않는 것이 기본 동작이다. 업데이트가 멈춘 것처럼 보이면 open-pull-requests-limit부터 확인한다.
2단계: 분류 규칙을 스크립트로 만들기
"patch·minor이고 CI가 통과하면 병합 후보, 나머지는 사람이 본다"는 규칙을 코드로 먼저 만든다. 규칙이 코드로 있으면 Copilot 자동화의 결과와 비교할 기준이 생긴다. 아래 스크립트는 gh pr list로 열린 Dependabot PR을 읽어 제목의 버전 변화와 CI 상태로 정렬한다. 읽기 전용이라 PR을 바꾸지 않는다.
// 사용법: node triage.mjs owner/repo
// 열린 Dependabot PR을 버전 변화(major/minor/patch)와 CI 상태로 분류한다(읽기 전용).
import { execFileSync } from 'node:child_process';
const repo = process.argv[2];
const prs = JSON.parse(execFileSync('gh', [
'pr', 'list', '-R', repo, '--author', 'app/dependabot', '--state', 'open', '--limit', '100',
'--json', 'number,title,statusCheckRollup',
], { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 }));
// "Bump X from 1.2.3 to 1.3.0" 형식의 제목에서 버전을 읽는다. 그룹 PR은 제목에 버전이 없다.
function bumpType(title) {
const m = title.match(/from v?(\d+)\.(\d+)\.(\d+)\S* to v?(\d+)\.(\d+)\.(\d+)/);
if (!m) return 'unknown';
const [a, b] = [m.slice(1, 4).map(Number), m.slice(4, 7).map(Number)];
if (b[0] !== a[0]) return 'major';
if (b[1] !== a[1]) return 'minor';
return 'patch';
}
function ciState(checks = []) {
if (!checks.length) return 'none';
const states = checks.map((c) => c.conclusion || c.state || c.status);
if (states.some((s) => /FAILURE|ERROR|CANCELLED|TIMED_OUT/.test(s))) return 'failing';
if (states.some((s) => /PENDING|IN_PROGRESS|QUEUED|EXPECTED/.test(s))) return 'pending';
return 'passing';
}
const RISK = { patch: 1, minor: 2, unknown: 3, major: 4 };
const rows = prs
.map((p) => ({ '#': p.number, bump: bumpType(p.title), ci: ciState(p.statusCheckRollup), title: p.title.slice(0, 60) }))
.sort((x, y) => RISK[x.bump] - RISK[y.bump] || x['#'] - y['#']);
console.table(rows);
const safe = rows.filter((r) => (r.bump === 'patch' || r.bump === 'minor') && r.ci === 'passing');
console.log(`열린 PR ${rows.length}건 · 병합 후보(patch/minor + CI 통과) ${safe.length}건 · 사람 검토 필요 ${rows.length - safe.length}건`);
공개 저장소에 실행한 결과: CI가 끝나기 전과 후
2026-10-07에 영국 통계청(ONS)의 공개 저장소 ONSdigital/survey-assist-sayt-ui에 실행했다. 첫 실행은 Dependabot이 PR 5건을 연 직후였다.
$ node triage.mjs ONSdigital/survey-assist-sayt-ui # PR 생성 직후
│ 39 │ 'patch' │ 'pending' │ 'chore(deps): bump werkzeug from 3.1.8 to 3.1.9' │
│ 35 │ 'minor' │ 'pending' │ 'chore(deps): bump dompurify from 3.2.6 to 3.4.16' │
│ 36 │ 'minor' │ 'pending' │ 'chore(deps): bump urllib3 from 2.7.0 to 2.8.0' │
│ 37 │ 'minor' │ 'pending' │ 'chore(deps-dev): bump virtualenv from 21.4.3 to 21.7.13' │
│ 38 │ 'major' │ 'pending' │ 'chore(deps): bump cryptography from 48.0.1 to 50.0.0' │
열린 PR 5건 · 병합 후보(patch/minor + CI 통과) 0건 · 사람 검토 필요 5건
$ node triage.mjs ONSdigital/survey-assist-sayt-ui # 몇 분 뒤 다시 실행 (2026-10-07T14:12Z)
│ 39 │ 'patch' │ 'passing' │ 'chore(deps): bump werkzeug from 3.1.8 to 3.1.9' │
│ 35 │ 'minor' │ 'passing' │ 'chore(deps): bump dompurify from 3.2.6 to 3.4.16' │
│ 36 │ 'minor' │ 'pending' │ 'chore(deps): bump urllib3 from 2.7.0 to 2.8.0' │
│ 37 │ 'minor' │ 'passing' │ 'chore(deps-dev): bump virtualenv from 21.4.3 to 21.7.13' │
│ 38 │ 'major' │ 'passing' │ 'chore(deps): bump cryptography from 48.0.1 to 50.0.0' │
열린 PR 5건 · 병합 후보(patch/minor + CI 통과) 3건 · 사람 검토 필요 2건
이 결과에서 세 가지를 확인할 수 있다. 첫째, 트리아지는 CI가 끝난 뒤에 돌려야 한다. PR 생성 직후에는 모든 PR이 대기 상태라 분류 결과가 쓸모없다. 둘째, 열린 PR이 정확히 5건이었는데, 이는 위에서 본 기본 PR 수 제한과 일치한다. 셋째, 규칙이 기계적이라 놓치는 판단이 있다. dompurify는 3.2.6에서 3.4.16으로 minor 버전이 둘 올랐고 HTML을 정화하는 보안 라이브러리라, 규칙상 병합 후보여도 사람이 변경 내역을 보는 편이 맞다. 이 저장소의 CodeQL 검사는 NEUTRAL 결론을 내는데, 스크립트는 실패가 아니므로 통과로 셌다. 팀마다 NEUTRAL·SKIPPED를 어떻게 볼지 정해 두어야 한다.
3단계: Copilot 앱 자동화에 규칙을 넣어 맡기기
공식 블로그에 따르면 Copilot 앱에서 자동화를 만들고 트리거(Manual, Hourly, Daily, Weekly, 이슈 생성 시)를 고른 뒤 작업을 자연어로 설명하고 저장소를 선택한다. 실행 결과는 시간, 동작, 결과와 함께 저장되고 이어서 Copilot 세션으로 작업할 수 있다. 블로그의 예시 요청은 열린 Dependabot PR을 위험도로 묶고, 안전한 patch·minor를 찾고, CI 통과를 확인한 뒤 다음 단계를 요약하라는 내용이다. 2단계에서 정한 규칙을 요청에 그대로 넣으면 결과가 팀 기준과 맞는다.
열린 Dependabot PR을 검토해 다음 기준으로 분류하고 요약해 줘.
- 병합 후보: patch 또는 minor이고, 모든 필수 CI가 성공(NEUTRAL·SKIPPED는 별도 표시)
- 사람 검토: major, 버전 형식을 읽을 수 없는 그룹 PR, CI 실패·진행 중
- 보안 민감 패키지(인증, 암호화, HTML 정화: cryptography, dompurify, jsonwebtoken 등)는
버전과 관계없이 사람 검토로 분류
- PR을 병합하거나 닫지 말 것. 결과는 표와 다음 단계 3줄로 요약.
- Copilot 앱에서 새 자동화를 만들고 "Daily Dependabot Triage"처럼 용도가 드러나는 이름을 붙인다.
- 트리거를 고른다. 이 작업은 CI 완료 이후가 되도록 Daily를 쓴다.
- 위 요청 문장을 작업 설명에 넣고 대상 저장소를 선택한다.
- 한 번 수동으로 실행해, 결과를 2단계 스크립트 출력과 비교한다. 분류가 다르면 요청 문장의 기준을 고친다.
- 이후 실행 기록에서 결과를 확인하고, 사람 검토로 분류된 PR만 이어서 Copilot 세션이나 직접 리뷰로 처리한다.
트리거는 CI가 끝난 뒤가 되도록 Daily로 두고, Dependabot의 schedule보다 몇 시간 뒤에 실행되게 맞춘다. 자동화가 병합까지 하게 만들지는 않는다. 분류 결과를 보드로 보고 싶다면 Copilot 캔버스 확장으로 트리아지 보드를 만들 수 있고, 리뷰 봇과의 역할 분담은 GitHub Actions AI 리뷰 봇 연동을 참고한다.
초보자가 자주 실수하는 포인트
- Dependabot 실행 직후에 트리아지를 돌려 모든 PR을 "대기"로 분류한다.
- 기본 PR 수 제한(5건)을 모르고 업데이트가 멈췄다고 생각한다.
- minor 업데이트라는 이유로 보안 라이브러리 변경을 검토 없이 병합한다.
- 자동화 요청에 병합 금지를 적지 않는다.
체크리스트
- dependabot.yml에 groups와 schedule을 설정했다.
- 병합 후보의 기준을 팀 문서와 스크립트로 정했다.
- 보안 민감 패키지 목록을 정했다.
- 자동화 트리거가 CI 완료 이후로 맞춰져 있다.
- 자동화 요청에 병합·닫기 금지를 적었다.
자주 묻는 질문
Dependabot auto-merge를 켜면 트리아지가 필요 없지 않나요?
자동 병합은 규칙에 맞는 PR을 처리할 뿐 판단이 필요한 PR을 골라 주지는 않습니다. 보안 민감 패키지나 major 업데이트처럼 사람이 볼 PR을 추리는 단계는 여전히 필요합니다.
그룹 PR은 어떻게 분류하나요?
그룹 PR은 제목에 단일 버전이 없어 이 스크립트에서 unknown으로 분류됩니다. 그룹을 update-types로 patch·minor만 묶어 두면, 그룹 PR 자체가 낮은 위험이라는 전제를 둘 수 있습니다.
참고 자료 · 검증 기준
- GitHub Docs — Dependabot options reference (groups, schedule)
- GitHub Blog — GitHub Copilot app for beginners: automate Dependabot pull request triage
- GitHub CLI Manual — gh pr list
위 자료와 내용을 대조한 날짜: . 도구·서비스 정책은 이후 바뀔 수 있으므로 적용 전 공식 문서를 다시 확인하세요.
이 글은 실제 실무 환경 테스트 및 검증된 기술 자료를 바탕으로 작성되었으며, 환경 변화에 따라 지속적으로 갱신됩니다.