Migrate dependency management from pip to uv
Los mantenedores suelen responder en 2 días
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 48/100
- Tipo de issue
- Refactorización
- Claridad
- Bastante claro
- Estado de actividad
- Tranquilo
- Stack tecnológico
- docker, github-actions, python
- Área
- build-system, ci-cd, tooling
Línea de trabajo
Empieza por pyproject.toml, requirements.txt y requirements-dev.txt para trasladar los pins directos existentes y las dependencias de desarrollo a manifiestos gestionados por UV antes de tocar los archivos de despliegue. Después inspecciona Dockerfile en el paso actual de pip install, .github/actions/setup-dev-python-env/action.yml y .github/workflows/check_python.yml, porque controlan el flujo de instalación y el comportamiento de los triggers. Después de los cambios, verifica DEVELOPER.md, Makefile, .github/dependabot.yml y las actualizaciones de la documentación; luego ejecuta uv sync --group dev, make qa y el workflow de Python. El trabajo está terminado cuando los cambios que solo afectan a dependencias siguen activando las rutas de CI, la imagen de producción instala cero dependencias de desarrollo sin incluir /bin/uv, y la compilación de la pipeline externa tiene éxito con condiciones reales de certificados/índice.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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 inrequirements*.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", anddependenciesmigrated fromrequirements.txt(keep the exact==pins on the first pass so the migration is a no-op dependency-wise). - Add
[dependency-groups] devwith the contents ofrequirements-dev.txt. - Decide how to handle the
psycopg2(prod) vspsycopg2-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-versionfile 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:
--no-binary confluent-kafkais load-bearing. It forces a source build against the systemlibrdkafkacompiled 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.- Deps currently land in the base image's system
site-packages(plainpip install, no--target), which is already on the Lambda import path. Keep that placement —uv pip install --systemis 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-devabove. Nopytest/moto/testcontainersin the shipped image. --no-emit-projectassumessrc/isCOPY'd rather than installed as a package (as today). If[project]ends up declaring packages, set[tool.uv] package = falseinstead.uv exportemits hashes by default; confirm hash verification still passes for theconfluent-kafkasdist given--no-binary-package.- Remove
/bin/uvin 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 1000block —chown -Rmust still cover everything uv writes. - Confirm the image still passes the AquaSec night scan.
4. CI
.github/actions/setup-dev-python-env/action.yml: swapactions/setup-python+pip installforastral-sh/setup-uv(SHA-pinned,enable-cache: true) +uv sync --group dev.check_python.yml: change detection currently keys onrequirements*.txt— must key onpyproject.tomlanduv.lockinstead, 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. Noteuv synccreates.venvin the workspace, so the existing barepylint/mypy/pytestcalls will break unless the venv is put onPATH.
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.txtdirectly — pre-fetching wheels, vulnerability scanning, an SBOM step, or an offline/air-gapped install stage? If so, deletingrequirements*.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 buildon an unproxied machine does not exercise blockers (a) or (b) at all. - Note that
docker/build-push-actionlayer caching keys off theCOPYset; swappingrequirements.txtforpyproject.toml+uv.lockinvalidates 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/pythonwithuv runso it works on Windows too (.venv/bin/pythondoes not exist there — it is.venv/Scripts/python.exe).
6. Dependabot
.github/dependabot.yml:package-ecosystem: "pip"→"uv". Verify thegroups/allow: directconfig carries over and thatdependabot_auto.ymlstill matches the resulting PRs.
7. Docs
DEVELOPER.md"Set Up Python Environment" →uv sync --group dev..github/copilot-instructions.mddependency-layout paragraph.README.mdif it references requirements files.
8. Cleanup
- Delete
requirements.txt/requirements-dev.txtonce nothing references them — including the external deployment pipeline (see 4b). If a consumer outside this repo needs them, generate on demand withuv export --format requirements-txtrather than keeping them hand-maintained, and sequence the deletion after the consumer is updated.
Open questions
- Should the
==pins inpyproject.tomlbe relaxed to>=/~=now thatuv.lockprovides exact reproducibility? Relaxing means Dependabot updates the lock rather than churning the manifest. Recommend a follow-up, not part of this migration. - Is
uv.lockcovered by the AquaSec/Trivy scanning currently in use, or does the scan needuv exportoutput to see the dependency tree?
Acceptance criteria
-
uv sync --group devproduces a working dev environment from a clean clone. -
make qapasses locally. - All jobs in
check_python.ymlpass, and are still triggered by a dependency-only change. - Docker image builds for
linux/arm64andconfluent-kafkais compiled from source against the systemlibrdkafka— verify GSSAPI is actually present in the built image, not just that the build succeeded. - Shipped image contains no dev dependencies and no
uvbinary. - 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.lockis committed and Dependabot opens PRs against it. - No references to
requirements*.txtremain in the repo.
- Lenguaje dominante
- Python
- Estrellas
- 4
- Forks
- 0
- Merge medio
- 3 d 7 h
- PR fusionados (30 d)
- 11
Preparar el entorno
- Incluye un Dockerfile o un archivo de Docker Compose
- Tiene una plantilla de pull request
- Sin guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de AbsaOSS/EventGate
-
refactoring type:tech-debt
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
Los mantenedores suelen responder en 2 días
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
Los mantenedores suelen responder en 2 días
-
bug
Dificultad 3/5 1-2 días Aptitud para principiantes 64/100
Los mantenedores suelen responder en 2 días
-
Make Writes IdempotentAbiertoenhancement
Dificultad 5/5 Más de una semana Aptitud para principiantes 32/100
Los mantenedores suelen responder en 2 días
-
infrastructure type:tech-debt
Dificultad 3/5 1-2 días Aptitud para principiantes 70/100
Los mantenedores suelen responder en 2 días
Todos los issues de AbsaOSS/EventGate
Issues similares
-
Action calls retired claude-3-5-haiku-20241022, generating failing API requests for every userAbierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 91/100
-
Dificultad 1/5 1-3 horas Aptitud para principiantes 92/100
-
enhancement P2
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Toloka/tolokaforge#1776 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
TencentCloud/Octop#1622 ·
Los mantenedores suelen responder en 1 día
-
[arch] gate_sync.sh: stale remote tmp files on install failure; overlapping cron runs unguardedAbiertoarch area:fleet priority:p3 severity:low track:hosted-product
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día