Skip to content

ci: migrate reusable Python workflows from pip to uv #381

@Faerkeren

Description

@Faerkeren

Severity

LOW

Area

packaging / ci

Problem

The four reusable Python workflows (python-lint.yml, python-typecheck.yml, python-ci.yml, python-tests.yml) use pip install -e "<install-spec>" for dependency setup. The project itself is managed with uv and ships a uv.lock, so CI does not install the locked dependency set — it resolves fresh from PyPI on every run against >= constraints in pyproject.toml.

This means:

  • CI can drift from local developer environments and from each previous CI run.
  • uv.lock is authoritative locally but ignored by CI.
  • Cold-cache CI installs are slower than uv sync would be.
  • Each workflow re-implements pip cache + install logic instead of using a single astral-sh/setup-uv action.

This was raised as the broader scope option during the dev-tooling cleanup (#380); that PR took the minimal path (drop ad-hoc pip install ruff/mypy, declare them in pyproject.toml) and explicitly deferred the full migration to here.

Evidence

  • .github/workflows/python-lint.ymlpython -m pip install -e "${{ inputs.install-spec }}"
  • .github/workflows/python-typecheck.yml — same
  • .github/workflows/python-ci.yml — same (build job)
  • .github/workflows/python-tests.yml — same (matrix test job)
  • uv.lock exists at repo root and is the source of truth for local uv sync.

Impact

  • Reproducibility: CI green != local green is a real risk when an upstream releases a breaking patch.
  • Speed: uv sync with a warm cache is significantly faster than pip install; cold installs are also faster.
  • Maintainability: Four workflows duplicate the same setup pattern.
  • Consistency: AGENTS.md tells contributors to use uv sync / uv run; CI should follow.

Proposed Direction

Migrate the four reusable workflows to use uv end-to-end:

  1. Replace actions/setup-python + pip install with astral-sh/setup-uv@v6 + uv sync --extra <group>.
  2. Replace python -m <tool> invocations with uv run <tool>.
  3. Keep the install-spec input but interpret it as the extras list passed to uv sync (e.g. dev, dev,test). Alternatively introduce a new extras input and deprecate install-spec.
  4. Update ci.yml callers to pass the new shape.

Out of scope:

  • Removing the reusable-workflow pattern (they should remain consumable from other repos).
  • Changing the test matrix (3.11, 3.12, 3.13).
  • Touching python-release.yml or python-gitleaks.yml (not pip-based in the same way).

Acceptance Criteria

  • python-lint.yml uses astral-sh/setup-uv + uv sync + uv run ruff check ...
  • python-typecheck.yml uses astral-sh/setup-uv + uv sync + uv run mypy ...
  • python-ci.yml (build) uses uv build (or uv run python -m build)
  • python-tests.yml matrix uses astral-sh/setup-uv + uv sync + uv run pytest ...
  • ci.yml callers updated; existing vars.CI_PYTHON_* overrides preserved or cleanly renamed
  • CI runtime for a cold cache is equal-or-better than before (sanity check, not a hard gate)
  • No regressions: lint, typecheck, all 1983 tests still pass at 95%+ coverage across 3.11/3.12/3.13
  • uv.lock is the dependency source on CI (locked installs, no resolution drift)
  • AGENTS.md "Commands" section reviewed to confirm it matches what CI now runs

Dependencies

Suggested Labels

  • severity:low
  • area:packaging
  • ci
  • type:refactor
  • enhancement
  • python:uv

Notes

This is intentionally scoped as a single coordinated change rather than per-workflow PRs, because the install-spec input shape change ripples through ci.yml. Doing it once keeps the diff coherent.

Estimated Refactor Risk

Low — CI-only change, no runtime code touched. Risk is "CI is broken until fixed" not "library is broken".

Epic

I: Packaging & Docs

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:packagingPackaging / project structureciContinuous integrationenhancementNew feature or requestepic:packagingEpic I: Packaging & Docspython:uvPull requests that update python:uv codeseverity:lowLow severity findingtype:refactorRefactor

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions