Skip to content

Migrate packaging, dependencies and CI to uv - #127

Draft
zeevmoney wants to merge 1 commit into
per-16176/cve-gates-and-fixesfrom
per-16221/uv-migration
Draft

zeevmoney wants to merge 1 commit into
per-16176/cve-gates-and-fixesfrom
per-16221/uv-migration

Conversation

@zeevmoney

@zeevmoney zeevmoney commented Sep 23, 2026 •

Copy link
Copy Markdown

Linear issue

PER-16221. Stacked on #126 (PER-16176); retarget to main once #126 merges.

Why

The SDK still packaged with setup.py and requirements*.txt, installed with plain pip in CI and had no lockfile, so every CI run resolved a different tree. This is a pure migration: no SDK code changes and no tool upgrades. PER-16222 is the tooling-upgrade follow-up, stacked on this PR.

What changed

  • pyproject.toml: PEP 621 [project] metadata carries everything setup.py declared. It builds with uv_build, and the runtime ranges are unchanged.
  • Dependency groups: dev tools are in a PEP 735 dev group, pinned exactly at the versions CI installed before. ruff and mypy match the pre-commit hook revs, which are what actually lint. The two pydantic lanes are conflicting groups (pydantic-v1, pydantic-v2), so both resolutions are locked in uv.lock.
  • uv version: pinned once, by [tool.uv] required-version = "==0.12.18". setup-uv reads the same pin, and so does the uv-lock hook rev. exclude-newer = "7 days" keeps a manual uv lock from picking up just-published packages.
  • .python-version: 3.11, matching CI, so local runs use the same Python.
  • Deleted: setup.py, requirements.txt, requirements-dev.txt, MANIFEST.in, pytest.ini and the Makefile. pytest config moves to pyproject's [tool.pytest].
    • The Makefile's generate-models would regenerate models.py with whatever generator was installed, and silently drop the file's hand-written pydantic v1/v2 compatibility header.
    • CONTRIBUTING.md now documents a pinned, reproducible regeneration instead.
  • CONTRIBUTING.md: new, with setup, offline vs e2e tests, both pydantic lanes, building and model regeneration. These dev instructions were kept out of the README, which is also the PyPI page. The README image now uses an absolute URL so it renders on PyPI.
  • CI:
    • test.yml runs uv sync --locked --group <lane>, then a step that fails unless the installed pydantic major matches the lane. Job names are unchanged, so the required checks still match.
    • pre-commit.yml runs pre-commit from the lock, with a Python-aware hook cache.
    • security.yml and audit-deps.sh still build the same three trees, now compiled from pyproject.toml with --no-sources. The dev tree now fails if the dev group didn't resolve.
    • python-sdk-publish.yml stamps the version with uv version --frozen from the validated tag, then builds with uv build --no-sources on a checksum-verified uv. The build → scan → publish gating and the PYPI_TOKEN auth are unchanged.
  • Dependabot and hooks: Dependabot moves to the uv ecosystem, keeping the cooldowns, groups and versioning-strategy: increase. A uv-lock pre-commit hook stops pyproject.toml and uv.lock from drifting apart.

Published package

  • Wheel: the permit/ files are identical to the setuptools build, and there is still no tests package.
  • METADATA: Requires-Dist, Requires-Python and the classifiers are identical. The differences:
    • the license is an SPDX expression (License-Expression: Apache-2.0, PEP 639) plus License-File;
    • Author-email now includes the author's name;
    • new Project-URL entries for Homepage, Documentation and Repository.
  • sdist: no longer ships tests/, setup.py or requirements.txt. uv_build always adds a pyproject.toml.orig next to the pyproject.toml it rewrites.

Architectural changes

No architectural change.

How it was tested

  • uv lock --check passes, and uv sync --locked works on both lanes.
  • The offline suite (7 test files) passes on the pydantic-v1 lane (1.10.26) and on the pydantic-v2 lane (2.13.5). The audit script tests (46) pass. The e2e tests need a PDP and keys, as before.
  • bash .github/scripts/audit-deps.sh with Trivy: runtime-ceiling 18, runtime-floor 15 and dev-ceiling 43 packages, 0 vulnerabilities. The floor tree resolves aiohttp==3.14.3, pydantic==1.10.13, loguru==0.7.0 and typing-extensions==4.5.0. The dev-group guard fails (exit 1) when pytest is missing.
  • uv build --no-sources: wheel and sdist contents and METADATA were diffed against the pre-migration setuptools build; see above.
  • actionlint, zizmor (0 findings), shellcheck and prek run --all-files, including uv-lock.
  • Reviewed by four specialised agents (CI and publishing, security and supply chain, packaging, general). Their findings were fixed, deferred to PER-16222, or rejected as invalid.

Manual test plan

  1. Install uv 0.12.18 (uv self update 0.12.18), then run uv sync. Expect pydantic 2.x.
  2. uv sync --group pydantic-v1 && uv run python -c "import pydantic; print(pydantic.VERSION)". Expect 1.10.x.
  3. uv run pytest tests/test_offline_regressions.py. Expect all to pass.
  4. uv build, then check that the wheel contains only permit/ and dist-info.
  5. On this PR's CI, both pytest (Pydantic …) jobs should print the pydantic major they asserted.

Blast radius and isolation

  • Blast radius: the build, CI, the release pipeline and Dependabot. SDK code is untouched.
  • Consumers: metadata changes as listed above, and the dependency ranges are unchanged.
  • Local uv: contributors must use uv 0.12.18. An older uv can't parse the [tool.uv] table: it warns and ignores the whole table, including the version pin and the cooldown. CI's uv-lock hook catches the resulting lock drift.
  • Isolation: isolated.

Scope and size

  • Production/config lines: about 375 added and 194 removed, excluding uv.lock.
  • Test lines: 0 (no runtime code changed; covered by the existing suites and the audit script tests).
  • Single responsibility: yes.

Deferred to PER-16222

  • Run the pre-commit hooks from the locked tools and upgrade ruff/mypy with strict rules.
  • Pin hook revs by SHA and add a Dependabot pre-commit ecosystem entry.
  • Add py.typed and a Python 3.14 lane.

🤖 Generated with Claude Code

pyproject.toml now carries the PEP 621 metadata setup.py declared, built
with uv_build; dev tools move to a PEP 735 group and both pydantic lanes
become conflicting groups, so every CI lane installs from the committed
uv.lock. setup.py, requirements*.txt, MANIFEST.in, pytest.ini and the
Makefile are gone; contributor docs move to CONTRIBUTING.md.

CI installs with uv sync --locked; the publish job stamps the version
with uv version, builds with uv build --no-sources on a checksum-verified
uv, and keeps its build -> scan -> publish gating and PyPI token auth.
The audit compiles its three trees from pyproject.toml with --no-sources
and fails if the dev group did not resolve. uv is pinned once, by
[tool.uv] required-version, with a 7-day exclude-newer cooldown;
Dependabot uses the uv ecosystem and a uv-lock hook stops drift.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F6b4ERDYYZ8NRTv1zJYxx2
@linear-code

linear-code Bot commented Sep 23, 2026

Copy link
Copy Markdown

PER-16221

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant