Skip to content

Document the two prerequisites for a uv git dependency in the service template #4

Description

@topij

Problem

Adding a git-sourced dependency (pkg @ git+https://…) to a uv + hatchling + python:3.12-slim service has two non-obvious prerequisites. Both fail late, both have confusing error surfaces, and neither is mentioned in the kit's service scaffolding.

Surfaced while extracting a shared library across three services — it cost a failed build in all three repos, in the same two ways each time. That repetition is why this is a template change rather than three tickets.

1. python:3.12-slim has no git

uv shells out to the git binary to resolve git dependencies. The slim base image ships no VCS tooling, so uv sync --frozen fails inside the container while working fine on the dev machine.

Fix that worked — a multi-stage build, which also drops uv and the apt cache from the shipped image:

FROM python:3.12-slim AS builder
COPY --from=ghcr.io/astral-sh/uv:0.9.28 /uv /bin/uv
RUN apt-get update \
    && apt-get install -y --no-install-recommends git \
    && rm -rf /var/lib/apt/lists/*
# ... uv sync --frozen --no-dev ...

FROM python:3.12-slim
COPY --from=builder /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "yourpkg"]

2. hatchling rejects direct URL references by default

ValueError: Dependency #3 of field `project.dependencies` cannot be a direct
reference unless field `tool.hatch.metadata.allow-direct-references` is set to `true`

The confusing part: uv lock succeeds first, so it reads as a resolution problem when it's a build-backend policy. Needs:

[tool.hatch.metadata]
allow-direct-references = true

Why it matters

Neither is discoverable from the error message alone, and #1 in particular fails only in the container — so without a CI image build it surfaces on the deploy host, which is the worst possible place.

Proposed fix

  • Add both to the kit's service template / scaffolding docs, gated on "if you use a git dependency".
  • Ship the multi-stage Dockerfile as the default template rather than single-stage — it's better regardless (no uv, no apt cache, no git in the runtime image).
  • Add a CI docker job to the template that builds the image and imports the app. That's what turns this class of failure from a deploy-host surprise into a red PR check:
docker:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - run: docker build -t app:ci .
    - run: docker run --rm app:ci python -c "import yourpkg; print('ok')"

Acceptance

  • Service template documents both prerequisites
  • Template Dockerfile is multi-stage
  • Template CI includes an image-build + import job

Source: docs/friction-log.md 2026-07-23 (topij/brain), entries 1 and 2 — one pattern, observed across three repos.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Priority 3 — backlogdocumentationImprovements or additions to documentationenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions