커버리지가 떨어지는 PR은 룰셋으로 막습니다. GitHub은 2026년 9월 18일 체인지로그에서 Restrict code coverage 저장소 룰셋을 REST API로 만들고 고치고 읽는 길을 열었다고 알렸습니다(Manage the code coverage ruleset condition with the REST API). 그전까지 웹 화면에서만 켜지던 항목인데요, 같은 공지는 이 규칙을 쓰려면 저장소에 GitHub Code Quality가 켜져 있고 커버리지 업로드가 설정돼 있어야 한다고 적습니다.

「Manage the code coverage ruleset condition with the REST API (GitHub Changelog)」 문서 화면

출처: 「Manage the code coverage ruleset condition with the REST API (GitHub Changelog)」, github.blog. 2026-09-19 캡처.

플랜도 가립니다.

공지에 적힌 제공 범위는 GitHub Enterprise Cloud와 GitHub Team이고, 데이터 레지던시를 쓰는 Enterprise Cloud도 여기 들어갑니다. GitHub Enterprise Server에는 없습니다. 자체 호스팅 인스턴스만 굴리는 팀에게는 이 룰셋 옵션이 아직 선택지 밖에 놓여 있는 셈입니다.

GitHub 코드 커버리지 룰셋은 어떤 PR을 막나요?

막는 기준은 두 개뿐입니다. 하나는 최소 라인 커버리지 비율이고, 다른 하나는 최대 라인 커버리지 하락폭입니다. 앞엣것은 PR 브랜치의 집계 라인 커버리지가 정해 둔 비율 아래로 내려가면 병합을 막고, 뒤엣것은 기본 브랜치 대비 라인 커버리지가 정해 둔 퍼센트포인트보다 많이 떨어지면 막습니다(Setting code coverage thresholds for pull requests).

「Setting code coverage thresholds for pull requests (GitHub Docs)」 문서 화면

출처: 「Setting code coverage thresholds for pull requests (GitHub Docs)」, docs.github.com. 2026-09-19 캡처.

여기서 걸리는 지점이 하나 있는데요, 판정에 쓰이는 값이 라인 커버리지 하나라는 점입니다. GitHub 문서는 Code Quality가 라인 커버리지만 보고하며, 커버리지 도구가 흔히 같이 내주는 함수·분기·구문 커버리지는 Cobertura XML 리포트 안에 들어 있어도 쓰지 않는다고 못 박습니다. 그 값들은 PR 화면에도 나오지 않고 임계값 규칙의 판정에도 들어가지 않습니다(Code coverage reference). 분기 커버리지를 팀 기준으로 삼아 온 저장소라면 숫자의 의미가 바뀌는 셈이라 임계값을 그대로 옮겨 적으면 안 맞습니다.

「Code coverage reference (GitHub Docs)」 문서 화면

출처: 「Code coverage reference (GitHub Docs)」, docs.github.com. 2026-09-19 캡처.

계산 방식은 단순해요. 테스트가 실행한 줄 수를 전체 줄 수로 나눈 비율이고, Code Quality는 브랜치마다 가장 최근 업로드를 저장해 두었다가 PR 브랜치 값을 기본 브랜치 값과 비교합니다. 같은 문서가 드는 예시에서는 기본 브랜치가 44퍼센트, PR 브랜치가 65퍼센트일 때 그 PR이 21퍼센트포인트를 얻은 것으로 셉니다. PR 화면에는 파일별 증감도 함께 붙어서, 수정한 파일 각각이 기본 브랜치보다 올랐는지 내렸는지가 따로 보입니다.

규칙을 만질 수 있는 사람은 저장소 소유자, 조직 소유자, admin 역할을 가진 사용자입니다.

Restrict code coverage 설정 위치와 임계값 입력 순서

클릭 경로는 이렇습니다. 저장소 메인 화면에서 저장소 이름 아래 Settings 탭으로 들어가고, 탭이 보이지 않으면 드롭다운을 펼쳐 Settings를 고릅니다. 왼쪽 사이드바 Code and automation 아래 Rulesets를 누른 다음 다시 Rulesets로 들어갑니다. 거기서 브랜치 룰셋을 새로 만들거나 기존 룰셋을 열고, Branch rules 목록에서 Restrict code coverage를 선택합니다. 임계값은 Additional settings를 펼쳐야 나옵니다. 마지막으로 Create 또는 Save changes를 누르면 끝입니다.

입력칸에서 헷갈리기 쉬운 값이 0입니다. GitHub 문서는 0을 “임계값 비활성화”로 정의합니다. 최소 커버리지만 걸고 하락폭은 신경 쓰지 않겠다면 하락폭 칸에 0을 넣는 식이고, 0을 “무조건 막겠다”는 뜻으로 읽으면 정반대로 동작합니다.

이 화면을 열기 전에 채워 둬야 할 조건이 문서에 두 줄로 적혀 있습니다. 저장소에서 Code Quality가 켜져 있어야 하고, PR 브랜치의 커버리지 데이터가 GitHub에 올라와 있어야 합니다.

시험 삼아 돌려 보는 경로에는 제약이 붙어 있어요. 룰셋의 enforcement 값은 disabled·active·evaluate 셋 중 하나인데, 관리자가 강제 적용 전에 규칙을 미리 재 보는 evaluate는 GitHub Enterprise에서만 제공된다고 REST API 문서가 적습니다(REST API endpoints for rules). 이 모드로 둔 규칙이 무엇을 걸렀는지는 관리자가 Rule Insights 페이지에서 본다고 같은 문서가 덧붙입니다. 엔터프라이즈 전용이라는 이 단서를 체인지로그의 제공 범위와 겹쳐 읽으면, GitHub Team 쪽에는 드라이런을 거치지 않고 임계값을 곧바로 active로 올리는 경로만 남는 쪽이겠네요. 이게 이 기능에서 가장 불편한 대목이에요.

REST API code_coverage 파라미터 두 개와 토큰 권한

이번에 열린 것이 바로 이 부분입니다. 룰셋 생성은 POST /repos/{owner}/{repo}/rulesets, 수정은 PUT /repos/{owner}/{repo}/rulesets/{ruleset_id}, 조회는 GET /repos/{owner}/{repo}/rulesets/{ruleset_id}로 처리합니다. 세분화된 개인용 액세스 토큰이나 GitHub App 토큰을 쓸 경우 생성과 수정 모두 저장소의 Administration 권한이 쓰기로 필요합니다. 요청 헤더에는 Accept: application/vnd.github+json과 API 버전 헤더를 같이 붙입니다.

규칙 배열에 넣는 객체는 타입이 code_coverage이고, 파라미터는 minimum_coveragemax_coverage_drop 두 개입니다. 앞은 요구하는 절대 최소 라인 커버리지 비율이고, 뒤는 기본 브랜치 대비 허용하는 하락 퍼센트포인트입니다.

{
  "name": "coverage-guard",
  "target": "branch",
  "enforcement": "active",
  "conditions": { "ref_name": { "include": ["refs/heads/main"], "exclude": [] } },
  "rules": [
    {
      "type": "code_coverage",
      "parameters": { "minimum_coverage": 62, "max_coverage_drop": 1.5 }
    }
  ]
}

같은 룰셋 스키마에 code_quality 타입도 함께 들어 있습니다. 이쪽 파라미터는 severity 하나이고 고를 수 있는 값은 errors·warnings·notes·all 넷인데요, 방향을 거꾸로 읽기 쉬운 값이에요. REST API 문서가 severity에 붙여 둔 정의는 “해결이 필요한 가장 낮은 심각도 등급”입니다. 고른 등급 아래를 봐주는 것이 아니라, 고른 등급을 바닥선으로 잡고 거기서부터 위쪽 품질 리뷰가 정리돼야 커밋을 병합할 수 있다는 뜻이죠. all은 등급을 가리지 않고 전부 해결 대상으로 두는 값입니다. 커버리지 숫자와 품질 지적은 서로 다른 규칙이라 하나만 걸어도 되고 둘을 같이 걸어도 됩니다.

API로 현황을 긁어 갈 때 조심할 구석이 하나 더 있습니다. 브랜치에 적용되는 규칙을 돌려주는 엔드포인트는 evaluatedisabled 상태의 룰셋을 결과에 넣지 않습니다. 인프라를 코드로 관리하면서 “이 브랜치에 커버리지 규칙이 걸려 있나”를 API 응답만 보고 판단하면, 평가 모드로 돌려 둔 룰셋이 통째로 안 보여서 없는 것으로 집계됩니다.

Cobertura XML 리포트 업로드가 먼저입니다

규칙을 켜도 업로드가 없으면 판정할 값이 없습니다. GitHub 문서가 안내하는 수동 설정 경로는 테스트 프레임워크가 Cobertura XML 형식 리포트를 내게 만들고, 그 파일을 Actions 워크플로에서 올리는 순서입니다(Setting up code coverage for your repository).

언어별 명령은 문서 표에 그대로 나옵니다. 파이썬은 pytest에 pytest-cov를 붙여 pytest --cov=. --cov-report=xml, 자바스크립트와 타입스크립트는 Istanbul 계열에서 nyc report --reporter=cobertura, 고는 go test -coverprofile=cover.out 뒤에 gocover-cobertura로 변환합니다. 루비는 SimpleCov에 Cobertura 포맷터를 추가하고, 자바는 JaCoCo 결과를 cover2cover.py 스크립트나 Gradle·Maven 플러그인으로 바꿉니다. Cobertura XML만 나오면 언어는 가리지 않아요.

업로드는 actions/upload-code-coverage@v1 액션이 맡습니다. 문서 예시가 이 액션에 넘기는 값은 세 개인데요, Cobertura XML 파일 경로를 적는 file, 커버리지를 잰 코드의 주 언어를 적는 language, 그 리포트를 구분할 이름을 적는 label입니다. 워크플로 권한에는 code-quality: write가 있어야 합니다.

permissions:
  contents: read
  code-quality: write

# 기본 브랜치 push 로 기준값을 만들고, pull_request 로 비교 대상을 만든다
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

트리거를 둘 다 걸어야 하는 이유가 여기 있습니다. Code Quality는 PR 브랜치 커버리지를 기본 브랜치 커버리지와 견주는 구조라, 기본 브랜치 쪽 업로드가 없으면 비교 대상이 비어 버립니다. 체크아웃 단계에서는 병합 커밋이 아니라 PR의 head 커밋을 집어야 커버리지 줄 번호가 변경분과 맞게 붙습니다.

포크에서 올라온 PR은 조건문으로 걸러 둡니다. 문서 예시의 if 조건은 이벤트가 pull_request가 아니거나, PR의 head 저장소가 현재 저장소와 같을 때만 업로드 단계를 돌리도록 짜여 있습니다. 외부 기여가 포크로 들어오는 오픈소스 저장소라면 그 PR에서는 업로드 단계가 통째로 건너뛰어진다는 뜻이라, 커버리지 규칙을 켜기 전에 이 조건문을 어떻게 둘지부터 정해야 하는 구성이에요.

업로드가 성공하면 PR에 github-code-quality[bot] 이름으로 커버리지 요약 코멘트가 붙습니다. 그 코멘트에 담기는 값은 집계 라인 커버리지 비율과 파일별 증감이에요.

YAML을 직접 쓰기 싫으면 자동 설정도 있어요. Settings → 사이드바 Security 아래 Code qualityCode coverage analysis 항목의 Setup 드롭다운에서 Generate workflow with AI를 고르면 에이전트가 저장소를 분석해 초안 PR을 열고 진행 중인 단계를 체크리스트로 적어 둡니다. 문서는 이 워크플로 생성 자체에는 추가 비용이 붙지 않는다고 밝힙니다.

GitHub Code Quality 요금은 어떻게 계산되나요?

돈이 붙는 자리는 세 군데입니다(GitHub Code Quality billing).

첫째는 GitHub Actions 분입니다. Code Quality 스캔이 Actions 워크플로로 돌기 때문에, 셀프 호스티드 러너를 쓰지 않는 한 분이 깎입니다. 상세 사용량 리포트에서 workflow_path 값이 dynamic/github-code-quality/codeql인 항목을 걸러 내면 이 스캔이 쓴 분만 따로 보입니다.

둘째는 AI 크레딧입니다. AI 모델을 쓰는 기능은 Code Quality 전용 할당량이 아니라 이미 쓰고 있는 공유 AI 크레딧 풀에서 차감되고, 문서가 밝힌 환산 기준은 1 AI 크레딧이 미화 0.01달러입니다. 사용량은 토큰 수로 매겨집니다. 모델 교체는 지원하지 않습니다. 분석 품질을 맞추려고 모델·프롬프트 조합을 고정해 두었다는 것이 문서의 설명인데요, 싼 모델로 갈아타 비용을 누르는 선택지가 없다는 얘기이기도 합니다.

셋째가 좌석 라이선스입니다. 기준은 Code Quality를 켠 저장소에 커밋한 고유 활동 커미터 수이고, 활동 여부는 최근 90일 안에 그 사람의 커밋이 저장소에 푸시됐는지로 가릅니다. 커밋을 언제 작성했는지는 보지 않습니다. 활동 커미터 한 명이 라이선스 한 개를 쓰는 셈법이라, 한 사람이 저장소나 조직을 몇 군데 오가며 커밋해도 조직·엔터프라이즈 전체로 묶어 한 개로 셉니다. GitHub App 봇은 세지 않고, Code Quality는 자체 라이선스를 쓰기 때문에 GitHub Advanced Security 라이선스를 갉아먹지도 않습니다. 소비 중인 라이선스 수는 조직이나 엔터프라이즈의 Licensing 페이지에 Consumed licenses로 표시됩니다.

좌석 단가는 이 문서에 나오지 않습니다. 조직 설정에서 저장소 접근 범위를 바꿀 때 뜨는 Review enablement and billing changes 대화상자가 켜지고 꺼지는 저장소 수와 그에 딸린 비용을 보여 준다고 Enabling GitHub Code Quality 문서가 적어 두었을 뿐이라, 금액은 조직 화면에서 확인하는 구조입니다.

Coveralls 요금과 비교하면 어느 쪽이 싼가요?

비교 기준을 요금 구조로 잡으면 성격이 확 갈려요. Coveralls 요금 페이지는 오픈소스를 계속 무료로 두고, Cloud Plans 묶음의 유료 플랜을 비공개 저장소 개수로 끊습니다. Starter가 월 10달러에 비공개 저장소 1개, Org가 월 50달러에 10개, Org+가 월 100달러에 20개, Pro가 월 200달러에 40개, Unlimited가 월 400달러에 무제한입니다. 모든 플랜이 사용자 수는 무제한이고, 연 단위로 결제하면 10퍼센트를 깎아 줍니다. 그 위로 Cloud Premium이 월 800달러에 비공개 저장소 무제한과 멀티 조직 결제를 얹어 따로 놓여 있고, 클라우드 플랜에는 월 200달러짜리 지원 애드온과 월 30달러에 업로드 3,000건을 더 얹는 사용량 애드온이 붙습니다.

여기에 조건 하나가 같은 페이지 머리에 달려 있습니다. Coveralls의 유료 플랜은 비공개 저장소 커버리지를 재는 용도이고 GitHub·GitLab·Bitbucket 가운데 한 곳의 조직 하나에 묶입니다. 그 조직의 저장소에 접근 권한이 있는 사용자는 Coveralls에서 해당 커버리지 리포트를 볼 수 있다는 설명도 같이 붙어 있어요. 조직을 여러 개 굴리는 팀이라면 요금 계산이 조직마다 따로 붙는 구조인 셈입니다.

같은 페이지 아래쪽 Enterprise Plans는 셈법이 다릅니다. 이쪽은 저장소가 아니라 좌석으로 값을 매겨서, Enterprise Cloud가 사용자 한 명당 월 35달러, 직접 호스팅하는 Enterprise On-Prem이 한 명당 월 25달러이고 조직·저장소·사용자 수 제한이 없습니다. Enterprise Cloud에는 최소 주문 40석이라는 단서가 달려 있어 시작 금액이 월 1,400달러로 적혀 있어요.

그래서 갈림길은 어느 묶음을 쓰느냐에서 생깁니다. 클라우드 플랜 범위에서는 Coveralls가 저장소 개수만 세고 사용자 수를 세지 않는데, Code Quality 쪽 좌석 계산은 거꾸로 활동 커미터 수만 봅니다.

저장소를 잘게 쪼개 쓰는 소수 정예 팀이라면 좌석 과금이 유리하고, 저장소는 몇 개 안 되는데 커미터가 수십 명인 조직이라면 저장소 과금이 눈에 띄게 쌉니다. 커미터가 20명인 팀이 한 조직 아래 비공개 저장소 8개를 굴리는 상황이면 Coveralls 클라우드 Org 플랜의 저장소 10개 한도 안에 들어가 월 50달러로 끝나는데, GitHub 쪽은 좌석 20개에 Actions 분과 AI 크레딧이 얹힙니다. 다만 단일 테넌트 인프라가 필요해 Enterprise Cloud를 골라야 하는 상황이면 최소 주문 40석 단서에 걸려 월 1,400달러부터라 이 계산이 뒤집힙니다. 직접 호스팅하는 Enterprise On-Prem 쪽은 한 명당 월 25달러만 적혀 있고 최소 좌석 규정은 요금 페이지에 없으니, 좌석 수가 적은 팀이 단일 테넌트를 원한다면 이쪽 견적을 먼저 받아 보는 편이 맞습니다.

그래도 GitHub 쪽에만 있는 값이 하나 있습니다. 서드파티 서비스에 저장소 접근 권한을 새로 내주지 않아도 된다는 점입니다. 업로드 토큰을 시크릿으로 관리하고 외부 대시보드 계정을 따로 붙이는 절차가 통째로 사라지고, 룰셋이라는 같은 화면에서 병합 차단까지 한 번에 묶입니다. 보안 심사를 통과시켜야 하는 조직에서는 이 한 줄이 월 50달러보다 크게 잡히기도 합니다.

공개 미리보기 표기와 Enterprise Server 미지원

표기가 한 군데 어긋나 있습니다. 체인지로그는 “정식 출시된 REST API”로 이 옵션을 관리한다고 쓰는데, GitHub 문서의 Restrict code coverage 항목에는 공개 미리보기이며 변경될 수 있다는 주의 문구가 그대로 붙어 있습니다. API 쪽이 정식 출시고 규칙 자체는 미리보기라는 읽기가 자연스럽지만, 미리보기 딱지가 남아 있는 동안에는 파라미터 이름이나 동작이 바뀔 여지를 감안해 두는 편이 안전합니다.

지원 언어도 짚어 둘 대목이에요. GitHub 문서가 규칙 기반 분석 대상으로 적어 둔 언어는 C#·Go·Java·JavaScript·Python·Ruby·TypeScript 일곱 가지입니다(GitHub Code Quality). 같은 문서는 최근 바뀐 코드에 한해서는 규칙 기반 질의가 아직 없는 언어까지 AI 분석이 훑는다고 덧붙입니다. 다만 커버리지 쪽은 이 목록과 무관합니다. Cobertura XML을 낼 수 있는 언어면 커버리지 숫자와 임계값 규칙은 동작합니다. 코드 품질 지적은 안 나오는데 커버리지 차단만 걸리는 조합이 나올 수 있다는 뜻이에요.

켜는 순서를 정리하면 엔터프라이즈 소유자가 Code Quality 사용을 허용해 두어야 하고, Actions가 켜져 있어야 하며, 그다음 저장소나 조직 단위로 켭니다. 조직 단위에서는 Repository access 드롭다운으로 전체·선택 저장소·필터 조건 중에 고르고, Enforce access를 켜면 저장소 관리자가 이 설정을 뒤집지 못합니다. 대규모 조직에서는 변경이 모든 저장소에 퍼지는 데 몇 분이 걸린다고 문서가 덧붙입니다.

라인 커버리지 하나로 병합을 막는 규칙이라, 숫자를 올리는 가장 쉬운 방법이 의미 없는 줄을 훑는 테스트를 늘리는 것이라는 오래된 문제는 그대로 남아 있습니다. 미리보기 딱지와 evaluate 모드의 Enterprise 전용 제약까지 겹친 지금이라면, 최소 커버리지 비율보다 최대 하락폭 쪽을 먼저 걸어 두는 구성이 덜 위험해 보이네요. 기본 브랜치 수치가 그대로면 통과하고, 떨어뜨리는 PR만 걸리니까요.

참고 자료