chore(ci): apply 48h cooldown to dependabot and riot lockfile updates - #18182
gh-worker-dd-mergequeue-cf854d[bot] merged 17 commits into
Conversation
Implements the cross-language supply-chain hardening "cooldown" controls documented in APMLP-1343 for Python (dd-trace-py). - GHA-CD (APMLP-1359): adds a 48h `cooldown` block to the existing `github-actions` ecosystem in `.github/dependabot.yml` so Dependabot never proposes an update to a release that is less than two days old. Major bumps wait 7 days for extra safety. - DOCKER-CD (APMLP-1360): adds new `docker` and `docker-compose` ecosystems to Dependabot for the Dockerfiles in `docker/`, `benchmarks/`, `lib-injection/` and the `docker-compose*.yml` files, all with the same 48h cooldown. This gives us automated digest rotation that respects the cooldown instead of the previous fully manual process. - TEST-CD (APMLP-1362): teaches `scripts/freshvenvs.py` to ignore PyPI releases that are less than `COOLDOWN_DAYS` (= 2) old when deciding whether the riot test lockfiles are out of date. The daily `generate-package-versions.yml` workflow now waits 48h after a release before regenerating a riot lockfile against it. Falls back to the absolute latest if every candidate is too fresh so detection is never silently disabled. Known limitation captured in APMLP-1362: riot uses `python -m piptools compile` (pip-tools, not uv) and pip-tools has no `--exclude-newer` flag, so transitive dependencies of a triggered direct update can still be < 48h old. A follow-up will either switch riot to `uv pip compile --exclude-newer` or add a post-compile validator. APMLP-1361 (DEP-PIN) is intentionally not implemented: ddtrace is a library and its runtime dependencies must remain version ranges so they coexist with the customer's installed versions; PyPI's immutable releases provide the equivalent protection against tag-mutation attacks. Co-authored-by: Cursor <cursoragent@cursor.com>
Codeowners resolved as |
|
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>
…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>
BenchmarksBenchmark execution time: 2026-05-21 07:53:05 Comparing candidate commit a0bbc33 in PR branch Found 0 performance improvements and 4 performance regressions! Performance is the same for 616 metrics, 10 unstable metrics. scenario:iastaspects-lstrip_aspect
scenario:iastaspects-translate_aspect
scenario:iastaspectsospath-ospathbasename_aspect
scenario:span-start
|
Resolved docker/Dockerfile conflict: kept RIOT_VERSION=0.22.0 from this branch and UV_VERSION=0.11.14 from main. Co-authored-by: Cursor <cursoragent@cursor.com>
emmettbutler
left a comment
There was a problem hiding this comment.
Looks like a solid security improvement
|
/merge |
|
View all feedbacks in Devflow UI.
The expected merge time in
Tests failed on this commit 1e6f4b8: What to do next?
|
…r ecosystems The github-actions, docker, and docker-compose ecosystems do not support the semver-major/minor/patch-days cooldown keys; only default-days is allowed. These keys are rejected by Dependabot's config validation, which runs in the merge queue, so keep default-days: 2 only. Co-authored-by: Cursor <cursoragent@cursor.com>
GitHub's native Dependabot config validator only reliably runs against the merge queue's merge commit, so invalid configs (such as semver-*-days cooldown keys on non-semver ecosystems) are surfaced at merge time rather than on PR CI. Add a validate-dependabot workflow that runs on PRs touching the config. It performs structural JSON-schema validation plus a self-contained guard (scripts/validate_dependabot_config.py) for the ecosystem-specific cooldown rule that the public schema does not encode. Co-authored-by: Cursor <cursoragent@cursor.com>
The new validate-dependabot workflow installed pyyaml and
check-jsonschema directly, which triggers the check_ci_dependencies job
("Direct pip install detected"). Both packages are already pinned with
== and are only used by this one workflow, so adding them to
ci/requirements/ci.txt would be overkill.
Use the documented `# ci-deps: allow` exception (which requires version
specifiers — already satisfied) following the same pattern as
.gitlab/package.yml:434.
Signed-off-by: Juanjo Alvarez <juanjo.alvarezmartinez@datadoghq.com>
f4e8000
into
main
## Description Output of running `scripts/update-ci-dependencies --compile --update-workflows`. This should restore the `Update riot lockfiles` workflow’s cooldown behavior added in #18182, since the workflow installs CI tools from `ci/requirements/ci.txt`. Co-authored-by: louis.tricot <louis.tricot@datadoghq.com>
Note
Depends on the next riot version (0.22.0) so not mergeable until that one has been released and
scripts/update-ci-dependencieshas 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:
cooldownblock to the existinggithub-actionsentry in.github/dependabot.yml. GitHub Actions are already SHA-pinned (the existingGHA-PINwork); this adds the missing 48h delay before proposing an update.dockeranddocker-composeecosystems to.github/dependabot.ymlfordocker/,benchmarks/,lib-injection/, the rootdocker-compose*.ymlfiles, with the same 48h cooldown. This replaces a fully manual digest-rotation flow with cooldown-aware automation. (Tightening DOCKER-PIN on the unpinneddocker-compose.ymlimages, e.g.mysql:5.7,redis:4.0-alpine, etc., is intentionally out of scope here and would be a follow-up.)scripts/freshvenvs.pyignores PyPI releases that are less thanCOOLDOWN_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.Run regenerate-riot-latestworkflow step setsRIOT_PIP_COMPILE_BACKEND=uvandRIOT_PIP_COMPILE_EXCLUDE_NEWER=<cutoff>(computed asnow - 48h). With the riot change in DataDog/riot#267 this tells riot to resolve the lockfile viauv pip compile --exclude-newer=<cutoff>so transitive dependencies younger than the cooldown cannot enter the lockfile in the first place.scripts/check_lockfile_cooldown.pywalks the regenerated lockfiles after compile, queries PyPI for the upload time of everyname==versionpin, and exits non-zero if any release is younger thanCOOLDOWN_DAYS. It is invoked fromscripts/regenerate-riot-latest.shimmediately aftercompile-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).