Skip to content

Use uv as hatch installer for faster test environment setup#23497

Merged
AAraKKe merged 10 commits into
masterfrom
aarakke/hatch-uv-installer
Apr 29, 2026
Merged

Use uv as hatch installer for faster test environment setup#23497
AAraKKe merged 10 commits into
masterfrom
aarakke/hatch-uv-installer

Conversation

@AAraKKe

@AAraKKe AAraKKe commented Apr 28, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Switches the ddev hatch plugin to use uv as the installer for every hatch environment it collects (default, test, e2e, benchmark, latest, lint), and migrates the CI shared-dependency cache from pip's wheel cache to uv's wheel cache.

Motivation

ddev test is slow because pip spends a lot of time on metadata resolution even with wheels cached. Swapping pip for uv as the hatch installer makes environment setup substantially faster, both locally and in CI. uv was already partially used in the repo (resolve-build-deps.yaml, .gitlab/tagger/Dockerfile), so this rounds it out for the test path.

Core changes

  • ddev/src/ddev/plugin/external/hatch/environment_collector.py
    • pip_install_commanduv_install_command (returns uv pip install)
    • finalize_config sets installer = 'uv' on every collected env (setdefault, so individual integrations can override back to pip if they ever need to)
    • get_initial_config sets the same on the lint env
    • pip added to lint env and test/e2e env deps (uv-managed venvs don't seed pip; mypy --install-types and a couple of in-env scripts still need it)
  • ddev/pyproject.toml: bump hatch>=1.8.1hatch>=1.13.0 (first hatch version with the uv installer)
  • datadog_checks_dev/pyproject.toml: bump virtualenv<20.22.0virtualenv>=20.26.1 in the [cli] extras. The old upper bound was added in 2023 to keep Py2 testing alive; core no longer tests Py2 and the cli extras already pull Py3-only tools, so the cap was dead weight blocking modern hatch.
  • Four hatch.toml files migrated from python -m pip install to uv pip install in pre/post-install commands: ddev/hatch.toml, datadog_checks_dev/hatch.toml, root hatch.toml (docs env), kafka_consumer/hatch.toml.
  • CI workflows now install uv via astral-sh/setup-uv and cache ~/.cache/uv:
    • .github/workflows/cache-shared-deps.yml
    • .github/workflows/test-target.yml
    • .github/workflows/test-fips-e2e.yml
    • .github/workflows/docs.yml
    • .github/workflows/build-ddev.yml
  • Cache key prefix bumped v01-python-...v02-uv-... so the new cache populates cleanly without colliding with the old pip cache.

No per-integration hatch.toml files needed touching beyond those four — the plugin injects installer = 'uv' into all envs collected via [env.collectors.datadog-checks].

Cache layout: why both uv AND pip paths are cached

The cache-shared-deps, test-target, test-fips-e2e workflows all cache two directories under a single key:

path: |
  ${{ runner.os == 'Windows' && '~\AppData\Local\uv\cache' || runner.os == 'macOS' && '~/Library/Caches/uv' || '~/.cache/uv' }}
  ${{ runner.os == 'Windows' && '~\AppData\Local\pip\Cache' || runner.os == 'macOS' && '~/Library/Caches/pip' || '~/.cache/pip' }}

Each line is a per-platform path (Linux / macOS / Windows) for one of the two wheel caches. Both directories are saved and restored together under one cache entry.

Why both:

  • uv cache (~/.cache/uv) is what hatch envs and the system-level uv pip install --system write to. This is the cache that drives the speedup.
  • pip cache (~/.cache/pip) is still used by the Python 2.7 branch in cache-shared-deps.yml (uv requires Py3.8+, so the Py2 step has to stay on pip). Without caching this path, the Py2 install step would re-download every wheel each run.

Caching both under one key with one actions/cache step keeps the workflow simple — single restore, single save, both ecosystems covered.

Snowflakes handled along the way

Switching the installer surfaced several second-order issues unrelated to uv itself but exposed by it. Documenting them so future debugging starts with context.

1. Windows venv cleanup blocked by uv hardlinks (_zstd.pyd lock)

Symptom: Windows minimum-base-package jobs failed during post-test cleanup with PermissionError: [WinError 5] Access is denied on backports/zstd/_zstd.cp313-win_amd64.pyd.

Cause: uv defaults to hardlink mode on Windows. When hatch>=1.13 (whose transitive backports-zstd ships a .pyd) is installed into a uv-managed venv, the .pyd is a hardlink to the uv cache. hatch env remove (invoked at the end of ddev test --compat, used by the minimum-base-package matrix) then can't shutil.rmtree the venv because the loaded extension keeps the file open, and Windows refuses to delete any hardlink to an open file. Whether this manifests on a given run depends on Defender activity / shell / Windows build — see the astral-sh/uv#7918 discussion.

Fix: Set UV_LINK_MODE=copy only on Windows minimum-base-package jobs in .github/workflows/test-target.yml. Regular Windows test jobs never run hatch env remove, so they keep the hardlink fast path. Refs: uv link-mode setting.

2. datadog-checks-tests-helper distribution name

Symptom: tests/cli/release/test_get_datadog_wheels.py::test_get_datadog_wheels failed in the ddev test job with the helper package now reported as datadog_checks_tests_helper (underscores) instead of datadog-checks-tests-helper (hyphens).

Cause: datadog_checks_tests_helper/setup.py had name='datadog_checks_tests_helper'. The legacy python setup.py bdist_wheel invocation that older pip+setuptools defaulted to silently normalized this to hyphens. Modern pip+setuptools (and uv) use the PEP 517 setuptools.build_meta:__legacy__ backend, which writes the literal name= value verbatim into METADATA. The virtualenv<20.22.0 bump alone (which brings modern pip+setuptools) would have broken this test even without the uv switch.

Fix: Change name='datadog_checks_tests_helper'name='datadog-checks-tests-helper' in datadog_checks_tests_helper/setup.py. The package is now distributed under the same canonical hyphenated name on PyPI conventions.

3. SAP HANA application_name in audit logs

Symptom: sap_hana/tests/test_integration.py::test_check failed with the audit log carrying application_name='python3' while the test asserted 'python'.

Cause: The application_name field that the SAP HANA server records is the connecting client's process name (argv[0]//proc/self/comm on Linux). With installer = "uv" set on hatch envs, hatch ≥1.13 creates the venv via uv venv, whose canonical interpreter symlink is python3; the previous virtualenv-created venvs used python. So pytest is now launched as python3 and HANA records python3.

Fix: Update the three assertions in sap_hana/tests/test_integration.py from 'application_name': 'python' to 'application_name': 'python3' to reflect the new (and now stable) venv layout.

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Add the qa/skip-qa label if the PR doesn't need to be tested during QA.
  • If you need to backport this PR to another branch, you can add the backport/<branch-name> label to the PR and it will automatically open a backport PR once this one is merged

- ddev hatch plugin: set installer = 'uv' on every collected env (default,
  test, e2e, benchmark, latest, lint) and switch _dd-install-packages
  scripts to 'uv pip install'.
- Bump min hatch dependency to >=1.13 (first version with the uv installer).
- CI: add astral-sh/setup-uv and migrate the shared dependency cache from
  ~/.cache/pip to ~/.cache/uv (plus pip path for the Py2 branch). Workflows
  updated: cache-shared-deps, test-target, test-fips-e2e, docs, build-ddev.
@AAraKKe AAraKKe added the qa/skip-qa Automatically skip this PR for the next QA label Apr 28, 2026
Modern hatch (1.10+, where the uv installer lives) requires
virtualenv>=20.26.1, but datadog_checks_dev[cli] was capping it at
<20.22.0 — a constraint added in 2023 to keep Py2 testing alive. Core
no longer tests Py2, and the [cli] extras already pull tools that don't
support Py2 anyway, so the upper bound was dead weight.

Also adds changelog entries for both packages.
uv-managed venvs don't seed pip the way virtualenv does. This breaks two
classes of in-env consumers:

- mypy --install-types in lint envs (auto-installs missing type stubs)
- python -m pip subprocess calls in tests / hatch.toml pre/post-install
  commands

Fixes:

- Add pip to lint env deps (for mypy) and to test/e2e env deps
  (defensively, for any test that subprocesses pip).
- Migrate the four hatch.toml files that explicitly used python -m pip
  (ddev, datadog_checks_dev, root docs env, kafka_consumer) to uv pip.
@datadog-prod-us1-3

datadog-prod-us1-3 Bot commented Apr 28, 2026

Copy link
Copy Markdown

Tests

Fix all issues with BitsAI or with Cursor

⚠️ Warnings

🧪 1 Test failed

❄️ Known flaky: test_e2e_profile_cisco_icm from test_e2e_core_vs_python.py   View in Datadog   (Fix with Cursor)
[s6-init] making user provided files available at /var/run/s6/etc...exited 0.
[s6-init] ensuring user provided files have correct perms...exited 0.
[fix-attrs.d] applying ownership & permissions fixes...
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 01-check-apikey.sh: executing... 
[cont-init.d] 01-check-apikey.sh: exited 0.
[cont-init.d] 50-ci.sh: executing... 
[cont-init.d] 50-ci.sh: exited 0.
[cont-init.d] 50-ecs-managed.sh: executing... 
...

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 0.00%
Overall Coverage: 87.26% (+0.12%)

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: af5f65b | Docs | Datadog PR Page | Give us feedback!

…elper

uv builds this legacy setup.py-only package via setuptools.build_meta:__legacy__,
which writes the name verbatim into METADATA. With the underscored form
'datadog_checks_tests_helper', get_datadog_wheels() in datadog_checks_base
(filters by 'datadog-' prefix) didn't pick it up, breaking
test_get_datadog_wheels. Distribution names should use hyphens per PEP 503/625
anyway.
@codecov

codecov Bot commented Apr 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.83%. Comparing base (06dc694) to head (af5f65b).
⚠️ Report is 52 commits behind head on master.

Additional details and impacted files
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

AAraKKe added 3 commits April 28, 2026 18:57
uv defaults to hardlink mode on Windows. When backports.zstd._zstd.pyd
(transitive of hatch>=1.13) is loaded by Python during the test run,
Windows refuses to delete the venv's hardlinked copy while the cache
copy is still pinned. This breaks hatch env remove invoked by
ddev test --compat / --recreate.

See astral-sh/uv#7918
Regular Windows test jobs do not invoke 'hatch env remove' at the end,
so they keep uv's hardlink speed-up. Only minimum-base-package jobs
(which run with --compat) need the copy mode to allow venv cleanup.
The application_name field in HANA audit logs reflects the client
process name (argv[0]). uv-created venvs launch pytest as 'python3',
virtualenv-created venvs launch it as 'python'. Both are valid; the
integration just forwards whatever HANA reports. Use ANY instead of
asserting on the runner's binary name.
uv-created venvs (which is what hatch>=1.13 uses with installer=uv)
launch pytest as python3, so HANA records the audit-log
application_name as python3 rather than python. Update the test to
match the new stable layout.
@AAraKKe AAraKKe marked this pull request as ready for review April 28, 2026 17:47
@AAraKKe AAraKKe requested a review from a team as a code owner April 28, 2026 17:47

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ee47f0f0c3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


def finalize_config(self, config):
for env_name, env_config in config.items():
env_config.setdefault('installer', 'uv')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep the pip installer for Python 2 envs

The reusable test workflow still exposes the test-py2 path for non-core/community repos, but this unconditional default makes every collected Hatch env use uv. Hatch's installer = "uv" uses uv for environment creation and package installation, and uv's Python support policy excludes Python 2, so any integration matrix that still has a python = "2.7" env will fail before tests run. Gate this on the resolved env Python version or leave the installer unset for Python 2 environments.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

integrations-extras: no python = "2.x" entries in any hatch.toml or pyproject.toml matrix. Not affected.

marketplace: three integrations still declare py2.7 in their matrix — avmconsulting_workday,
mulesoft_anypoint,
oracle_timesten — all with:

[[envs.default.matrix]]             
python = ["2.7", "3.11"]

None of them actually build a py2 env in CI:

  • marketplace/.github/workflows/test-all.yml defaults test-py2: false and test-py3: true.

  • test-target.yml's setup-test-env.sh translates that into
    SKIP_ENV_NAME=py2.*, so hatch filters the py2 envs out before the installer is invoked:

    if [[ "${INPUT_TEST_PY2:-}" == 'true' && "${INPUT_TEST_PY3:-}" != 'true' ]]; then                                                                           
      SKIP_ENV_NAME="py3.*"                                                                                                                                                                           
    elif [[ "${INPUT_TEST_PY2:-}" != 'true' && "${INPUT_TEST_PY3:-}" == 'true' ]]; then                                                                                                               
      SKIP_ENV_NAME="py2.*"                                                                                                                                                                           
    else                                                                                                                                                                                              
      SKIP_ENV_NAME=""                                                                                                                                                                                
    fi                                                                                                                                                          
  • Verified on the 2026-04-29 nightly minimum-base-package run for oracle_timesten (job 73524721025): resolved
    test-py2: false, only py3.11 envs were built.

The failure mode flagged (uv refusing py2 because of its Python support policy) is real but unreachable from any current caller. It would
require explicitly invoking the workflow with test-py2: true, which is not exercised anywhere today.

Decision: not gating the installer on env Python version. Carrying that conditional in the plugin to cover three already-dead matrix entries puts the workaround in the wrong layer. The
intended follow-up is to drop "2.7" from those three marketplace hatch.toml files and remove the test-py2 plumbing from the reusable workflows once we confirm no external caller relies on
it.

iliakur
iliakur previously approved these changes Apr 28, 2026
Comment thread .github/workflows/build-ddev.yml Outdated
Pin to specific versions for deterministic builds and stable cache reuse.
virtualenv stays on the 20.x line (latest 20.39.1) due to hatch issue #2193.
@temporal-github-worker-1 temporal-github-worker-1 Bot dismissed iliakur’s stale review April 29, 2026 09:31

Review from iliakur is dismissed. Related teams and files:

  • agent-integrations
    • .github/workflows/build-ddev.yml
@dd-octo-sts

dd-octo-sts Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

⚠️ The qa/skip-qa label has been added with shippable changes

The following files, which will be shipped with the agent, were modified in this PR and
the qa/skip-qa label has been added.

You can ignore this if you are sure the changes in this PR do not require QA. Otherwise,
consider removing the label.

List of modified files that will be shipped with the agent
hatch.toml
kafka_consumer/hatch.toml

NouemanKHAL
NouemanKHAL previously approved these changes Apr 29, 2026
hatch 1.16.5 declares virtualenv>=21 as a hard requirement (the
2026-02-25 virtualenv 21.0.0 release moved interpreter discovery to
the python-discovery package, fixed in hatch by pypa/hatch#2196).
The earlier 20.39.1 pin would have failed dependency resolution.
@temporal-github-worker-1 temporal-github-worker-1 Bot dismissed NouemanKHAL’s stale review April 29, 2026 09:34

Review from NouemanKHAL is dismissed. Related teams and files:

  • agent-integrations
    • .github/workflows/build-ddev.yml
@dd-octo-sts

dd-octo-sts Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Validation Report

All 20 validations passed.

Show details
Validation Description Status
agent-reqs Verify check versions match the Agent requirements file
ci Validate CI configuration and Codecov settings
codeowners Validate every integration has a CODEOWNERS entry
config Validate default configuration files against spec.yaml
dep Verify dependency pins are consistent and Agent-compatible
http Validate integrations use the HTTP wrapper correctly
imports Validate check imports do not use deprecated modules
integration-style Validate check code style conventions
jmx-metrics Validate JMX metrics definition files and config
labeler Validate PR labeler config matches integration directories
legacy-signature Validate no integration uses the legacy Agent check signature
license-headers Validate Python files have proper license headers
licenses Validate third-party license attribution list
metadata Validate metadata.csv metric definitions
models Validate configuration data models match spec.yaml
openmetrics Validate OpenMetrics integrations disable the metric limit
package Validate Python package metadata and naming
readmes Validate README files have required sections
saved-views Validate saved view JSON file structure and fields
version Validate version consistency between package and changelog

View full run

@AAraKKe AAraKKe enabled auto-merge April 29, 2026 10:10

@NouemanKHAL NouemanKHAL left a comment

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.

Nice!

@AAraKKe AAraKKe added this pull request to the merge queue Apr 29, 2026
Merged via the queue into master with commit a0c604e Apr 29, 2026
397 of 399 checks passed
@AAraKKe AAraKKe deleted the aarakke/hatch-uv-installer branch April 29, 2026 11:50
@dd-octo-sts dd-octo-sts Bot added this to the 7.79.0 milestone Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants