풀 리퀘스트 검사

풀 리퀘스트 검사 (Pull request checks)

풀 리퀘스트(PR)를 열면 Hugging Face CI가 몇 가지 검사를 실행하는데, PR이 머지되려면 모두 통과해야 합니다.

출처: 문서

본문

  • Fixing the CI는 검사를 통과하기 위해 로컬에서 실행할 명령을 정리해 둔 목록입니다.
  • 그 뒤의 섹션은 각 검사가 무엇을 검증하는지 설명합니다. 예를 들어 코드 품질과 저장소 일관성이 있죠.
  • Tests는 어떤 테스트가 실행되는지, 테스트 범주와 슬로우(slow) 테스트를 설명합니다.

CI 고치기 (Fixing the CI)

대부분의 경우 make style면 코드 품질 검사를 통과하기에 충분합니다. 가장 흔한 실패 지점이거든요.

make style

저장소 일관성, 복사본, 자동 생성 파일에서 실패하면 make fix-repo를 실행하세요. 스타일, 복사본, docstring, 자동 생성 파일을 한 번에 고쳐 줍니다.

make fix-repo

더 큰 변경이라면 아래의 3개 명령 시퀀스가 모든 검사를 다룹니다. 더 무겁지만 push 전에 모든 문제를 잡아줘요.

make fix-repo   # auto-fix everything that can be auto-fixed
make typing     # check types and model structure, fix any errors manually
make check-repo # verify all checks pass, fix anything that remains

make typing은 타입 오류와 모델 구조 위반을 잡아주며, 이건 직접 고쳐야 해요. make check-repo는 최종 읽기 전용 패스를 수행해 모든 것이 준비됐는지 확인할 수 있습니다.

[!NOTE] CI는 가끔 불안정(flaky)합니다. 내 변경과 무관한 검사가 실패하면 메인테이너에게 재실행을 요청하세요.

코드 품질 (Code quality)

코드 품질 검사는 포맷팅, 임포트, 타입 검사, 모델 구조 규칙을 다룹니다. 이는 make fix-repo와 make typing에 해당합니다.

make style(make fix-repo에 포함)은 Ruff 린팅·포맷팅, __init__.py 임포트 정렬 순서, 자동 매핑 일관성을 자동으로 고칩니다.

make typing은 ty로 타입 검사를 수행하고, TRansFormers(TRF) 규칙을 검증합니다. TRF 규칙은 설정 클래스 이름 규칙과 forward() 시그니처를 다뤄요. 타입 오류와 TRF 위반은 특정 규칙 번호를 보고하며 직접 고쳐야 합니다. 규칙은 mlinter 저장소에 있습니다. python -m utils.mlinter --list-rules를 실행하면 모든 TRF 규칙을 볼 수 있고, python -m utils.mlinter --rule TRFXXX로 특정 규칙의 전체 문서를 확인할 수 있어요.

TRF 규칙에 예외가 필요하다면 다음 옵션 중 하나를 선택하세요 (자세한 내용은 Suppressing violations를 보세요).

  • 해당 규칙의 utils/mlinter/rules.toml에 있는 allowlist_models 목록에 내 모델 이름을 추가합니다. 모델 파일 전체에 예외가 필요할 때 사용해요.
  • 플래그가 붙은 구성요소와 같은 줄, 혹은 그 바로 위 줄에 # trf-ignore: TRFXXX를 추가합니다. 플래그가 붙은 구성요소 하나에만 예외가 필요할 때 사용해요.

저장소 일관성 (Repository consistency)

저장소 일관성 검사는 make check-repo와 비슷하지만, 첫 실패에서 멈춘다는 점이 다릅니다. 아래 범주에 걸쳐 저장소의 내부 일관성을 유지합니다: 공용 객체가 임포트 가능하게 유지되고, 복사된 코드가 원본과 동기화되며, 자동 생성 파일(dummies, doctests, metadata)이 코드의 현재 상태를 반영해야 해요. 새 모델의 경우 모든 새 모델 클래스가 자동 매핑에 등록되었는지도 검증합니다.

범주 검증 내용 자동 수정?
Init 파일 src/transformers/models/__init__.py가 디스크의 임포트 구조와 일치해서, if TYPE_CHECKING 블록(타입 체커 임포트)이 레이지 런타임 절반과 같은 모델을 노출하는지 make fix-repo
복사본과 모듈식 # Copied from 블록이 원본과 일치하고 모듈식 생성 파일이 최신인지 make fix-repo
Docstring과 문서 인자 docstring이 함수 시그니처와 문서 목차에 일치하는지 make fix-repo
자동 생성 파일 Dummies, 파이프라인 타이핑, doctest 목록, 메타데이터, 의존성 테이블 make fix-repo
설정 검증 설정 클래스가 docstring에 유효한 체크포인트를 갖고 설정 속성이 모델링 파일과 일치하는지 수동
리뷰어 배정 모든 모델 디렉터리와 리뷰어 파일의 모든 규칙이 여전히 리뷰어에 도달하는지 수동

리뷰어 배정 (Reviewer assignment)

풀 리퀘스트가 리뷰 가능 상태로 표시되면, Assign PR Reviewers 워크플로우는 PR이 소유한 파일에서 변경한 줄 수를 기준으로 최대 두 명의 리뷰어를 요청합니다. 초안(draft) PR은 초안이 풀리기 전까지 리뷰어를 받지 않아요. 워크플로우 자체는 huggingface/transformers-ci에 있고 이 저장소가 이를 호출합니다. 여기에 남는 것은 워크플로우가 읽는 소유권 데이터입니다.

소유권은 파일별로 해석되며, 가장 구체적인 것부터 우선합니다.

위치 모습 용도
모델 파일 modular_<model>.py 또는 modeling_<model>.py의 앞부분 주석 블록에 있는 # Reviewers: @login 해당 모달리티 소유자가 아닌 다른 사람이 필요한 모델. 모듈식 변환기가 헤더를 생성된 modeling 파일로 복사하므로 태그가 재생성 후에도 유지됩니다
경로 규칙 .github/scripts/codeowners_for_review_action에 있는 /src/transformers/<area>/ @login 모델이 아닌 모든 것
모달리티 테이블 같은 파일에 있는 @@modality/vision @login 문서 페이지가 docs/source/en/_toctree.yml의 해당 섹션에 있는 모든 모델
캐치올(catch-all) * @Rocketknight1 @ArthurZucker 다른 무엇도 주장하지 않는 것

새 모델은 어디에도 항목이 필요 없습니다. 문서 목차 검사가 이미 요구하는 대로 _toctree.yml에 문서 페이지를 추가하기만 하면 모달리티에 배치되고, 그 모달리티 테이블이 이후부터 그것을 다룹니다.

리뷰는 저장소 협력자(collaborator)에게만 요청할 수 있습니다. 워크플로우는 각 리뷰어를 별도 호출로 요청하고 요청할 수 없는 사람은 건너뜁니다. 그래서 떠난 사람을 명명한 항목은 전체 리뷰어 대신 한 명만 소모하게 되고, 건너뛸 때마다 워크플로우 실행에 경고로 보고돼요.

풀 리퀘스트는 자신의 리뷰를 재라우팅할 수 없습니다. .github/scripts/codeowners_for_review_action은 PR의 head가 아니라 항상 기본(branch)에서 읽힙니다. 그리고 PR CI 보안 게이트는 협력자가 아닌 PR이 src/, tests/, docs/, utils/ 밖의 어떤 것도 건드리는 것을 차단합니다. 그래서 그 파일이 그 위치에 있는 것이에요. # Reviewers: 태그는 head에서 읽힙니다. 새 모델이 태그와 함께 도착하기 때문이죠. 따라서 태그가 명명하는 모든 로그인도 누군가 요청되기 전에 협력자 검사를 거칩니다.

make check-repository-consistency는 utils/check_reviewers.py를 실행하는데, 모델이 캐치올 외에는 아무에게도 닿지 않을 때, 규칙이 더 이상 어떤 파일과도 일치하지 않을 때, 모달리티 테이블이 목차와 불일치할 때 실패합니다. 이는 배정 워크플로우와 해석기(resolver)를 공유하므로 둘이 불일치할 수 없어요. 그 해석기는 transformers의 의존성이 아니고, utils/checkers-requirements.txt에 나열되며 체커 러너가 환경에서 첫 실행 시 설치합니다. 오프라인처럼 설치가 불가능하면 검사는 차단하지 않고 이를 보고하며 통과합니다.

src/transformers/models 밖에서 소유자가 없는 경로는 주의(note)로 보고됩니다. 다음 명령으로 나열해 보세요:

python utils/check_reviewers.py --strict

테스트 (Tests)

CI는 PR이 변경하는 내용에 따라 테스트의 일부 하위 집합을 실행합니다. CI는 pytest-random-order로 테스트를 약간 무작위화된 순서로 실행해 결합된(coupled) 테스트를 잡아냅니다. 실행은 시작 시 무작위 시드를 출력하므로, --random-order-seed=<seed>로 같은 순서를 재현할 수 있어요.

테스트가 로컬 GPU에서는 통과하는데 CI에서 실패한다면, TRANSFORMERS_TEST_DEVICE="cpu"로 설정해 CPU에서 실패를 재현할 수 있는지 확인하세요.

TRANSFORMERS_TEST_DEVICE="cpu" pytest tests/models/my_model/ -v

아래 섹션은 테스트 선택이 어떻게 동작하는지, 어떤 작업이 실행되는지, 슬로우 테스트를 어떻게 다루는지 설명합니다.

테스트 선택 (Test selection)

CI는 모든 PR에서 전체 테스트 스위트를 실행하지 않습니다. CI는 모든 PR에서 @slow로 데코레이션된 테스트를 건너뛰고, PR이 리뷰 중이면 메인테이너가 GPU에서 이를 트리거합니다.

utils/tests_fetcher.py는 변경된 파일에서 임포트 의존성을 추적해 영향을 받는 테스트를 식별하고, 그 테스트만 실행합니다. 공유 유틸리티를 건드릴 때 다른 모델의 회귀도 잡아줘요. 페처는 어떤 파일이 변경됐고 어떤 테스트가 영향을 받는지 출력한 뒤, 목록을 tests_torch_test_list.txt에 씁니다.

페처를 사용해 CI가 실행하는 것과 정확히 동일하게 재현할 수 있습니다.

python utils/tests_fetcher.py
python -m pytest -n 8 --dist=loadfile -rA -s $(cat test_preparation/tests_torch_test_list.txt)

[!TIP] modeling_utils.py나 generation/utils.py 같은 핵심 파일을 변경하면 영향을 받는 하위 집합뿐만 아니라 모든 모델 테스트가 트리거됩니다.

내 모델에 대한 모든 테스트를 무조건 실행할 수도 있어요. 직접 실행하는 것은 로컬에서 더 빠른 빠른 검사(sanity check)이지만, 건드린 공유 코드 때문에 생긴 다른 모델의 회귀는 잡지 못합니다.

pytest tests/models/my_model/ -v

테스트 작업 범주 (Test job categories)

테스트는 병렬 CI 작업에 나뉘고, 각 작업은 경로 패턴으로 파일을 가져옵니다. 모델 PR과 관련된 작업은 다음과 같습니다.

  • tests_torch: 모델링 테스트 (tests/models/*/test_modeling_*.py)
  • tests_tokenization: 토크나이저 테스트 (tests/models/*/test_tokenization_*.py)
  • tests_processors: 프로세서 및 특징 추출기 테스트 (tests/models/*/test_(processing|image_processing|feature_extractor)_*.py)
  • tests_generate: 생성 테스트
  • pipelines_torch: 파이프라인 테스트
  • tests_training_ci: 훈련 루프 테스트
  • tests_tensor_parallel_ci: 텐서 병렬 테스트
  • tests_fsdp_ci: FSDP 테스트

슬로우 테스트 (Slow tests)

일반 CI 실행은 @slow로 데코레이션된 테스트를 건너뜁니다. 실제 체크포인트를 다운로드하거나 상당한 컴퓨팅이 필요하므로 GPU 인스턴스에서 실행되며, PR이 리뷰 중이면 메인테이너가 트리거해요.

슬로우 테스트는 NVIDIA A10에서 실행되며, 수치 결과는 CI 하드웨어와 로컬 기기 사이에서 약간 달라질 수 있습니다. 메인테이너는 보통 새 모델을 추가할 때 필요하면 테스트에서 그 값을 조정합니다.

슬로우 테스트를 로컬에서 실행하려면 아래 명령을 사용하세요.

RUN_SLOW=1 python -m pytest tests/models/my_model/ -v

문서 빌드 (Documentation build)

build_pr_documentation 작업이 문서의 빌드와 미리보기를 생성합니다. 봇이 PR에 미리보기 링크를 올리고, 머지 전에 검사가 통과해야 합니다. 대부분의 실패는 toctree의 누락된 항목입니다. 문서를 로컬에서 빌드하려면 docs 폴더의 README.md를 보세요.

# Copied from 문법

[!WARNING] 새 모델에는 # Copied from보다 항상 모듈식 워크플로우(modular_*.py)를 우선하세요. 가능하면 # Copied from을 피하는 것이 좋아요.

# Copied from 메커니즘은 복사된 코드를 원본과 동기화된 상태로 유지합니다. make fix-repo를 실행하면 모든 # Copied from 블록을 확인하고 원본과 일치하도록 업데이트합니다. 따라서 # Copied from 블록 안의 편집은 덮어써집니다. 원본을 편집하고 make fix-repo가 변경을 전파하게 하세요.

# Copied from의 기본 형태에는 다음이 포함됩니다.

# Copied from transformers.models.bert.modeling_bert.BertSelfOutput

# Copied from transformers.models.bert.modeling_bert.BertAttention with Bert->Roberta

# Copied from transformers.models.bert.modeling_bert.BertForSequenceClassification with Bert->MobileBert all-casing

with model->newModel 문법은 복사 후 문자열 치환을 적용합니다. 여러 치환은 쉼표로 구분하며 왼쪽에서 오른쪽으로 적용됩니다. all-casing 옵션은 모든 대소문자 변형을 한 번에 치환합니다(Bert, bert, BERT가 MobileBert, mobilebert, MOBILEBERT가 됩니다).

더 알아보기 (Learn more)