빌드봇으로 작업하기

개발 및 유지 보수 브랜치에 회귀가 없음을 보장하기 위해, Python은 지속적 통합에 사용되는 전용 머신들(빌드봇 또는 빌드 워커라고 부릅니다)을 갖추고 있습니다. 이 머신들은 다양한 하드웨어/운영 체제 조합을 아우릅니다. 또한 각 머신은 활성 브랜치마다 하나씩, 여러 빌더를 호스팅합니다: 공개 GitHub 저장소의 해당 브랜치에 새 변경 사항이 푸시되면, 대응하는 모든 빌더가 가능한 한 빨리 실행될 새 빌드를 예약합니다.

빌드봇이 실행하는 빌드 단계는 다음과 같습니다:

  • 빌드를 촉발한 변경 사항의 소스 트리를 체크아웃합니다

  • Python을 컴파일합니다

  • 엄격한 설정으로 테스트 스위트를 실행합니다

  • 빌드 트리를 정리합니다

저장소에 변경 사항을 푸시한 후 자동 빌드 결과를 확인하는 것은 코어 팀 구성원의 책임입니다. 따라서 이 결과가 어떻게 표시되는지, 그리고 다양한 종류의 실패를 어떻게 설명하고 진단할 수 있는지 잘 알고 있는 것이 중요합니다.

문제가 생긴 경우

이 페이지를 끝까지 읽어 주십시오. 여기에서 질문에 대한 답을 찾지 못했고 빌드봇과 관련해 도움이 필요하다면, 다음 중 한 가지 방법으로 도움을 받는 것이 좋습니다:

  • 모든 빌드봇 워커 소유자가 구독하고 있는 python-buildbots@python.org 메일링 리스트에 문의하거나,

  • 문제가 있는 브랜치의 릴리스 관리자에게 문의하십시오.

풀 리퀘스트에 대한 빌드봇 실패

안정 빌드봇 워커에서 여러분의 커밋을 빌드하는 데 실패하면, GitHub의 Bedevere 봇이 병합된 풀 리퀘스트에 메시지를 남깁니다. 언뜻 보기에 관련이 없어 보이더라도 실패를 주의 깊게 평가하십시오.

모든 빌드가 커밋마다 실행되는 것은 아니므로, 모든 실패에 대해 알림이 생성되지는 않습니다. 특히 참조 누수 빌드는 완료하는 데 몇 시간이 걸리므로 주기적으로 실행됩니다. 그렇기 때문에 여러분이 직접 결과를 확인할 수 있는 것도 중요합니다.

풀 리퀘스트에서 빌드봇 실행하기

풀 리퀘스트에서 빌드봇을 실행하려면 CPython 트리아지 담당자이거나 코어 팀 구성원이어야 합니다. 그렇지 않다면 다른 사람에게 대신 실행해 달라고 요청하십시오.

PR에서 대부분의 빌드봇을 실행하는 가장 간단한 방법은 🔨 test-with-buildbots 및 🔨 test-with-refleak-buildbots 레이블을 사용하는 것입니다. (PR 전용 레이블를 참조하십시오.)

이 레이블들은 가장 최근 커밋에 대해 빌드봇을 실행합니다. 이후 커밋에 대해 빌드봇을 다시 실행하려면 레이블을 제거했다가 다시 추가해야 합니다.

특정 플랫폼에서 풀 리퀘스트를 테스트하려면, 다음으로 시작하는 댓글을 게시하여 하나 이상의 빌드봇을 실행할 수 있습니다:

!buildbot regex-matching-target

예를 들어 iOS와 Android 빌드봇을 모두 실행하려면 다음을 사용할 수 있습니다:

!buildbot ios|android

Bedevere는 일치하는 빌드봇이 있다면 어떤 빌드봇이 일치했는지 알려 주는 댓글을 게시합니다. 일치하는 빌드봇이 없거나 요청을 실행하는 데 필요한 권한이 없는 경우에도 그 사실을 알려 줍니다.

!buildbot 댓글도 가장 최근 커밋에 대해서만 빌드봇을 실행합니다. 이후 커밋에서 빌드봇을 다시 실행하려면 댓글을 다시 달아야 합니다.

자동 빌드 결과 확인하기

https://buildbot.python.org/#/ 의 웹 인터페이스에서는 최근 빌드 결과를 여러 방식으로 볼 수 있습니다:

  • 활성 브랜치별 빌드봇 상태를 보여 주고 빌드가 릴리스할 준비가 되었는지 요약해 주는 릴리스 상태 대시보드.

  • 빌더별 최근 빌드를 세로로 나열해 보여 주는 폭포 뷰. 특정 빌드가 궁금하면 그 빌드를 클릭하십시오. 로그나 해당 빌드에 대응하는 커밋 등 더 자세한 정보를 볼 수 있습니다.

  • 넓은 고해상도 모니터에서 가장 잘 보이는 콘솔 뷰. 색이 칠해진 원을 클릭하면 새 페이지가 열리며, 해당 빌드에 대해 궁금한 정보를 무엇이든 확인할 수 있습니다. 맨 윗줄에 있는 빌더 상태 말풍선을 클릭하여 빌더 정보를 볼 수도 있습니다.

빌드봇 웹 페이지는 로딩이 느린 경우가 많으니 인내심을 가지십시오.

어떤 빌드봇은 다른 빌드봇보다 훨씬 빠릅니다. 시간이 지나면 빌드 후 어떤 빌드봇이 가장 빨리 결과를 내고 어떤 빌드봇이 가장 오래 걸리는지 알게 될 것입니다.

또한 같은 브랜치에 여러 커밋이 연달아 빠르게 푸시되면 이 커밋들 전체에 대해 빌드가 하나만 예약되는 경우가 많습니다.

안정성

빌드봇 중 일부는 “안정(stable)”으로 표시되어 있습니다. 이 빌드봇들은 새 릴리스를 만들 때 고려 대상이 됩니다. 규칙상 릴리스를 만드는 시점에 1등급과 2등급의 모든 안정 빌더에서 지속적인 실패가 없어야 합니다(자세한 내용은 PEP 11을 참고하십시오). 코어 팀 구성원은 자신이 안정 빌드봇에 일으킨 문제를 가능한 한 빨리 수정하거나 되돌리는 것이 절대적으로 중요합니다.

그렇다고 다른 빌더의 테스트 결과를 가볍게 여겨도 된다는 뜻은 아닙니다. 그중 일부는 플랫폼 고유의 문제로 일부 테스트가 성공하지 못하는(또는 아예 종료되지 않는) 것으로 알려져 있지만, 일반적으로 실패를 추가로 일으키는 일은 있어서는 안 됩니다.

플래그에 따른 실패

커밋하기 전에 전체 테스트 스위트를 실행했는데도 빌드봇에서 예상치 못한 실패를 보게 될 때가 있습니다. 이런 차이가 생기는 원인 중 하나는 테스트 실행기나 Python 자체에 서로 다른 플래그가 전달된 경우입니다. 문제를 재현하려면 빌드봇과 같은 플래그를 사용해야 합니다. 이 플래그는 실패한 빌드의 테스트에 있는 stdio 링크를 클릭하기만 하면 확인할 수 있습니다. 예를 들면 다음과 같습니다:

./python -E  -m test --slow-ci --timeout=1200 -j2 --junit-xml test-results.xml -j10

순서에 따른 실패

때로는 테스트가 실행되는 순서에 따라 실패가 발생하기 때문에 원인을 알아차리기가 더욱 어렵습니다. 빌드봇은 라이브러리 모듈 간에 일어날 수 있는 간섭이 드러날 가능성을 최대화하기 위해 (테스트 실행기에 -r 옵션을 사용하여) 테스트 순서를 무작위화합니다. 그 대신 실패가 산발적으로 일어나는 것처럼 보일 수 있다는 단점이 있습니다.

--randseed 옵션을 사용하면 특정 빌드에서 사용한 무작위화를 그대로 쉽게 재현할 수 있습니다. 마찬가지로 실패한 테스트 실행의 stdio 링크를 열고 테스트 출력 본문의 시작 부분을 확인하십시오.

예를 들어 출력이 다음과 같이 시작한다고 가정해 봅시다:

./python -E  -m test --slow-ci --timeout=2400 -j2 -u-cpu,-urlfetch,-network --junit-xml test-results.xml -j4
== CPython 3.15.0a6+ (heads/main:d625f7da33b, Feb 13 2026, 17:27:29) [GCC 12.2.0]
== Linux-6.12.20+rpt-rpi-v8-aarch64-with-glibc2.36 little-endian
== Python build: release
== cwd: /home/stan/buildarea/3.x.stan-raspbian.nondebug/build/build/test_python_worker_181905æ
== CPU count: 4
== encodings: locale=ISO-8859-1 FS=utf-8
== resources: all,-cpu,-network,-urlfetch
Using random seed: 1000348774
0:00:00 load avg: 3.34 Run 500 tests in parallel using 4 worker processes (timeout: 40 min, worker timeout: 45 min)
0:00:01 load avg: 3.34 [  1/500] test_colorsys passed
0:00:01 load avg: 3.34 [  2/500] test_float passed

--randseed 1000348774 옵션을 추가하면 정확히 같은 순서를 재현할 수 있습니다:

./python -E  -m test --slow-ci --timeout=2400 -j2 -u-cpu,-urlfetch,-network --junit-xml test-results.xml -j4 --randseed 1000348774

다음 순서로 테스트가 실행됩니다(간결하게 하려고 일부를 생략했습니다):

[  1/500] test_colorsys
[  2/500] test_float
[  3/500] test.test_io.test_memoryio
[  4/500] test_profile
[  5/500] test_picklebuffer
[  6/500] test_zipimport
[  7/500] test_devpoll
...

이것으로 여러분의 환경에서 실패를 재현할 수 있다면, 테스트 순서를 이분 탐색하여 실패를 일으키는 특정 간섭을 찾을 수 있습니다. 테스트 순서를 텍스트 파일에 복사하여 붙여 넣은 다음, 테스트 실행기의 --fromfile (또는 -f) 옵션을 사용하여 해당 텍스트 파일에 기록된 순서 그대로 실행하십시오:

./python -E  -m test --slow-ci --timeout=2400 -j2 -u-cpu,-urlfetch,-network --junit-xml test-results.xml -j4 --fromfile mytestsequence.txt

위의 예시 순서에서 test_zipimport가 실패했다면, 먼저 다음 순서를 테스트합니다:

[  1/500] test_colorsys
[  2/500] test_float
[  3/500] test.test_io.test_memoryio
[  6/500] test_zipimport

그리고 이 순서가 성공하면, 대신 다음 순서를 테스트합니다(이번에는 실패하기를 바랍니다):

[  4/500] test_profile
[  5/500] test_picklebuffer
[  6/500] test_zipimport

그런 다음, 실패를 일으키는 테스트 한 쌍만 남을 때까지 재귀적으로 탐색 범위를 좁혀 나가십시오. 이러한 간섭에 두 개보다 많은 테스트가 관련되는 경우는 매우 드뭅니다. 만약 그런 경우라면, 행운을 빌어 드릴 수밖에 없습니다!

참고

순서에 의존하는 실패를 진단할 때는 -j 옵션(병렬 테스트용)을 사용할 수 없습니다. -j를 사용하면 각 테스트가 깨끗한 새 서브프로세스에서 격리되어 실행되므로, 테스트 간의 간섭을 재현할 수 없게 됩니다.

일시적인 실패

테스트 스위트를 최대한 신뢰할 수 있게 만들려고 노력하지만, 일부 테스트는 완벽한 수준의 재현성에 도달하지 못합니다. 그중 일부는 다양한 조건에 따라 때때로 가짜 실패를 보이기도 합니다. 흔히 문제를 일으키는 테스트는 다음과 같습니다:

  • test_poplib, test_urllibnet 등 네트워크 관련 테스트. 이러한 테스트의 실패는 좋지 않은 네트워크 상태나, 종종 별도의 스레드에서 서버를 실행해야 하는 테스트 코드의 불완전한 스레드 동기화에서 비롯될 수 있습니다.

  • 스레드 간 또는 프로세스 간 동기화나 Unix 시그널처럼 까다로운 문제를 다루는 테스트: test_multiprocessing, test_threading, test_subprocess, test_threadsignals.

실패가 일시적인 것일 수 있다고 생각되면, 다음 빌드를 기다려 확인하는 것을 권장합니다. 그렇더라도 실패가 산발적이고 예측할 수 없는 것으로 밝혀지더라도, 해당 문제는 버그 추적기에 보고해야 합니다. 테스트 구현을 수정하거나 타임아웃과 같은 매개변수를 더 견고하게 만들어 문제를 진단하고 억제할 수 있다면 더욱 좋습니다.