Skip to content

Name the end of the pipeline that actually broke - #176

Merged
frangarcj merged 1 commit into
masterfrom
next-pipeline-blame
Aug 28, 2026
Merged

Name the end of the pipeline that actually broke#176
frangarcj merged 1 commit into
masterfrom
next-pipeline-blame

Conversation

@frangarcj

Copy link
Copy Markdown
Contributor

Windows failed twice this month at the same line of create-core-package.sh, and both logs said the same thing:

bin/makepkg.conf: Write error: Broken pipe

That is bsdtar, naming whatever file it happened to be reading when its output closed. The file was fine. What broke was the other end — most likely xz, killed for memory during a silent three-minute -9 over the core payload — and a process the kernel kills leaves no message at all, so nothing in the log was ever about it.

Under pipefail the failing status surfaces. The text does not: it belongs to whichever element noticed first, and that is never the one that died.

What changes

Both compression pipelines — the .MTREE one through gzip and the package one through xz — capture PIPESTATUS and hand it to report_pipeline, which names every element that failed and reads a status above 128 as the signal it is.

The status the caller sees is unchanged. report_pipeline returns the last failing one, which is exactly what pipefail would have handed up, so anything reading 137 out of this keeps reading 137. It says more, not something else.

Simulated

An xz on PATH that dies mid-write, against tests/package/test-core-package.sh in ubuntu:24.04:

output exit
master : Write error, plus a job-control dump 137
this branch packaging vdpm-…pkg.tar.xz: bsdtar exited 1
packaging vdpm-…pkg.tar.xz: xz killed by signal 9
137

The first poison I wrote drained its input before dying, which let bsdtar finish and produced a different signature — worth saying, because the faithful one has to die while bsdtar is still writing, which is what an OOM kill does.

Test

tests/ci/test-pipeline-report.sh drives the function the script defines, extracted with sed the way test-host-runner.sh already does, rather than a copy of it:

  • nothing failed → says nothing, succeeds
  • the compressor killed for memory → named, with signal 9
  • the upstream elements that took the SIGPIPE → reported too, so the consequence is visible alongside the cause
  • an ordinary non-zero exit → reported as an exit, not as a signal
  • an element past the names given → still reported, by position
  • the last failing status → preserved

One thing worth noting

tests/ci/test-empty-array-expansion.sh caught this change before it ran anywhere:

these expansions die on macOS bash 3.2 when the array is empty;
write ${name[@]+"${name[@]}"} instead:

scripts/create-core-package.sh:213:  statuses=("${PIPESTATUS[@]}")

PIPESTATUS is never empty after a pipeline, so this would not have failed in practice — but the rule was made mechanical on purpose after #168, and the guard doing its job on new code is the point of it. Fixed rather than exempted.

All 10 CI tests pass; tests/package/test-bootstrap-archive.sh and the packaging half of test-core-package.sh pass in a Linux container (its pacman step needs a docker this one does not have).


AI tools were used in preparing this PR (Claude Opus 5, Anthropic).

Windows failed twice this month at the same line, and both logs said the
same thing:

    bin/makepkg.conf: Write error: Broken pipe

That is bsdtar, naming whatever file it happened to be reading when its
output closed. The file was fine. What broke was the other end -- most
likely xz, killed for memory during a silent three-minute -9 over the
core payload -- and a process the kernel kills leaves no message at all,
so nothing in the log was about it.

Under pipefail the failing status does surface; the text does not,
because it belongs to whichever element noticed first, and that is never
the one that died. So both compression pipelines now capture PIPESTATUS
and report every element that failed, by name, and a status above 128 as
the signal it is. The status the caller sees is unchanged: report_pipeline
returns the last failing one, which is what pipefail would have given it.

Simulated by putting an xz on PATH that dies mid-write, against
tests/package/test-core-package.sh:

    before   : Write error   (and a job-control dump)
    after    packaging vdpm-…pkg.tar.xz: bsdtar exited 1
             packaging vdpm-…pkg.tar.xz: xz killed by signal 9

both exiting 137.

tests/ci/test-pipeline-report.sh drives the function the script defines,
not a copy: silence when nothing failed, the killed compressor with its
signal, the upstream SIGPIPE reported too, an ordinary exit as an exit,
an element past the names given, and the status preserved.
@frangarcj
frangarcj force-pushed the next-pipeline-blame branch from 9a738f3 to 9bfacbc Compare August 28, 2026 10:43
@frangarcj
frangarcj merged commit dda49cc into master Aug 28, 2026
17 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant