본문으로 건너뛰기

생성된 requirements 드리프트

증상

워커 CI는 다음과 같은 형태의 출력으로 Requirements drift check 단계에서 실패합니다.

ERROR: services/<svc>/requirements.txt is out of sync with uv.lock.
Run 'make regen-requirements' and commit the result.

현재 검사는 lumie-worker/.github/workflows/ci.yml에 있으며 grading, report, analysis, chatbot에 대해 생성한 export 결과를 커밋된 services/<svc>/requirements.txt 파일과 비교합니다.

CI가 계약으로 간주하는 것

계약은 2026년 5월 14일부터 5월 16일까지 종속성 변경 사항에 걸쳐 명시되었습니다.

날짜커밋계약 변경
2026-05-1479bc1fe1워커 서비스가 uv workspace 기준으로 표준화됨
2026-05-161a7ffc16uv.lock가 잠금 소스로 추가되었습니다
2026-05-16258735b4생성된 requirements.txt 파일과 make regen-requirements가 고정된 export 경로가 됨
2026-07-12072c96b6종속성 변경 후 분석 요구사항을 다시 생성해야 함

Makefile은 재생성을 위한 로컬 기준입니다.

EXPORT = $(UV) export --frozen --no-dev --no-emit-project --no-header

regen-requirements:
$(EXPORT) --package grading-svc --output-file services/grading/requirements.txt
$(EXPORT) --package report-svc --output-file services/report/requirements.txt
$(EXPORT) --package analysis-svc --output-file services/analysis/requirements.txt
$(EXPORT) --package chatbot-svc --output-file services/chatbot/requirements.txt

출처: lumie-worker/Makefile

CI는 .github/workflows/ci.yml에서 동일한 export 플래그를 사용하므로, 정확한 export 출력과 일치하지 않는 커밋된 파일은 드리프트로 처리됩니다.

가능한 원인

서비스 pyproject.toml은 변경됐지만 requirements.txt는 변경되지 않았습니다

이것은 가장 흔한 실패입니다. 예를 들어 현재 services/analysis/pyproject.toml에는 asyncpg==0.30.0reportlab==4.2.5가 포함됩니다. 이러한 종속성은 chardetpillow와 같은 전이적 패키지와 함께 현재 생성된 services/analysis/requirements.txt에도 나타납니다.

uv.lock은 변경됐지만 export가 다시 생성되지 않았습니다

Makefile은 uv export --frozen를 사용하기 때문에 커밋된 requirements.txt 파일은 서비스 pyproject.toml 입력뿐 아니라 잠금 파일과도 정확히 일치해야 합니다.

requirements.txt는 직접 편집되었습니다

생성된 파일은 해시와 전이적 종속성을 포함한 전체 잠금 export입니다. 파일이 수동으로 줄었거나 부분 편집된 것처럼 보이면 CI는 즉시 이를 드리프트로 비교합니다.

로컬 uv 출력이 CI와 일치하지 않습니다.

Makefile 주석은 이 위험을 직접 언급합니다. CI는 requirements 드리프트 단계에서 표준 uv 버전을 고정합니다. 로컬 uv 출력이 다르면 종속성 그래프가 개념적으로 바뀌지 않았더라도 잘못된 드리프트를 볼 수 있습니다.

진단 경로

  1. CI 출력에서 실패한 서비스 이름을 읽습니다.
  2. 서비스 pyproject.toml을 생성된 파일과 비교합니다. services/report/pyproject.tomlservices/analysis/pyproject.toml는 직접 종속성 변경을 확인할 첫 번째 위치입니다.
  3. uv.lock에 패키지가 있는지 확인합니다. 현재 잠금 매니페스트에는 analysis-svc, chatbot-svc, grading-svcreport-svc가 포함됩니다.
  4. 동일한 export 플래그를 사용해 정확한 CI 차이를 로컬에서 재현합니다.

analysis-svc에 대한 대표 확인:

uv export --frozen --no-dev --no-emit-project --no-header --package analysis-svc \
| diff -u services/analysis/requirements.txt -

수정

lumie-worker 저장소 루트에서:

make regen-requirements

이 명령은 CI와 동일한 플래그를 사용해 uv.lock에서 모든 워커 서비스의 requirements.txt 파일을 다시 생성합니다.

make regen-requirements--frozen와 함께 실패하면 먼저 잠금 파일을 수정해 uv.lock이 서비스 pyproject.toml 입력과 일치하도록 합니다. 명령은 성공했지만 CI가 여전히 드리프트를 보고하면 로컬 uv 버전을 .github/workflows/ci.yml에 고정된 버전과 맞춥니다.

확인

  • 영향을 받은 서비스에 대해 정확한 diff 명령을 다시 실행하고 출력이 없는지 확인합니다.
  • 재생성된 파일이 직접 유지 관리하는 단순 종속성 목록이 아니라 해시가 포함된 전이적 export인지 확인하세요.
  • 워커 CI를 다시 실행하거나 .github/workflows/ci.ymlRequirements drift check 단계가 통과하는지 확인합니다.

운영 세부사항

생성된 requirements.txt 파일은 deployment artifact이지만 각 worker service image가 이를 소비하기 때문에 commit됩니다. 따라서 drift는 cosmetic CI 실패가 아니라 release blocker입니다.

파일역할
service pyproject.tomlservice direct dependency를 선언합니다.
uv.lockresolved workspace dependency graph를 고정합니다.
services/<svc>/requirements.txtservice build가 소비하는 generated export입니다.
.github/workflows/ci.ymlcommitted export가 lock graph와 일치하는지 강제합니다.

한 service dependency만 바뀌었더라도 모든 service requirements file을 다시 생성합니다. Makefile target은 shared transitive change가 두 번째 service를 stale 상태로 남기지 않도록 의도적으로 all-service입니다.

복구 절차

  1. CI에서 실패한 service를 식별합니다.
  2. 개별 service directory가 아니라 lumie-worker에서 make regen-requirements를 실행합니다.
  3. 예상 밖 dependency graph change가 있는지 diff를 확인합니다.
  4. uv export --frozen이 실패하면 export를 재생성하기 전에 uv.lock을 갱신하거나 복구합니다.
  5. local output이 CI와 다르면 local uv version과 workflow-pinned version을 비교합니다.
  6. pyproject.toml, uv.lock, generated requirements가 모두 바뀌었다면 함께 commit합니다.

검증 증거

다음으로 실패를 닫습니다.

  • affected service export에 대한 local diff check가 깨끗합니다.
  • hand-edited short requirements file이 없습니다.
  • CI Requirements drift check가 통과합니다.
  • 큰 transitive dependency movement가 있다면 diff review에 이유가 설명되어 있습니다.

피해야 할 패턴

  • runtime missing package를 해결하려고 requirements.txt를 직접 편집하는 것.
  • 먼저 실패한 service 하나만 재생성하는 것.
  • generated export 없이 uv.lock만 update하는 것.
  • local/CI uv version mismatch를 무시하고 churn commit을 반복하는 것.