From 79dcb81c424349febd0145047a34ce38d39fd5eb Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 2 Sep 2026 15:30:49 -0700 Subject: [PATCH 1/2] fix(ci): compare requirements.txt drift against git, not a scratch export MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guard added in #966 exports to /tmp/requirements.check.txt and diffs it against the checked-in file. uv stamps the -o path it was given into the export's header comment, so the two files differ on line 2 no matter how fresh the checked-in export is: -# uv export --no-hashes -o requirements.txt +# uv export --no-hashes -o /tmp/requirements.check.txt Every lane that has a requirements.txt fails: examples/ag-ui e2e and the six cockpit/ag-ui cockpit e2e caps. Regenerate in place with the exact command the error message tells you to run, and let `git diff --exit-code` decide — that compares against the committed file, so real drift is still caught. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ec1bc43d..7053ee5b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -461,8 +461,11 @@ jobs: - name: Check requirements.txt matches uv.lock working-directory: examples/ag-ui/python run: | - uv export --no-hashes -o /tmp/requirements.check.txt - if ! diff -u requirements.txt /tmp/requirements.check.txt; then + # Regenerate in place with the exact documented command. uv stamps the + # -o path into the file's header, so exporting to a scratch path never + # matches a checked-in export no matter how fresh it is. + uv export --no-hashes -o requirements.txt + if ! git diff --exit-code -- requirements.txt; then echo "::error::examples/ag-ui/python/requirements.txt is stale — the Railway image installs from this file, not uv.lock. Run 'uv export --no-hashes -o requirements.txt' in examples/ag-ui/python and commit the result." exit 1 fi @@ -567,8 +570,10 @@ jobs: echo "No requirements.txt in ${{ matrix.cap.python }} — skipping drift check." exit 0 fi - uv export --no-hashes -o /tmp/requirements.check.txt - if ! diff -u requirements.txt /tmp/requirements.check.txt; then + # Regenerate in place with the exact documented command; see the + # examples/ag-ui lane above for why a scratch-path export can't match. + uv export --no-hashes -o requirements.txt + if ! git diff --exit-code -- requirements.txt; then echo "::error::${{ matrix.cap.python }}/requirements.txt is stale relative to uv.lock — deployments/ag-ui-dev is generated from this file. Run 'uv export --no-hashes -o requirements.txt' in ${{ matrix.cap.python }} and commit the result." exit 1 fi From 653b02ea27ff20acfed867f5a3fa868a03c49695 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 2 Sep 2026 15:39:57 -0700 Subject: [PATCH 2/2] fix(ci): honour what each requirements.txt actually is MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the header fix in this branch. Two more ways the guard disagreed with reality: - Three lanes export with --no-dev (their header records it), but the guard always ran a bare --no-hashes export, so it demanded that pytest and pytest-asyncio be committed into the files the Railway images install from. - cockpit/langgraph/streaming/python/requirements.txt is hand-maintained loose pins, not a uv export at all; the guard would have replaced it wholesale with a 257-line resolved export. The guard now reads the command uv stamps into each file's own header: it skips files that aren't uv exports, and mirrors --no-dev when the header used it. aws-strands and microsoft-agent-framework are re-exported so their headers record the --no-dev they were actually built with. Content is byte-identical — the change is one header line each, no package added or removed from any image. Verified across all 12 lanes that have a requirements.txt: 9 clean, 1 skipped as hand-maintained, 2 corrected here. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 37 +++++++++++++++---- .../aws-strands/python/requirements.txt | 2 +- .../python/requirements.txt | 2 +- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7053ee5b7..d196c25b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -461,10 +461,21 @@ jobs: - name: Check requirements.txt matches uv.lock working-directory: examples/ag-ui/python run: | - # Regenerate in place with the exact documented command. uv stamps the - # -o path into the file's header, so exporting to a scratch path never - # matches a checked-in export no matter how fresh it is. - uv export --no-hashes -o requirements.txt + # requirements.txt is what the Dockerfile installs from; uv.lock is what + # dependency bumps actually update. Regenerate in place with the very + # command the file records in its own header — uv stamps both the flags + # and the -o path there, so exporting to a scratch path or dropping + # --no-dev can never match a correctly-generated file — and let + # `git diff` compare against the committed version. + if ! head -1 requirements.txt | grep -q 'autogenerated by uv'; then + echo "examples/ag-ui/python/requirements.txt is hand-maintained, not a uv export — skipping drift check." + exit 0 + fi + flags="--no-hashes" + if sed -n 2p requirements.txt | grep -q -- '--no-dev'; then + flags="$flags --no-dev" + fi + uv export $flags -o requirements.txt if ! git diff --exit-code -- requirements.txt; then echo "::error::examples/ag-ui/python/requirements.txt is stale — the Railway image installs from this file, not uv.lock. Run 'uv export --no-hashes -o requirements.txt' in examples/ag-ui/python and commit the result." exit 1 @@ -570,9 +581,21 @@ jobs: echo "No requirements.txt in ${{ matrix.cap.python }} — skipping drift check." exit 0 fi - # Regenerate in place with the exact documented command; see the - # examples/ag-ui lane above for why a scratch-path export can't match. - uv export --no-hashes -o requirements.txt + # requirements.txt is what the Dockerfile installs from; uv.lock is what + # dependency bumps actually update. Regenerate in place with the very + # command the file records in its own header — uv stamps both the flags + # and the -o path there, so exporting to a scratch path or dropping + # --no-dev can never match a correctly-generated file — and let + # `git diff` compare against the committed version. + if ! head -1 requirements.txt | grep -q 'autogenerated by uv'; then + echo "${{ matrix.cap.python }}/requirements.txt is hand-maintained, not a uv export — skipping drift check." + exit 0 + fi + flags="--no-hashes" + if sed -n 2p requirements.txt | grep -q -- '--no-dev'; then + flags="$flags --no-dev" + fi + uv export $flags -o requirements.txt if ! git diff --exit-code -- requirements.txt; then echo "::error::${{ matrix.cap.python }}/requirements.txt is stale relative to uv.lock — deployments/ag-ui-dev is generated from this file. Run 'uv export --no-hashes -o requirements.txt' in ${{ matrix.cap.python }} and commit the result." exit 1 diff --git a/cockpit/runtimes/aws-strands/python/requirements.txt b/cockpit/runtimes/aws-strands/python/requirements.txt index e1319fb6d..0a960769d 100644 --- a/cockpit/runtimes/aws-strands/python/requirements.txt +++ b/cockpit/runtimes/aws-strands/python/requirements.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv export --no-hashes -o requirements.txt +# uv export --no-hashes --no-dev -o requirements.txt -e . ag-ui-a2ui-toolkit==0.0.4 # via ag-ui-strands diff --git a/cockpit/runtimes/microsoft-agent-framework/python/requirements.txt b/cockpit/runtimes/microsoft-agent-framework/python/requirements.txt index 8d81d664e..5ae305ddc 100644 --- a/cockpit/runtimes/microsoft-agent-framework/python/requirements.txt +++ b/cockpit/runtimes/microsoft-agent-framework/python/requirements.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv export --no-hashes -o requirements.txt +# uv export --no-hashes --no-dev -o requirements.txt -e . ag-ui-protocol==0.1.21 # via agent-framework-ag-ui