From bae977204f5f8839156150bd4968487502677a58 Mon Sep 17 00:00:00 2001 From: ryan-morris Date: Wed, 7 Oct 2026 09:40:29 -0500 Subject: [PATCH 1/2] fix(deps): hold nv-codec-headers on SDK 13.0 (driver 570+); make dependency holds reviewable nv-codec-headers SDK 13.1 raises the minimum NVIDIA driver to 610, and FFmpeg's NVENC refuses to open on anything older. NVIDIA's 580 branch is the last for Maxwell/Pascal/Volta, so 13.1 retired NVENC for those cards outright, and for any machine not yet on 610 (measured on an RTX 3080 with Windows driver 591.86). Pin n13.0.19.1 (570+) and cap Renovate to the 13.0 line; 13.0.x patch releases still flow. The nv-codec rule gets explicit four-component versioning: the default semver-coerced scheme drops the build number, so n13.0.19.2 would never be offered. Holds never expire on their own, which is how a pinned dependency stagnates. Each hold now carries a structured record in deps.json (.defaults..hold: allowedVersions, issue, reviewAfter, liftWhen): - ledger-validate.sh checks its shape; - scripts/ci/holds-test.sh keeps it and the renovate.json rule in step (exactly one unconditional rule, identical cap, pin inside its own cap), with fixtures; - a review-holds job in the weekly check-updates.yml comments on the hold's issue once reviewAfter passes, labels it hold-review-due until a human lifts the hold or sets a new date, and reopens the issue if it was closed while the hold is live. It never lifts a hold or moves a date by itself. mbedTLS's existing hold (#18) gets the same record, and its rationale is corrected: its consumers here are SRT and librist, not FFmpeg's TLS layer. Refs #54, #18 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/check-updates.yml | 23 +++- .github/workflows/shellcheck.yml | 1 + deps.json | 19 +++- docs/install/linux.md | 5 + docs/install/windows.md | 5 + docs/matrix/ffmpeg-8-gplv2.md | 2 +- docs/matrix/ffmpeg-8-gplv3.md | 2 +- docs/matrix/ffmpeg-8-lgplv2.md | 2 +- docs/matrix/ffmpeg-8-lgplv3.md | 2 +- docs/matrix/ffmpeg-9-gplv2.md | 2 +- docs/matrix/ffmpeg-9-gplv3.md | 2 +- docs/matrix/ffmpeg-9-lgplv2.md | 2 +- docs/matrix/ffmpeg-9-lgplv3.md | 2 +- renovate.json | 10 +- scripts/ci/holds-test.sh | 160 +++++++++++++++++++++++++++ scripts/ci/review-holds.sh | 110 ++++++++++++++++++ scripts/deps/ledger-validate-test.sh | 27 +++++ scripts/deps/ledger-validate.sh | 24 ++++ 18 files changed, 387 insertions(+), 13 deletions(-) create mode 100644 scripts/ci/holds-test.sh create mode 100644 scripts/ci/review-holds.sh diff --git a/.github/workflows/check-updates.yml b/.github/workflows/check-updates.yml index 18b7206..ff509dd 100644 --- a/.github/workflows/check-updates.yml +++ b/.github/workflows/check-updates.yml @@ -1,4 +1,4 @@ -name: Check for a new FFmpeg major line +name: Check for a new FFmpeg major line and due dependency-hold reviews on: schedule: @@ -152,3 +152,24 @@ jobs: fi echo "Done." + + # Dependency holds (deps.json .defaults..hold) never expire on their own, so each one + # carries a reviewAfter date and a tracking issue; scripts/ci/review-holds.sh raises the review + # there once the date passes and reopens the issue if it was closed while the hold is live. + # Its own job: it needs issue write access, which is a GITHUB_TOKEN permission -- the job above + # runs on UPDATE_PR_TOKEN, whose scopes this repo cannot see -- and a failure here must not + # stop the new-major check (or the reverse). Read-only on contents. + review-holds: + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - name: Review dependency holds + run: bash scripts/ci/review-holds.sh diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 1b1ae4a..003198a 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -67,6 +67,7 @@ jobs: bash scripts/ci/image-pins-test.sh bash scripts/ci/matrix-consistency-test.sh bash scripts/ci/renovate-config-test.sh + bash scripts/ci/holds-test.sh # Our dep scripts are SOURCED by build.sh and share its environment, so many variables # are assigned by the parent, not the script itself; -x follows sourced files so diff --git a/deps.json b/deps.json index c44ab5f..9bc7e15 100644 --- a/deps.json +++ b/deps.json @@ -209,8 +209,15 @@ }, "nv-codec": { "origin": "https://github.com/FFmpeg/nv-codec-headers.git", - "tag": "n13.1.15.0", - "datasource": "github-tags" + "tag": "n13.0.19.1", + "datasource": "github-tags", + "reason": "Pinned to the SDK 13.0 line for driver compatibility. Each nv-codec-headers SDK line sets a minimum NVIDIA driver, and FFmpeg's NVENC refuses to open on anything older ('Driver does not support the required nvenc API version'): sdk/13.0 needs 570+, sdk/13.1 needs 610+. NVIDIA's 580 branch is the LAST driver for Maxwell/Pascal/Volta (GTX 900/10-series), so 13.1 headers would break NVENC on those cards permanently, and on any machine not yet on 610 (measured: RTX 3080 on Windows driver 591.86). Both FFmpeg lines accept ffnvcodec >= 12.1.14.0, and the 13.1-only features (newer counting fields, AV1 hierarchical refs) sit behind version guards with fallbacks, so 13.0 loses optional features, not builds. See hold.", + "hold": { + "allowedVersions": "/^n13\\.0\\./", + "issue": 54, + "reviewAfter": "2027-04-07", + "liftWhen": "Review, never lift automatically, when: an FFmpeg line we ship requires ffnvcodec >= 13.1 (consider a per-line override rather than raising every line's floor); NVIDIA ends the 580 branch (lifting still retires NVENC on Maxwell/Pascal/Volta -- that is a policy decision, not a consequence); or a 13.1+ NVENC feature is worth raising the driver floor to 610. Re-run a real NVENC encode on the oldest driver the new floor claims." + } }, "opus": { "origin": "https://github.com/xiph/opus.git", @@ -313,7 +320,13 @@ "origin": "https://github.com/Mbed-TLS/mbedtls.git", "tag": "v3.6.7", "datasource": "github-tags", - "reason": "Renovate tracks the git tag (git-tags on origin), but mbedtls.sh downloads the matching release tarball (mbedtls-.tar.bz2, v stripped) instead of cloning — the tarball bundles the 'framework' submodule in-tree, avoiding a submodule init. Pin the 3.6 LTS line (4.x is the TF-PSA-Crypto split, not yet consumed by FFmpeg/SRT/librist). Held at 3.x by a renovate.json allowedVersions rule: FFmpeg's tls_mbedtls.c uses mbedtls_ctr_drbg_*/mbedtls_entropy_* unconditionally and mbedTLS 4 removed those headers (moved to PSA Crypto); FFmpeg master has not migrated either. Tracked in issue #18." + "reason": "Renovate tracks the git tag (git-tags on origin), but mbedtls.sh downloads the matching release tarball (mbedtls-.tar.bz2, v stripped) instead of cloning — the tarball bundles the 'framework' submodule in-tree, avoiding a submodule init. Pin the 3.6 LTS line (4.x is the TF-PSA-Crypto split, not yet consumed by FFmpeg/SRT/librist). Held at 3.x by a renovate.json allowedVersions rule (see hold): mbedTLS 4 removed ctr_drbg.h/entropy.h (moved to PSA Crypto). The consumers here are SRT and librist, not FFmpeg's TLS layer (OpenSSL/GnuTLS/SChannel), so the hold lifts on THEIR mbedTLS 4 support. Tracked in issue #18.", + "hold": { + "allowedVersions": "<4.0.0", + "issue": 18, + "reviewAfter": "2027-04-07", + "liftWhen": "Review when the SRT and librist versions we ship build and link against mbedTLS 4 (its TF-PSA-Crypto split) through this repo's mbedtls.sh recipe -- those transports, not FFmpeg's TLS layer (OpenSSL/GnuTLS/SChannel here), are what consume it. Verify encrypted SRT and RIST transport before lifting." + } }, "srt": { "origin": "https://github.com/Haivision/srt.git", diff --git a/docs/install/linux.md b/docs/install/linux.md index 8b5a028..5c5824c 100644 --- a/docs/install/linux.md +++ b/docs/install/linux.md @@ -59,6 +59,11 @@ To actually **use** hardware acceleration or GPU Whisper you additionally need t for Vulkan/GPU transcription. Without one, hardware paths are simply unavailable and Whisper falls back to CPU, so treat drivers as optional `Recommends`, never a hard dependency. +**NVIDIA (NVENC/NVDEC/CUDA)** needs the proprietary driver at **570 or newer** — the floor set by +the NVIDIA Video Codec SDK 13.0 headers these builds use. On an older driver FFmpeg reports +`Driver does not support the required nvenc API version` and the NVIDIA paths are unavailable; +everything else is unaffected. The 580 series (the last for GTX 900/10-series cards) qualifies. + **`linux-musl-x64`/`linux-musl-arm64` (Alpine)** — the latter is the Alpine-on-ARM build (AWS Graviton, Ampere, and Docker Desktop on Apple Silicon, which defaults to arm64 containers) — are self-contained in the same way as the glibc builds above: the hwaccel dispatch libraries diff --git a/docs/install/windows.md b/docs/install/windows.md index 7802ca5..5a77d4a 100644 --- a/docs/install/windows.md +++ b/docs/install/windows.md @@ -32,6 +32,11 @@ Windows needs no extra runtime install: the Vulkan loader (`vulkan-1.dll`) ships driver, and TLS uses the OS-native **SChannel** backend in every cell (unaffected by the v2/v3 split — see the [runtime-dependency overview](./README.md#runtime-dependencies)). +On `win-x64`, **NVIDIA (NVENC/NVDEC/CUDA)** needs driver **570 or newer** — the floor set by the +NVIDIA Video Codec SDK 13.0 headers these builds use. On an older driver FFmpeg reports +`Driver does not support the required nvenc API version` and the NVIDIA paths are unavailable; +everything else is unaffected. The 580 series (the last for GTX 900/10-series cards) qualifies. + --- Back to the [install hub](./README.md) · [Linux](./linux.md) · [macOS](./macos.md) · diff --git a/docs/matrix/ffmpeg-8-gplv2.md b/docs/matrix/ffmpeg-8-gplv2.md index 9df9b4a..525285e 100644 --- a/docs/matrix/ffmpeg-8-gplv2.md +++ b/docs/matrix/ffmpeg-8-gplv2.md @@ -153,7 +153,7 @@ _15 of 23 built._ | CUVID (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | D3D11VA | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | DXVA2 | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| ffnvcodec headers | n13.1.15.0 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | +| ffnvcodec headers | n13.0.19.1 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | libdrm | libdrm-2.4.134 | ✓ | ✓ | — | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | MediaFoundation | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | NVDEC (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | diff --git a/docs/matrix/ffmpeg-8-gplv3.md b/docs/matrix/ffmpeg-8-gplv3.md index ff4e574..8f969ee 100644 --- a/docs/matrix/ffmpeg-8-gplv3.md +++ b/docs/matrix/ffmpeg-8-gplv3.md @@ -153,7 +153,7 @@ _16 of 23 built._ | CUVID (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | D3D11VA | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | DXVA2 | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| ffnvcodec headers | n13.1.15.0 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | +| ffnvcodec headers | n13.0.19.1 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | libdrm | libdrm-2.4.134 | ✓ | ✓ | — | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | MediaFoundation | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | NVDEC (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | diff --git a/docs/matrix/ffmpeg-8-lgplv2.md b/docs/matrix/ffmpeg-8-lgplv2.md index 7a33a9c..340d41d 100644 --- a/docs/matrix/ffmpeg-8-lgplv2.md +++ b/docs/matrix/ffmpeg-8-lgplv2.md @@ -153,7 +153,7 @@ _15 of 23 built._ | CUVID (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | D3D11VA | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | DXVA2 | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| ffnvcodec headers | n13.1.15.0 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | +| ffnvcodec headers | n13.0.19.1 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | libdrm | libdrm-2.4.134 | ✓ | ✓ | — | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | MediaFoundation | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | NVDEC (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | diff --git a/docs/matrix/ffmpeg-8-lgplv3.md b/docs/matrix/ffmpeg-8-lgplv3.md index e1b62f5..6275d20 100644 --- a/docs/matrix/ffmpeg-8-lgplv3.md +++ b/docs/matrix/ffmpeg-8-lgplv3.md @@ -153,7 +153,7 @@ _16 of 23 built._ | CUVID (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | D3D11VA | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | DXVA2 | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| ffnvcodec headers | n13.1.15.0 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | +| ffnvcodec headers | n13.0.19.1 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | libdrm | libdrm-2.4.134 | ✓ | ✓ | — | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | MediaFoundation | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | NVDEC (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | diff --git a/docs/matrix/ffmpeg-9-gplv2.md b/docs/matrix/ffmpeg-9-gplv2.md index aca7699..017ae21 100644 --- a/docs/matrix/ffmpeg-9-gplv2.md +++ b/docs/matrix/ffmpeg-9-gplv2.md @@ -150,7 +150,7 @@ _15 of 22 built._ | CUVID (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | D3D11VA | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | DXVA2 | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| ffnvcodec headers | n13.1.15.0 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | +| ffnvcodec headers | n13.0.19.1 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | libdrm | libdrm-2.4.134 | ✓ | ✓ | — | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | MediaFoundation | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | NVDEC (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | diff --git a/docs/matrix/ffmpeg-9-gplv3.md b/docs/matrix/ffmpeg-9-gplv3.md index 3a4b8e9..b42efe9 100644 --- a/docs/matrix/ffmpeg-9-gplv3.md +++ b/docs/matrix/ffmpeg-9-gplv3.md @@ -150,7 +150,7 @@ _16 of 22 built._ | CUVID (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | D3D11VA | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | DXVA2 | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| ffnvcodec headers | n13.1.15.0 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | +| ffnvcodec headers | n13.0.19.1 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | libdrm | libdrm-2.4.134 | ✓ | ✓ | — | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | MediaFoundation | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | NVDEC (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | diff --git a/docs/matrix/ffmpeg-9-lgplv2.md b/docs/matrix/ffmpeg-9-lgplv2.md index e54c461..0741361 100644 --- a/docs/matrix/ffmpeg-9-lgplv2.md +++ b/docs/matrix/ffmpeg-9-lgplv2.md @@ -150,7 +150,7 @@ _15 of 22 built._ | CUVID (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | D3D11VA | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | DXVA2 | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| ffnvcodec headers | n13.1.15.0 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | +| ffnvcodec headers | n13.0.19.1 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | libdrm | libdrm-2.4.134 | ✓ | ✓ | — | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | MediaFoundation | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | NVDEC (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | diff --git a/docs/matrix/ffmpeg-9-lgplv3.md b/docs/matrix/ffmpeg-9-lgplv3.md index 4d82361..2d23245 100644 --- a/docs/matrix/ffmpeg-9-lgplv3.md +++ b/docs/matrix/ffmpeg-9-lgplv3.md @@ -150,7 +150,7 @@ _16 of 22 built._ | CUVID (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | D3D11VA | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | DXVA2 | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | -| ffnvcodec headers | n13.1.15.0 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | +| ffnvcodec headers | n13.0.19.1 | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | libdrm | libdrm-2.4.134 | ✓ | ✓ | — | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | MediaFoundation | | n/a | n/a | n/a | n/a | n/a | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | | NVDEC (NVIDIA decode) | | ✓ | ✓ | — | ✓ | ✓ | ✓ | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | diff --git a/renovate.json b/renovate.json index d28e004..50364c0 100644 --- a/renovate.json +++ b/renovate.json @@ -175,11 +175,19 @@ "allowedVersions": "/^9\\./" }, { - "description": "Hold mbedTLS on 3.x. FFmpeg's libavformat/tls_mbedtls.c includes and and calls mbedtls_ctr_drbg_seed/mbedtls_entropy_init/mbedtls_entropy_func unconditionally, with no version guard; mbedTLS 4.x removed those headers when that work moved to PSA Crypto, so 4.x cannot compile against any FFmpeg line we ship. Without this the grouped PR would be permanently red on an impossible bump, which is worse than not offering it. Remove once FFmpeg's mbedTLS backend supports 4.x. Tracked in issue #18.", + "description": "Hold mbedTLS on 3.x. mbedTLS 4.x removed and when that work moved to PSA Crypto (the TF-PSA-Crypto split). Its consumers here are SRT and librist -- FFmpeg's own TLS layer uses OpenSSL/GnuTLS/SChannel in this build -- so the hold lifts when the SRT and librist versions we ship support 4.x. Without this the grouped PR would carry a bump nothing has validated. The rationale, tracking issue (#18) and review date live in deps.json (.defaults.mbedtls.hold); scripts/ci/holds-test.sh keeps this rule and that record in step.", "matchPackageNames": [ "https://github.com/Mbed-TLS/mbedtls.git" ], "allowedVersions": "<4.0.0" + }, + { + "description": "Hold nv-codec-headers on the SDK 13.0 line: sdk/13.1 raises the minimum NVIDIA driver to 610, which Maxwell/Pascal/Volta cards (last driver: 580) can never run, and FFmpeg's NVENC refuses to open on an older driver. 13.0.x patch releases still flow. The rationale, tracking issue and review date live in deps.json (.defaults[\"nv-codec\"].hold) -- metadata keys here would make Renovate refuse to start (see renovate-config-test.sh) -- and scripts/ci/holds-test.sh keeps this rule and that record in step. versioning: tags are n..., and the default semver-coerced scheme drops the fourth component, so n13.0.19.1 -> n13.0.19.2 would never compare as newer.", + "matchPackageNames": [ + "https://github.com/FFmpeg/nv-codec-headers.git" + ], + "versioning": "regex:^n(?\\d+)\\.(?\\d+)\\.(?\\d+)\\.(?\\d+)$", + "allowedVersions": "/^n13\\.0\\./" } ], "customDatasources": { diff --git a/scripts/ci/holds-test.sh b/scripts/ci/holds-test.sh new file mode 100644 index 0000000..ac38060 --- /dev/null +++ b/scripts/ci/holds-test.sh @@ -0,0 +1,160 @@ +#!/usr/bin/env bash +# Dependency holds: deps.json and renovate.json must agree, and the pin must satisfy its own cap. +# +# A hold is split across two files on purpose. The CAP is a renovate.json packageRule +# (allowedVersions), because that is what Renovate obeys. The WHY / WHEN is a `hold` record in +# deps.json (.defaults..hold: allowedVersions, issue, reviewAfter, liftWhen), because +# metadata keys on a packageRule make Renovate refuse to start (renovate-config-test.sh), and +# check-updates.yml reads the record to raise the review on the issue. Two files means two ways +# to drift, each of which turns a hold into something nobody revisits or nothing enforces: +# - a record with no rule: the review reminder fires, but Renovate was never capped; +# - a rule with no record: Renovate is capped forever and nothing ever asks why; +# - a rule with extra match conditions, or a second rule for the same package, can narrow or +# contradict the cap without either file looking wrong; +# - a pin outside its own cap (an accidental mbedTLS 4 pin) passes everything else. +# So: every ledger hold has EXACTLY ONE renovate rule constraining its origin, unconditional +# (matchPackageNames == [origin], nothing else to match on) with the identical allowedVersions; +# every allowedVersions rule that touches a ledger dep is some hold's rule; and the pinned tag +# satisfies the cap (and a regex `versioning`, when the rule sets one). +# +# Runs fixture cases first, then the real files. Shape (types, dates) is ledger-validate.sh's job. +set -uo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")/../.." || exit 1 +command -v python3 >/dev/null 2>&1 || { echo "holds-test: python3 required" >&2; exit 2; } + +check_holds() { # -> exit 0 if consistent, prints problems + python3 - "$1" "$2" <<'PY' +import json, re, sys + +ledger = json.load(open(sys.argv[1])) +rules = json.load(open(sys.argv[2])).get("packageRules", []) +defaults = ledger.get("defaults", {}) +entries = list(defaults.items()) + [(k, v) for o in ledger.get("overrides", {}).values() for k, v in o.items()] +origins = {v.get("origin") for _, v in entries if v.get("origin")} +names = {k for k, _ in entries} +problems = [] + +def vtuple(tag): + m = re.match(r"^[^0-9]*([0-9]+(?:\.[0-9]+)*)", tag) + return tuple(int(x) for x in m.group(1).split(".")) if m else None + +def cmp(a, b): + n = max(len(a), len(b)); a += (0,) * (n - len(a)); b += (0,) * (n - len(b)) + return (a > b) - (a < b) + +def satisfies(tag, constraint): + """Renovate allowedVersions: a /regex/ against the raw tag, or numeric comparators. + Anything else is rejected rather than guessed at -- extend this when a hold needs more.""" + if len(constraint) > 1 and constraint.startswith("/") and constraint.endswith("/"): + return re.search(constraint[1:-1], tag) is not None, None + v = vtuple(tag) + if v is None: + return False, f"tag {tag!r} has no numeric version to compare" + parts = [p for p in re.split(r"[,\s]+", constraint.strip()) if p] + for p in parts: + m = re.fullmatch(r"(<=|>=|<|>|=)v?([0-9]+(?:\.[0-9]+)*)", p) + if not m: + return False, f"unsupported allowedVersions syntax {constraint!r} (holds-test understands /regex/ and <,<=,>,>=,= comparators)" + c = cmp(v, tuple(int(x) for x in m.group(2).split("."))) + if not {"<": c < 0, "<=": c <= 0, ">": c > 0, ">=": c >= 0, "=": c == 0}[m.group(1)]: + return False, None + return True, None + +def touches(rule): + return (set(rule.get("matchPackageNames", [])) & origins) or (set(rule.get("matchDepNames", [])) & names) + +hold_rules = set() +for dep, e in defaults.items(): + h = e.get("hold") + if not isinstance(h, dict): + continue + origin, want = e.get("origin"), h.get("allowedVersions") + capping = [i for i, r in enumerate(rules) if "allowedVersions" in r and + (origin in r.get("matchPackageNames", []) or dep in r.get("matchDepNames", []))] + if len(capping) != 1: + problems.append(f"{dep}: hold needs exactly one renovate allowedVersions rule for {origin}, found {len(capping)}") + continue + i = capping[0]; r = rules[i]; hold_rules.add(i) + extra = set(r) - {"description", "matchPackageNames", "allowedVersions", "versioning"} + if r.get("matchPackageNames") != [origin] or extra: + problems.append(f"{dep}: its renovate rule must be unconditional -- matchPackageNames exactly [{origin}] and no other match keys (has {sorted(extra) or r.get('matchPackageNames')})") + if r.get("allowedVersions") != want: + problems.append(f"{dep}: deps.json hold allows {want!r} but renovate.json allows {r.get('allowedVersions')!r}") + tag = e.get("tag") + if tag is None: + problems.append(f"{dep}: a hold needs a tag pin to check against the cap") + continue + ok, why = satisfies(tag, want or "") + if why: + problems.append(f"{dep}: {why}") + elif not ok: + problems.append(f"{dep}: pinned tag {tag} is OUTSIDE its own hold {want!r}") + ver = r.get("versioning", "") + if ver.startswith("regex:"): + pat = re.sub(r"\(\?<", "(?P<", ver[len("regex:"):]) + if not re.fullmatch(pat, tag): + problems.append(f"{dep}: pinned tag {tag} does not match the rule's versioning {ver!r}, so Renovate cannot order it") + +for i, r in enumerate(rules): + if "allowedVersions" in r and i not in hold_rules and touches(r): + problems.append(f"renovate.json packageRules[{i}] caps a ledger dep ({sorted(touches(r))}) with no deps.json hold record (add .defaults..hold with issue/reviewAfter/liftWhen)") + +for p in problems: + print(" " + p) +sys.exit(1 if problems else 0) +PY +} + +FIX="$(mktemp -d)"; trap 'rm -rf "${FIX}"' EXIT +pass=0; fail=0 +case_() { #