From 0c63e4de64a4a6b8beefd8e1606c3808261d85bd Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Fri, 28 Aug 2026 20:36:08 +0500 Subject: [PATCH 1/2] ci: stop required checks from skipping when locks fail --- .github/workflows/ci.yml | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42277ec85d..d5e8885bfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,8 +71,16 @@ jobs: name: lint runs-on: ubuntu-latest needs: dependency-locks - if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork + if: >- + !cancelled() + && (github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork) steps: + - name: Require fresh dependency locks + if: needs.dependency-locks.result != 'success' + run: | + echo 'The dependency lock freshness job did not succeed.' >&2 + exit 1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -93,13 +101,21 @@ jobs: build: needs: dependency-locks - if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork + if: >- + !cancelled() + && (github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork) timeout-minutes: 10 name: build permissions: contents: read runs-on: ubuntu-latest steps: + - name: Require fresh dependency locks + if: needs.dependency-locks.result != 'success' + run: | + echo 'The dependency lock freshness job did not succeed.' >&2 + exit 1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -148,13 +164,21 @@ jobs: name: test (Python ${{ matrix.python-version }}) runs-on: ubuntu-latest needs: dependency-locks - if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork + if: >- + !cancelled() + && (github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork) strategy: fail-fast: false matrix: # Per-PR coverage protects both ends of the support window. python-version: ["3.10", "3.14"] steps: + - name: Require fresh dependency locks + if: needs.dependency-locks.result != 'success' + run: | + echo 'The dependency lock freshness job did not succeed.' >&2 + exit 1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -185,8 +209,16 @@ jobs: name: test (HTTPX2) runs-on: ubuntu-latest needs: dependency-locks - if: github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork + if: >- + !cancelled() + && (github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork) steps: + - name: Require fresh dependency locks + if: needs.dependency-locks.result != 'success' + run: | + echo 'The dependency lock freshness job did not succeed.' >&2 + exit 1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false From 19fe1e909cd04219b70a16a49ac9b515dde497ab Mon Sep 17 00:00:00 2001 From: Lazizbek Ergashev Date: Fri, 28 Aug 2026 21:11:56 +0500 Subject: [PATCH 2/2] ci: pin the dependency lock guard with a regression test --- .github/workflows/ci.yml | 9 +++++---- tests/test_uv_workflows.py | 13 +++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5e8885bfc..ef7e885af9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,10 +75,11 @@ jobs: !cancelled() && (github.event_name == 'push' || github.event_name == 'merge_group' || github.event.pull_request.head.repo.fork) steps: + # A skipped required check counts as satisfied, so refuse here instead of skipping. - name: Require fresh dependency locks if: needs.dependency-locks.result != 'success' run: | - echo 'The dependency lock freshness job did not succeed.' >&2 + echo 'Refuse to run without verified dependency locks.' >&2 exit 1 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -113,7 +114,7 @@ jobs: - name: Require fresh dependency locks if: needs.dependency-locks.result != 'success' run: | - echo 'The dependency lock freshness job did not succeed.' >&2 + echo 'Refuse to run without verified dependency locks.' >&2 exit 1 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -176,7 +177,7 @@ jobs: - name: Require fresh dependency locks if: needs.dependency-locks.result != 'success' run: | - echo 'The dependency lock freshness job did not succeed.' >&2 + echo 'Refuse to run without verified dependency locks.' >&2 exit 1 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -216,7 +217,7 @@ jobs: - name: Require fresh dependency locks if: needs.dependency-locks.result != 'success' run: | - echo 'The dependency lock freshness job did not succeed.' >&2 + echo 'Refuse to run without verified dependency locks.' >&2 exit 1 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/tests/test_uv_workflows.py b/tests/test_uv_workflows.py index 779777dabd..18a7bceb9d 100644 --- a/tests/test_uv_workflows.py +++ b/tests/test_uv_workflows.py @@ -1005,6 +1005,19 @@ def test_untrusted_provenance_leaves_no_dependency_install_reachable(tmp_path: P } == installers +def test_required_checks_fail_when_dependency_provenance_fails() -> None: + jobs = dependency_workflow_jobs() + for name in ("lint", "build", "test", "test-httpx2"): + job = jobs[name] + assert re.search(r"^ needs:\s*dependency-locks\s*$", job, re.MULTILINE) + assert re.search(r"^ if: >-\n !cancelled\(\)\n", job, re.MULTILINE) + + guard = job.split(" steps:\n", 1)[1].split("\n\n", 1)[0] + assert "if: needs.dependency-locks.result != 'success'" in guard + assert "exit 1" in guard + assert "uses:" not in guard + + def test_scheduled_compatibility_keeps_dependency_provenance_gate() -> None: jobs = dependency_workflow_jobs() assert not re.search(r"^ if:.*schedule", jobs["dependency-locks"], re.MULTILINE)