Hacktoberfest 2026:維護者為十月標記出來的 issue,仍然開放、適合新手。 瀏覽 Hacktoberfest issue

Migrate dependency management from pip to uv

未關閉
#211 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

評估

難度
4/5
預估耗時
3-5 天
新手友好度
48/100
Issue 類型
重構
描述清晰度
基本清楚
活躍度
冷清
技術堆疊
docker, github-actions, python

研究方向

先從 pyproject.toml、requirements.txt 和 requirements-dev.txt 開始,在修改部署檔案之前,將現有的直接 pin 和開發依賴對應到由 UV 管理的 manifest。接著檢查 Dockerfile 中目前的 pip install 步驟、.github/actions/setup-dev-python-env/action.yml 以及 .github/workflows/check_python.yml,因為它們控制安裝流程和觸發行為。編輯後,檢查 DEVELOPER.md、Makefile、.github/dependabot.yml 以及文件更新,然後執行 uv sync --group dev、make qa 和 Python workflow。完成的標準是:僅涉及依賴的變更仍會觸發 CI 路徑;生產映像不會安裝開發依賴,也不會隨映像發布 /bin/uv;外部 pipeline build 在真實的憑證/索引條件下成功。

由索引模型根據 Issue 內容生成。

描述

infrastructure

The task

Migrate the project's Python dependency and environment management from pip + requirements*.txt to uv.

Motivation
  • Reproducible builds via a committed uv.lock (today nothing pins transitive dependencies — only direct ones are pinned in requirements*.txt).
  • Single source of truth for metadata + dependencies in pyproject.toml (currently it holds only tool config: black, coverage, mypy).
  • Much faster resolve/install in CI and Docker builds.
  • Managed interpreter provisioning, so local setup and CI agree on Python 3.13.
Current state (what touches pip today)
File Usage
requirements.txt 9 pinned production deps (confluent-kafka, psycopg2, boto3, ...)
requirements-dev.txt -r requirements.txt + 11 test/lint deps, incl. psycopg2-binary
pyproject.toml No [project] table at all — tool config only
Dockerfile:65 pip install -r requirements.txt --no-binary confluent-kafka
.github/actions/setup-dev-python-env/action.yml setup-python with cache: 'pip' + pip install -r requirements-dev.txt
.github/workflows/check_python.yml:42,44 Change detection filters on requirements*.txt
.github/dependabot.yml:22 package-ecosystem: "pip"
Makefile:3 PYTHON := .venv/bin/python
DEVELOPER.md:31-36 python3 -m venv + pip3 install -r requirements-dev.txt
.github/copilot-instructions.md:56 Documents the two-requirements-files layout
Scope of work

1. pyproject.toml

  • Add a [project] table: name, version, requires-python = ">=3.13,<3.14", and dependencies migrated from requirements.txt (keep the exact == pins on the first pass so the migration is a no-op dependency-wise).
  • Add [dependency-groups] dev with the contents of requirements-dev.txt.
  • Decide how to handle the psycopg2 (prod) vs psycopg2-binary (dev) split — both are currently installed in a dev env. Either keep both (they are distinct distributions and resolve fine) or move to a single one plus a build-time flag. Document whichever is chosen.
  • Add [tool.uv] config if needed (e.g. no-binary-package = ["confluent-kafka"] for the Docker path).

2. Lockfile

  • Generate and commit uv.lock.
  • Add a .python-version file pinning 3.13.

3. Dockerfile — the highest-risk part of this migration

This Dockerfile is consumed by an external deployment pipeline, not by this repo's CI. That pipeline clones EventGate, supplies the config file, the trusted certificates (TRUSTED_SSL_CERTS) and the SASL/SSL artifacts (SASL_SSL_ARTIFACTS), then runs docker/build-push-action on top. Consequences:

  • The build environment is not a plain GitHub-hosted runner with open egress. Whatever assumptions uv makes about network, TLS trust and package indexes have to hold there, and that environment is not visible from this repo.
  • A break lands in the deployment pipeline, in another repo, after merge here — not on the PR. See item 4b for coordination.

Treat this section as the critical path.

Blockers to resolve before writing the Dockerfile change

These are places where uv does not behave like pip, and each is invisible until the deployment pipeline runs.

(a) TLS trust — uv ignores the system CA bundle by default. The image's first build step exists precisely to append the pipeline-supplied certs to /etc/pki/tls/certs/ca-bundle.crt (typical of an inspecting corporate proxy). uv ships its own TLS stack and does not read the system trust store unless told to. Any uv network operation must run with UV_NATIVE_TLS=1 (or --native-tls), otherwise dependency resolution fails with certificate errors in the deployment pipeline while working fine on an unproxied laptop. Set it as an ENV before the install step.

(b) Package index — uv does not read pip.conf / PIP_INDEX_URL. If the pipeline points pip at an internal mirror (Artifactory/Nexus) via pip.conf, PIP_INDEX_URL or PIP_EXTRA_INDEX_URL, uv will silently ignore all of it and go to public PyPI — which may be blocked, or worse, may not be the intended source. uv needs UV_DEFAULT_INDEX / UV_INDEX env vars or a [[tool.uv.index]] entry in pyproject.toml. Confirm with the pipeline owners whether an internal index is in play before implementing.

(c) Getting the uv binary into the image. COPY --from=ghcr.io/astral-sh/uv:... needs a pull from ghcr.io at build time. If the deployment environment only permits a mirrored registry, that fails. Fallbacks, in order of preference: mirror the uv image internally; or pip install uv==<pin> from the already-configured index as the first step (keeps a single network source, costs one pip invocation); or vendor the binary. Pick based on (b)'s answer.

Two further constraints must survive the migration:

  1. --no-binary confluent-kafka is load-bearing. It forces a source build against the system librdkafka compiled earlier in the image, which is what provides Kerberos/GSSAPI support. The PyPI wheel is built without GSSAPI. The uv equivalent is --no-binary-package confluent-kafka.
  2. Deps currently land in the base image's system site-packages (plain pip install, no --target), which is already on the Lambda import path. Keep that placement — uv pip install --system is the direct equivalent. Do not switch to --target "${LAMBDA_TASK_ROOT}" as part of this change; that alters the import path for no benefit and widens the blast radius.

Proposed shape:

# uv binary — pinned by digest, matching the repo's action-pinning convention.
# Base image is arm64; the ghcr.io/astral-sh/uv image is multi-arch.
COPY --from=ghcr.io/astral-sh/uv:<version>@sha256:<digest> /uv /bin/uv

# Dependency manifests only, copied before src so this layer stays cached
# until dependencies actually change (same caching property as today's
# `COPY requirements.txt`).
COPY pyproject.toml uv.lock ${LAMBDA_TASK_ROOT}/

and, replacing the pip install line inside the existing RUN:

    cd "${LAMBDA_TASK_ROOT}" && \
    uv export --frozen --no-dev --no-emit-project \
      --format requirements-txt -o /tmp/requirements.lock.txt && \
    uv pip install --system --no-binary-package confluent-kafka \
      -r /tmp/requirements.lock.txt && \

Why export-then-install rather than uv sync: uv sync builds a .venv, which is not on the Lambda import path and would need PYTHONPATH fixing. Why export rather than uv pip install -r pyproject.toml: the latter re-resolves at build time and ignores uv.lock, throwing away the reproducibility this migration is for. --frozen makes the build fail loudly if uv.lock is stale relative to pyproject.toml — a desirable build gate.

Additional Docker checks:

  • Only the prod dependency group must be installed — --no-dev above. No pytest/moto/testcontainers in the shipped image.
  • --no-emit-project assumes src/ is COPY'd rather than installed as a package (as today). If [project] ends up declaring packages, set [tool.uv] package = false instead.
  • uv export emits hashes by default; confirm hash verification still passes for the confluent-kafka sdist given --no-binary-package.
  • Remove /bin/uv in the existing cleanup step so the build tool is not part of the shipped image (attack surface + AquaSec/Trivy findings).
  • Re-check the non-root USER 1000 block — chown -R must still cover everything uv writes.
  • Confirm the image still passes the AquaSec night scan.

4. CI

  • .github/actions/setup-dev-python-env/action.yml: swap actions/setup-python + pip install for astral-sh/setup-uv (SHA-pinned, enable-cache: true) + uv sync --group dev.
  • check_python.yml: change detection currently keys on requirements*.txt — must key on pyproject.toml and uv.lock instead, otherwise dependency-only PRs stop triggering the quality gates.
  • Decide whether tool invocations become uv run pylint ... / uv run pytest ... or whether the synced venv is activated once. Note uv sync creates .venv in the workspace, so the existing bare pylint/mypy/pytest calls will break unless the venv is put on PATH.

4b. Coordinate with the external deployment pipeline

The pipeline that builds and pushes this image lives in another repo and runs docker/build-push-action against this Dockerfile after injecting config, certs and SASL/SSL artifacts. It is the only thing that exercises the changed install path, and it does so after merge here.

  • Confirm with the pipeline owners: internal package index in use? (blocker b) Registry egress restrictions? (blocker c) Any step that reads requirements.txt directly — pre-fetching wheels, vulnerability scanning, an SBOM step, or an offline/air-gapped install stage? If so, deleting requirements*.txt (item 8) breaks it, and the cutover must be sequenced with a pipeline change rather than merged independently.
  • Agree a verification run: build the migrated image through the real pipeline, against the real certs and index, before this issue is closed. A local docker build on an unproxied machine does not exercise blockers (a) or (b) at all.
  • Note that docker/build-push-action layer caching keys off the COPY set; swapping requirements.txt for pyproject.toml + uv.lock invalidates the dependency layer once, which is expected (librdkafka recompiles).

Optionally, add a build-only smoke job to this repo's CI (path-filtered on Dockerfile / pyproject.toml / uv.lock, --platform=linux/arm64, generous timeout-minutes since librdkafka compiles from source). It would catch plain Dockerfile syntax/step regressions on the PR, but it will not reproduce the proxy or index conditions of the deployment environment, so it is a complement to the pipeline verification, not a substitute.

5. Makefile

  • Replace PYTHON := .venv/bin/python with uv run so it works on Windows too (.venv/bin/python does not exist there — it is .venv/Scripts/python.exe).

6. Dependabot

  • .github/dependabot.yml: package-ecosystem: "pip" → "uv". Verify the groups/allow: direct config carries over and that dependabot_auto.yml still matches the resulting PRs.

7. Docs

  • DEVELOPER.md "Set Up Python Environment" → uv sync --group dev.
  • .github/copilot-instructions.md dependency-layout paragraph.
  • README.md if it references requirements files.

8. Cleanup

  • Delete requirements.txt / requirements-dev.txt once nothing references them — including the external deployment pipeline (see 4b). If a consumer outside this repo needs them, generate on demand with uv export --format requirements-txt rather than keeping them hand-maintained, and sequence the deletion after the consumer is updated.
Open questions
  • Should the == pins in pyproject.toml be relaxed to >=/~= now that uv.lock provides exact reproducibility? Relaxing means Dependabot updates the lock rather than churning the manifest. Recommend a follow-up, not part of this migration.
  • Is uv.lock covered by the AquaSec/Trivy scanning currently in use, or does the scan need uv export output to see the dependency tree?
Acceptance criteria
  • uv sync --group dev produces a working dev environment from a clean clone.
  • make qa passes locally.
  • All jobs in check_python.yml pass, and are still triggered by a dependency-only change.
  • Docker image builds for linux/arm64 and confluent-kafka is compiled from source against the system librdkafka — verify GSSAPI is actually present in the built image, not just that the build succeeded.
  • Shipped image contains no dev dependencies and no uv binary.
  • The image has been built through the real deployment pipeline, with the real certs and package index — not only locally.
  • The deployment pipeline has been confirmed to have no remaining dependency on requirements*.txt.
  • Lambda still cold-starts and serves both handlers (src.event_gate_lambda.lambda_handler, event stats) from the rebuilt image.
  • uv.lock is committed and Dependabot opens PRs against it.
  • No references to requirements*.txt remain in the repo.
主要語言
Python
星號
4
分支
0
平均合併
22 小時 3 分鐘
30 天內合併 PR
8

環境準備

  • 提供 Dockerfile 或 Docker Compose 檔案
  • 有 Pull Request 範本
  • 沒有貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

AbsaOSS/EventGate 的其他 Issue

查看 AbsaOSS/EventGate 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。