빌드봇으로 작업하기

관련 개발 및 유지보수 브랜치에 회귀가 없음을 확인하기 위해 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/#/ 의 웹 인터페이스는 최근 빌드 결과를 시각화하는 여러 방법을 제공합니다.

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

  • 각 빌더의 최근 빌드를 세로로 나열하는 폭포식 보기입니다. 특정 빌드에 관심이 있다면 해당 빌드를 클릭하여 로그 및 대응하는 커밋과 같은 자세한 정보를 확인하십시오.

  • 넓은 고해상도 모니터에서 가장 잘 작동하는 콘솔 보기입니다. 색상이 있는 원을 클릭하면 해당 빌드에 관해 관심 있는 정보를 담은 새 페이지를 열 수 있습니다. 맨 위 줄의 빌더 상태 풍선을 클릭하여 빌더 정보에 접근할 수도 있습니다.

빌드봇 웹 페이지는 로드가 느린 경우가 많으므로 기다려 주십시오.

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

또한 같은 브랜치에 여러 커밋이 빠르게 연속으로 푸시되면 이 모든 커밋에 대해 단일 빌드가 예약되는 경우가 많습니다.

안정성

빌드봇 중 일부는 “안정적”이라고 표시됩니다. 새 릴리스를 만들 때 이러한 빌드봇이 고려됩니다. 릴리스할 때 티어 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.

실패가 일시적일 수 있다고 생각되면 다음 빌드를 기다려 확인하는 것이 좋습니다. 그럼에도 실패가 간헐적이고 예측 불가능한 것으로 밝혀지더라도 버그 추적기에 이슈를 보고해야 합니다. 테스트 구현을 수정하거나 시간제한과 같은 매개변수를 더 견고하게 만들어 문제를 진단하고 억제할 수 있다면 더욱 좋습니다.