Skip to content

feat(requirements): add uv pip compile backend with --exclude-newer support - #267

Merged
juanjux merged 1 commit into
masterfrom
juanjux/uv-pip-compile-exclude-newer
May 20, 2026
Merged

juanjux merged 1 commit into
masterfrom
juanjux/uv-pip-compile-exclude-newer

Conversation

@juanjux

@juanjux juanjux commented May 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds an opt-in uv pip compile backend to VenvInstance.requirements, selectable via two new environment variables:

  • RIOT_PIP_COMPILE_BACKEND: piptools (default) or uv.
  • RIOT_PIP_COMPILE_EXCLUDE_NEWER: forwarded to uv pip compile as --exclude-newer=<value>. Ignored (with a warning) when the backend is piptools because pip-tools has no equivalent option.

The historical behaviour is preserved exactly when neither variable is set. When the uv backend is selected the uv executable must already be on PATH; a clear RuntimeError is raised if it isn't.

Why

Cross-language supply-chain hardening work in dd-trace-py (tracked in DataDog/dd-trace-py#18182) needs a 48h "cooldown" on every release that ends up in a compiled lockfile, including transitive dependencies. --exclude-newer=<date> is the ergonomic primitive for that on uv pip compile; pip-tools has nothing equivalent. Rather than fork riot or post-process its output, projects can now flip a single env var and get the cooldown for free.

@juanjux
juanjux requested a review from a team as a code owner May 20, 2026 08:19
@juanjux
juanjux requested review from Yun-Kim and r1viollet and removed request for a team May 20, 2026 08:19
@datadog-official

This comment has been minimized.

juanjux added a commit to DataDog/dd-trace-py that referenced this pull request May 20, 2026
Closes the transitive-dependency gap on TEST-CD (APMLP-1362). The
previous commit applied the 48h cooldown at the "outdated detection"
level only; this commit adds two complementary mechanisms so freshly
published transitive dependencies cannot make it into a regenerated
.riot/requirements/*.txt either.

1. uv pip compile backend (defense-in-front):
   Sets RIOT_PIP_COMPILE_BACKEND=uv and RIOT_PIP_COMPILE_EXCLUDE_NEWER
   (a cutoff 48h in the past) on the `Run regenerate-riot-latest`
   step of `generate-package-versions.yml`. With the riot change in
   DataDog/riot#267 these tell riot to resolve dependencies via
   `uv pip compile --exclude-newer=<cutoff>`. Older riot versions
   ignore both variables, so it is safe to set them before the riot
   bump lands.

2. Post-compile validator (defense-in-depth):
   Adds scripts/check_lockfile_cooldown.py, which walks the
   regenerated lockfiles, queries PyPI for the upload time of every
   `name==version` pin, and exits non-zero if any release is younger
   than COOLDOWN_DAYS (= 2). Wired into regenerate-riot-latest.sh
   immediately after compile-and-prune-test-requirements so the
   workflow fails before opening an update PR if a transitive bypass
   ever slips through.

Tests cover the lockfile parser, the cooldown decision logic, and
both pass / fail paths via `tests/internal/test_check_lockfile_cooldown.py`.

Refs APMLP-1362, depends on DataDog/riot#267.

Co-authored-by: Cursor <cursoragent@cursor.com>
@juanjux
juanjux force-pushed the juanjux/uv-pip-compile-exclude-newer branch 2 times, most recently from dbcc181 to 6b0a5f8 Compare May 20, 2026 08:25
…upport

Add an opt-in ``uv pip compile`` backend for ``VenvInstance.requirements``
selectable via two new environment variables:

- ``RIOT_PIP_COMPILE_BACKEND``: ``piptools`` (default) or ``uv``.
- ``RIOT_PIP_COMPILE_EXCLUDE_NEWER``: forwarded to ``uv pip compile`` as
  ``--exclude-newer=<value>``. Ignored (with a warning) when the backend
  is ``piptools`` because pip-tools has no equivalent option.

The historical behaviour is preserved exactly when neither variable is
set. When the ``uv`` backend is selected the ``uv`` executable must
already be on PATH; a clear ``RuntimeError`` is raised if it isn't.

This is motivated by the cross-language supply-chain hardening work in
``dd-trace-py`` (APMLP-1343 / TEST-CD): downstream projects need a way
to keep ``uv pip compile`` from pulling transitive dependencies that
were published less than 48h ago, and ``--exclude-newer`` is the
ergonomic primitive for that. ``pip-tools`` has no equivalent flag, so
projects that want the cooldown must opt in to the ``uv`` backend.

Tests cover the new code paths (uv backend invocation, exclude-newer
pass-through, missing-uv error, unknown-backend error, and the warning
emitted when ``RIOT_PIP_COMPILE_EXCLUDE_NEWER`` is set alongside the
pip-tools backend).

Co-authored-by: Cursor <cursoragent@cursor.com>
@juanjux
juanjux force-pushed the juanjux/uv-pip-compile-exclude-newer branch from 6b0a5f8 to 09a512d Compare May 20, 2026 08:41

@brettlangdon brettlangdon 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.

suggestion, but in general lgtm

Comment thread docs/configuration.rst
Comment thread riot/venv.py

@brettlangdon brettlangdon 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.

I am ok with this as-is, if we find no issues during the transition, then migrating to be only uv compile based makes sense as a follow-up

e.g.

  1. this PR
  2. next PR adds uv as project dep and change default to uv
  3. remove backend selection and only use uv

given we are primarily the only users, we can probably move a little quicker than that, but all in all seems good

@juanjux
juanjux merged commit c04e0aa into master May 20, 2026
29 checks passed
@juanjux
juanjux deleted the juanjux/uv-pip-compile-exclude-newer branch May 20, 2026 15:10
juanjux added a commit to DataDog/dd-trace-py that referenced this pull request May 20, 2026
…release)

The TEST-CD layer-2 (uv pip compile --exclude-newer) only takes effect
once we are on a riot version that ships the new
RIOT_PIP_COMPILE_BACKEND / RIOT_PIP_COMPILE_EXCLUDE_NEWER env vars
(DataDog/riot#267, merged into master as c04e0aa). The next riot
release will be 0.22.0 (semver minor bump for a feat: commit on top
of 0.21.0).

This commit:
- Bumps RIOT_VERSION in docker/Dockerfile from 0.21.0 to 0.22.0.
- Bumps riot in ci/requirements/ci.in from 0.20.1 to 0.22.0.

ci/requirements/ci.txt is intentionally NOT regenerated yet because
riot 0.22.0 is not on PyPI yet (uv pip compile --generate-hashes
fails with "No solution found"). Before merging this PR a maintainer
must:

  1. Wait for riot 0.22.0 to be cut and uploaded to PyPI.
  2. Run scripts/update-ci-dependencies to regenerate ci.txt with the
     new pin and hashes.
  3. Commit the regenerated ci.txt.

The dd-trace-py CI does not enforce ci.in/ci.txt sync (the
check-ci-dependencies job only validates that ci.txt is internally
consistent under --require-hashes), so this commit is safe to push
while we wait for the release.

Co-authored-by: Cursor <cursoragent@cursor.com>
gh-worker-dd-mergequeue-cf854d Bot pushed a commit to DataDog/dd-trace-py that referenced this pull request Jun 16, 2026
…#18182)

## Note

Depends on the next riot version (0.22.0) so not mergeable until that one has been released and `scripts/update-ci-dependencies` has been run!

## Summary

Implements the cross-language supply-chain hardening "cooldown" controls documented in epic APMLP-1343 for `dd-trace-py`. The 48h cooldown is now enforced everywhere Dependabot or our daily test-lockfile job would otherwise pull in a freshly published release, including transitive dependencies pulled in by lockfile recompilation.

Tickets addressed:

- APMLP-1359 — GHA-CD: added a `cooldown` block to the existing `github-actions` entry in `.github/dependabot.yml`. GitHub Actions are already SHA-pinned (the existing `GHA-PIN` work); this adds the missing 48h delay before proposing an update.
- APMLP-1360 — DOCKER-CD: added new `docker` and `docker-compose` ecosystems to `.github/dependabot.yml` for `docker/`, `benchmarks/`, `lib-injection/`, the root `docker-compose*.yml` files, with the same 48h cooldown. This replaces a fully manual digest-rotation flow with cooldown-aware automation. (Tightening DOCKER-PIN on the unpinned `docker-compose.yml` images, e.g. `mysql:5.7`, `redis:4.0-alpine`, etc., is intentionally out of scope here and would be a follow-up.)
- APMLP-1362 — TEST-CD: three layers of cooldown enforcement on riot test lockfile updates:
  1. **Trigger** — `scripts/freshvenvs.py` ignores PyPI releases that are less than `COOLDOWN_DAYS` (= 2) old when deciding whether the lockfiles are outdated. Falls back to the absolute latest if every candidate is too fresh so detection is never silently disabled.
  2. Transitive Deps — the `Run regenerate-riot-latest` workflow step sets `RIOT_PIP_COMPILE_BACKEND=uv` and `RIOT_PIP_COMPILE_EXCLUDE_NEWER=<cutoff>` (computed as `now - 48h`). With the riot change in [DataDog/riot#267](DataDog/riot#267) this tells riot to resolve the lockfile via `uv pip compile --exclude-newer=<cutoff>` so transitive dependencies younger than the cooldown cannot enter the lockfile in the first place.
  3. Validator — `scripts/check_lockfile_cooldown.py` walks the regenerated lockfiles after compile, queries PyPI for the upload time of every `name==version` pin, and exits non-zero if any release is younger than `COOLDOWN_DAYS`. It is invoked from `scripts/regenerate-riot-latest.sh` immediately after `compile-and-prune-test-requirements`, so the workflow fails before opening an update PR if a fresh transitive pin ever slips through (e.g. because we are still on an older riot, or because someone disables the uv backend).

Co-authored-by: juanjo.alvarezmartinez <juanjo.alvarezmartinez@datadoghq.com>
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