Skip to content

ci: keep a uv cache per Python version and skip unused source builds - #251

Merged
bagowix merged 1 commit into
mainfrom
ci/per-python-uv-cache
Oct 16, 2026
Merged

bagowix merged 1 commit into
mainfrom
ci/per-python-uv-cache

Conversation

@bagowix

@bagowix bagowix commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

Summary

The tests are not what makes CI slow. pytest-xdist already runs them with
-n auto, in 17–24 s per job. The time goes to uv sync on the newest
interpreters:

Job Total uv sync Tests
quality 3.11–3.14 ~1 min 1–3 s ~18 s
quality 3.14t 3.6 min 159 s 17 s
quality 3.15 4.3 min 195 s 18 s
quality 3.15t 6.3 min 315 s 17 s

(run 38958788451). Several dev dependencies ship no wheels for 3.14t, 3.15 or
3.15t, so uv builds them from source: lupa, zensical, pydantic-core, msgspec,
cryptography, aiohttp, pyyaml, nh3. On 3.15t lupa takes 5:03 and zensical 5:09.

Those builds were never cached. setup-uv's default key carries the
runner's system Python and the lock hash
(setup-uv-2-…-ubuntu-24.04-3.12.3-<hash>), not UV_PYTHON. All seven
quality jobs therefore shared one entry. Whichever finished first saved it,
and the 3.15 job logs Cache hit occurred on key …, not saving cache, so its
built wheels were discarded every run.

  • cache-suffix: ${{ matrix.python-version }} on setup-uv in quality
    and platform-smoke. Each interpreter gets its own entry, so once main
    has saved one, runs against the same uv.lock restore the built wheels
    instead of rebuilding them.
  • quality skips lupa and zensical (--no-install-package), the two
    slowest builds there. Neither is used in that job: Redis tests run against
    the service container (INTERLOCK_TEST_REDIS_URL), so fakeredis and its
    Lua engine are never imported, and the Docs job builds the docs. This also
    shortens the runs that miss the cache, which happen once per uv.lock
    change.
  • UV_NO_SYNC: '1' at job level, so the later uv run steps do not sync
    the skipped packages back. A plain uv run would reinstall them.
    platform-smoke handles the same problem with --no-sync.

No package or test changes, and no changelog entry: like #212, this is
CI-internal.

Cache budget: the repository already sits at the 10 GB Actions cache cap
(146 entries), with LRU eviction. main's per-version entries are reused on
every run and are the last to be evicted. If eviction starts hitting them,
the next lever is prune-cache: true, which keeps only source-built wheels.

Checklist

  • Tests added or updated (suite stays at 100% coverage): CI only, suite unchanged
  • uv run ruff format --check and uv run ruff check pass
  • uv run mypy, uv run pyright and uv run pyrefly check pass
  • Docs updated (docs/) for user-facing changes: none user-facing
  • CHANGELOG.md [Unreleased] updated: not needed, CI-internal
  • Commits follow Conventional Commits

Verified locally by replaying the quality job on 3.15t and 3.11 against a
real Redis 7: UV_NO_SYNC=1 with the new install flags, then zizmor, ruff,
mypy, pyright, pyrefly and pytest --cov. Result: 853 passed and 100%
coverage on both, with lupa and zensical absent after every step. Without
UV_NO_SYNC, uv run reinstalls lupa. zizmor (online) and prek pass.

The first run of this branch misses by design, since the keys are new. The
effect shows on the next run against the same lock.

Related issues

None.

Changed

  • CI: Cache uv dependencies separately for each Python version in the quality and platform-smoke jobs.
  • CI: Skip installing lupa and zensical in quality, and prevent later uv run commands from syncing them back in.
  • CI: Skip installing lupa in platform-smoke and run its tests with --no-sync.

Added

  • None.

Fixed

  • Prevent Python matrix jobs from sharing a uv cache entry keyed without the selected Python version.

setup-uv's default cache key carries the runner's system Python, not
UV_PYTHON, so the whole quality matrix shared one entry. The first job
to finish saved it, and the wheels 3.14t, 3.15 and 3.15t build from
source were never kept: `uv sync` took 2.5-5 minutes on those jobs in
every run. A cache-suffix per matrix version fixes that for quality and
platform-smoke.

The quality job also stops installing lupa and zensical, the slowest
source builds there, which it never imports (Redis tests use the
service container, the Docs job builds the docs). UV_NO_SYNC keeps the
later `uv run` steps from syncing them back.
@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: bagowix/interlock/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ef993379-a97e-426f-9480-db5105fad729
📥 Commits

Reviewing files that changed from the base of the PR and between 8a8d881 and d774291.

📒 Files selected for processing: 1
  • .github/workflows/ci.yml

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout: 19
  • GitHub Check: Platform smoke (windows-latest, Python 3.15)
  • GitHub Check: quality (3.14t)
  • GitHub Check: quality (3.15)
  • GitHub Check: quality (3.15t)
  • GitHub Check: quality (3.13)
  • GitHub Check: griffe
  • GitHub Check: quality (3.12)
  • GitHub Check: Packaging smoke test
  • GitHub Check: quality (3.14)
  • GitHub Check: Platform smoke (macos-latest, Python 3.11)
  • GitHub Check: Platform smoke (macos-latest, Python 3.15)
  • GitHub Check: Coverage
  • GitHub Check: Docs
  • GitHub Check: quality (3.11)
  • GitHub Check: Platform smoke (windows-latest, Python 3.11)
  • GitHub Check: Extras (minimum versions)
  • GitHub Check: Run benchmarks
  • GitHub Check: Analyze (actions)
  • GitHub Check: Analyze (python)
🧰 Additional context used
📚 Code guidelines: 1
CONTRIBUTING.md — configured
📓 Path-based instructions: 2
Actions must be pinned to a full commit SHA with the version in a trailing comment, `permissions:` must be least-privilege and declared per job, and untrusted input must never be interpolated into `run:`.

⚙️ CodeRabbit configuration file

Files:

  • .github/workflows/ci.yml
Applicability reference: AGENTS.md: # AGENTS.md Mutants: `uv run mutmut run` (optional, out of band — see `CONTRIBUTING.md`) Source excerpt: Every action in `.github/workflows/` is pinned to a commit SHA with the version in a trailing comme...

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Files:

  • .github/workflows/ci.yml

Walkthrough

The quality job sets UV_NO_SYNC, excludes lupa and zensical from dependency installation, and keys its uv cache by Python version. The platform-smoke job also keys its uv cache by Python version.

Changes

CI setup

Layer / File(s) Summary
Dependency installation and cache keys
.github/workflows/ci.yml
The quality job sets UV_NO_SYNC and skips installing lupa and zensical. The quality and platform-smoke jobs include the Python version in their uv cache keys.

Priority: ⬇️ Low

Estimated code review effort: 1 (Trivial) | ~5 minutes

Change: Other

Suggested labels: github_actions

Merge Risk: ⚪ Minimal · up to d7742

The quality job retains dependency installation for its checks, and the excluded packages are not used on that path. No actionable merge-blocking risk is established; the workflow change is ready for normal merge checks.

Pre-merge checks: Passed 9
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title uses the required ci: <summary> format, stays under 72 characters, uses lowercase imperative wording, and accurately describes the cache-key and source-build changes.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Zero-Dependency Core Passed The PR changes only .github/workflows/ci.yml; no file under interlock/ or pyproject.toml changes. pyproject.toml has dependencies = [] in both base and head. interlock/__init__.py is uncha…
Changelog Entry Passed The PR changes only .github/workflows/ci.yml. .github/ is explicitly exempt from the changelog requirement.
Docs And Llm Mirror Passed The PR changes only .github/workflows/ci.yml. It changes CI cache keys and dependency installation for CI jobs. It does not change interlock/, an integration, or a user-facing configuration option…
Tests Accompany Behaviour Change Passed The pull request changes only .github/workflows/ci.yml. It changes no .py file under interlock/, so the test-change requirement does not apply.
Public Api Surface Passed No public API surface changed. The reviewed diff changes only .github/workflows/ci.yml; interlock/__init__.py and interlock/pipeline.py have no removals or signature changes.
Full details: Zero-Dependency Core

Explanation

The PR changes only .github/workflows/ci.yml; no file under interlock/ or pyproject.toml changes. pyproject.toml has dependencies = [] in both base and head. interlock/__init__.py is unchanged and imports only interlock.* modules, not interlock.integrations.

Full details: Docs And Llm Mirror

Explanation

The PR changes only .github/workflows/ci.yml. It changes CI cache keys and dependency installation for CI jobs. It does not change interlock/, an integration, or a user-facing configuration option. No documentation update is required.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codspeed

codspeed Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

⚠️ 7 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

✅ 28 untouched benchmarks


Comparing ci/per-python-uv-cache (d774291) with main (8a8d881)

Open in CodSpeed

@codecov

codecov Bot commented Oct 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@bagowix
bagowix merged commit 8d1bea6 into main Oct 16, 2026
39 checks passed
@bagowix
bagowix deleted the ci/per-python-uv-cache branch October 16, 2026 21:07
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