From c4d96d7c4d4550fc6824dd8ef7b74f8171f20ce2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 9 Oct 2026 00:44:53 +0000 Subject: [PATCH] Unpack e2e binaries without piping into tar The e2e legs unpacked their binaries with `zstd -d -c ... | tar -xf -` under pipefail. A tar that exits at the end-of-archive marker without draining stdin leaves zstd writing into a closed pipe; zstd then fails with "Broken pipe" and the leg goes red even though every file was extracted. That happened on macOS in merge_group run 37863817362 and evicted #1152 from the merge queue 10 minutes after #1179 introduced the archive. Decompress to a file and extract that instead, so no reader can close the pipe early. Add a test that feeds each consumer an archive with bytes after the end marker; it fails on the old pipe (GNU tar stops reading there too) and passes now. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01QNubt2ZznwzckQai6WxCLa --- .github/workflows/ci.yml | 16 ++++++++++++++-- scripts/tests/test_ci_e2e_archive.py | 24 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7c0a1e20..886b730dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1317,10 +1317,16 @@ jobs: - name: Unpack the e2e binaries shell: bash + # Decompress to a file, not into `| tar -xf -`: a tar that exits at + # the end-of-archive marker without draining stdin (macOS's does) + # leaves zstd writing into a closed pipe, and pipefail turns its + # "Broken pipe" into a failed leg that evicts the queue entry. run: | set -euo pipefail mkdir -p target/e2e-bin - zstd -q -d -c target/e2e-archive/e2e-bin.tar.zst | tar -xf - -C target/e2e-bin + zstd -q -d -f target/e2e-archive/e2e-bin.tar.zst -o target/e2e-bin.tar + tar -xf target/e2e-bin.tar -C target/e2e-bin + rm target/e2e-bin.tar - name: Stage the CLI where the test binaries expect it # `CARGO_BIN_EXE_socket-patch` was baked in at compile time as @@ -2165,10 +2171,16 @@ jobs: path: target/e2e-archive - name: Unpack the e2e binaries shell: bash + # Decompress to a file, not into `| tar -xf -`: a tar that exits at + # the end-of-archive marker without draining stdin (macOS's does) + # leaves zstd writing into a closed pipe, and pipefail turns its + # "Broken pipe" into a failed leg that evicts the queue entry. run: | set -euo pipefail mkdir -p target/e2e-bin - zstd -q -d -c target/e2e-archive/e2e-bin.tar.zst | tar -xf - -C target/e2e-bin + zstd -q -d -f target/e2e-archive/e2e-bin.tar.zst -o target/e2e-bin.tar + tar -xf target/e2e-bin.tar -C target/e2e-bin + rm target/e2e-bin.tar - name: Install Rust run: rustup show - name: Install the cargo under test diff --git a/scripts/tests/test_ci_e2e_archive.py b/scripts/tests/test_ci_e2e_archive.py index 1cae12028..3ebbbc5d4 100644 --- a/scripts/tests/test_ci_e2e_archive.py +++ b/scripts/tests/test_ci_e2e_archive.py @@ -1,12 +1,14 @@ """Run CI's real archive commands and check binary/permission round trips.""" import importlib.util +import io import os from pathlib import Path import re import shutil import stat import subprocess +import tarfile import tempfile import textwrap import unittest @@ -86,6 +88,28 @@ def test_missing_bundle_fails_even_when_compressor_accepts_empty_input(self): (work / "target").mkdir() self.assertNotEqual(run(compressor, work).returncode, 0) + def test_consumer_survives_a_tar_that_stops_at_the_end_marker(self): + # Some tars (macOS's; GNU tar too) exit at the end-of-archive marker + # without reading the rest of stdin. Bytes after the marker made a + # `zstd -d -c | tar -xf -` consumer fail with "Broken pipe". + payload = b"\x7fELF\x00cli fixture\xff" + for consumer in commands("Unpack the e2e binaries"): + with tempfile.TemporaryDirectory(prefix="e2e-trailing-") as directory: + work = Path(directory) + tar = work / "bundle.tar" + with tarfile.open(tar, "w") as bundle: + info = tarfile.TarInfo("socket-patch") + info.size, info.mode = len(payload), 0o755 + bundle.addfile(info, io.BytesIO(payload)) + with tar.open("ab") as f: + f.write(bytes(2 * 1024 * 1024)) + archive_dir = work / "target/e2e-archive" + archive_dir.mkdir(parents=True) + subprocess.run(["zstd", "-q", str(tar), "-o", str(archive_dir / "e2e-bin.tar.zst")], check=True) + result = run(consumer, work) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual((work / "target/e2e-bin/socket-patch").read_bytes(), payload) + def test_corrupt_archive_fails_the_consumer_step(self): for consumer in commands("Unpack the e2e binaries"): with tempfile.TemporaryDirectory(prefix="e2e-bad-archive-") as directory: