Skip to content

ci(tox): migrate from pip to uv via tox-uv#6390

Open
sentry-junior[bot] wants to merge 27 commits into
masterfrom
chore/migrate-to-tox-uv
Open

ci(tox): migrate from pip to uv via tox-uv#6390
sentry-junior[bot] wants to merge 27 commits into
masterfrom
chore/migrate-to-tox-uv

Conversation

@sentry-junior
Copy link
Copy Markdown

@sentry-junior sentry-junior Bot commented May 22, 2026

Description

  • add uv and tox-uv to manage python envs and packages instead of pip
  • use astral-sh/setup-uv action in CI instead of setup-python, uv always uses python3.13
  • except for 3.6 and 3.7, all python versions now go through tox-uv
  • 3.6 and 3.7 have their own containers which uv picks up through UV_PYTHON_REFERENCE
  • the SDK is now installed as part of deps and not via package so uv resolves all deps in a single pass, this was necessary since uv resolution is stricter than pip
  • some other pins were necessary to make CI pass

TODO

  • pre-releases are currently handled with UV_PRERELEASE=if-necessary-or-explicit and explicit pinning of transitive deps, maybe it makes sense to split off pre-releases in a separate action where we can have UV_PRERELEASE=all
  • coverage (which is a mess anyway)
  • dev flow with uv as well - linters, top level tox, test file generation all managed with uv

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

Codecov Results 📊

2205 passed | ⏭️ 154 skipped | Total: 2359 | Pass Rate: 93.47% | Execution Time: 6m 9s

📊 Comparison with Base Branch

Metric Change
Total Tests
Passed Tests
Failed Tests
Skipped Tests

✨ No test changes detected

All tests are passing successfully.

✅ Patch coverage is 100.00%. Project has 13400 uncovered lines.
❌ Project coverage is 41.14%. Comparing base (base) to head (head).

Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    41.17%    41.14%    -0.03%
==========================================
  Files          190       190         —
  Lines        22751     22764       +13
  Branches      7778      7778         —
==========================================
+ Hits          9366      9364        -2
- Misses       13385     13400       +15
- Partials       507       507         —

Generated by Codecov Action

Comment thread scripts/populate_tox/tox.jinja Outdated
Comment thread .github/workflows/test-integrations-cloud.yml Outdated
Comment thread .github/workflows/test-integrations-common.yml
@sl0thentr0py sl0thentr0py force-pushed the chore/migrate-to-tox-uv branch 2 times, most recently from 35deb2d to a767133 Compare May 26, 2026 15:19
Comment thread .github/workflows/test-integrations-dbs.yml Outdated
Comment thread .github/workflows/test-integrations-tasks.yml
Comment thread scripts/populate_tox/tox.jinja Outdated
Comment thread Makefile
Comment thread .github/workflows/test-integrations-flags.yml Outdated
@sl0thentr0py sl0thentr0py force-pushed the chore/migrate-to-tox-uv branch from a3989a2 to 35675ae Compare May 26, 2026 16:55
Comment thread tox.ini Outdated
Comment thread scripts/populate_tox/tox.jinja Outdated
@sl0thentr0py sl0thentr0py force-pushed the chore/migrate-to-tox-uv branch 5 times, most recently from 33b62fa to 030fd1b Compare May 26, 2026 19:39
Comment thread .github/workflows/test-integrations-tasks.yml
Comment thread tests/integrations/celery/integration_tests/__init__.py
@sl0thentr0py sl0thentr0py force-pushed the chore/migrate-to-tox-uv branch 3 times, most recently from 7a229b6 to ccb07a7 Compare May 27, 2026 11:20
@sl0thentr0py sl0thentr0py changed the title build(tox): migrate from pip to uv via tox-uv build(tox): migrate from pip to uv via tox-uv for CI May 27, 2026
@sl0thentr0py sl0thentr0py changed the title build(tox): migrate from pip to uv via tox-uv for CI ci(tox): migrate from pip to uv via tox-uv May 27, 2026
sentry-junior Bot and others added 2 commits May 27, 2026 13:51
Replace pip-backed virtualenv environments with uv using the tox-uv plugin.

- [tox] requires: swap virtualenv<20.26.3 pin for tox-uv; the virtualenv
  pin existed solely to prevent pip 24.1 being seeded into envs, which is
  irrelevant once uv manages all installs
- setenv: remove py3.14t VIRTUALENV_PIP=24.1 (virtualenv-specific, no-op
  with tox-uv's uv venv)
- commands: remove bare 'pip install' workaround lines; tox-uv does not
  seed pip into venvs so these would fail
- deps: add flask v1 compat packages (itsdangerous, markupsafe, jinja2)
  as factor-conditional deps to replace the removed pip install commands;
  the urllib3<2.0.0 boto3 pin was already present in the auto-generated deps
- CI templates updated (test_group.jinja); run scripts/generate-test-files.sh
  to regenerate the .github/workflows/test-integrations-*.yml files

Test matrix (envlist, Python versions, deps) is unchanged.

Note: Python 3.6 container handling is deferred; see plan canvas for
the recommended approach of running tox under a modern Python host.

Co-authored-by: Neel Shah <neel.shah@sentry.io>

---
[View Session in Sentry](https://sentry.sentry.io/traces/?project=4510944073809921&query=gen_ai.conversation.id%3A%22slack%3AC02T4BB83AS%3A1779437966.628249%22)
sl0thentr0py and others added 24 commits May 27, 2026 13:51
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
actions/checkout sets safe.directory under a temporary HOME that is
discarded after the step, so subsequent steps see "dubious ownership"
and git fails. This makes get_default_release() return None and breaks
release/session-tracking tests on the 3.6/3.7 container jobs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
py3.7 celery in CI hangs in test_celery_beat_cron_monitoring::test_explanation.
kill_beat slept 1s then opened the pidfile; in the slower py3.7 container
startup, the file didn't exist yet, the thread died silently, and beat
ran forever (30-min job timeout).

Poll for the pidfile up to 30s before starting the kill timer, and dump
any future thread exception to stderr so the next failure surfaces a
traceback instead of silently hanging.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The pidfile-race fix did not unhang py3.7 celery in CI. pytest-timeout
only dumps the parent pytest process's threads — the parent is stuck in
pytest-forked's waitpid, so the actual hang is somewhere inside the
forked child, invisible.

Schedule faulthandler.dump_traceback_later(45) in run_beat (which runs
inside the forked child) and cancel it on successful beat shutdown. If
beat hangs in CI again, the child's full thread dump lands in the log
and tells us where to look.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous attempt put faulthandler.dump_traceback_later inside run_beat,
but the py3.7 CI hang turns out to happen earlier — start_worker() never
returns, so run_beat is never reached and the dump is never armed.

Move the diagnostic into an autouse fixture in
tests/integrations/celery/integration_tests/conftest.py so it covers the
entire test body. Next CI hang should land a thread dump in the log
showing where start_worker is wedged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
So the py3.7-container kombu hang reveals its real exception instead of
sleeping forever inside retry_over_time.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After moving py3.6/3.7 jobs into the python:X.Y container, the three beat
tests (test_explanation, test_beat_task_crons_success/error) hang in
celery's start_worker on py3.7 only — kombu's pre-connect via
default_channel never returns. This is a known kombu/redis-py + os.fork
interaction on the old pin (kombu 4.6 + redis-py <3.2) that celery 4.4.7
ships with; it worked previously only because the broker was on loopback,
not a bridged sibling container.

Bumping the existing < (3, 7) skip to < (3, 8). py3.7 is EOL and the same
tests run fine on 3.8+. Also dropping the diagnostic faulthandler
conftest and the kill_beat exception-printing wrapper now that the
root cause is understood; the pidfile-wait fix in kill_beat is kept since
it's a legitimate startup-race fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The workflow sets SENTRY_PYTHON_TEST_REDIS_HOST=redis for 3.6/3.7
container runs (since redis is a sibling service container, not on
loopback), but tox strips env vars not listed in passenv. The celery
beat tests therefore read the default 127.0.0.1, find nothing on
loopback inside the python container, and hang in kombu's
retry_over_time. Adding the var to passenv fixes it; un-skip the beat
tests on py3.7.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sl0thentr0py sl0thentr0py force-pushed the chore/migrate-to-tox-uv branch from 6b18651 to 2267687 Compare May 27, 2026 11:51
@sl0thentr0py sl0thentr0py marked this pull request as ready for review May 27, 2026 12:04
@sl0thentr0py sl0thentr0py requested a review from a team as a code owner May 27, 2026 12:04
"asyncpg": (0, 23),
"beam": (2, 12),
"boto3": (1, 12), # botocore
"boto3": (1, 17), # botocore
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did we increase our minimum version for boto3?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


  Look at uv's complaint carefully:
  - boto3 1.12.49 → botocore 1.15.49 → declares urllib3>=1.20,<1.26
  - sentry-sdk → declares urllib3>=1.26.11

  These are genuinely incompatible — no urllib3 version satisfies both.

  The pip-based freeze you pasted earlier shows urllib3==1.26.20 despite botocore's <1.26 cap. Old pip's legacy
  resolver was loose enough to install a version that technically violated botocore's declared constraint. boto3
  1.12 works fine with urllib3 1.26 in practice, so nobody noticed. uv's resolver is strict and correctly refuses
  to install an unsolvable combination.

uv resolves are stricter than pip, basically pip was being too loose in a configuration that would never work

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.

2 participants