Compare commits

...
Author SHA1 Message Date
JMR-dev 1535b61a96 Merge branch 'main' into fix/sdkmanager-pipefail 2026-08-25 09:02:54 -05:00
Jason Ross 2efd1f9a0d Merge pull request #95 from JMR-dev/docs/readme-restart-claim
Say which conversions come back, rather than that they all do
2026-08-25 09:02:23 -05:00
JMR-dev 5a8aedf53d Read sdkmanager's status, not the status of the yes feeding it
run-e2e.sh installs a missing system image with

  yes | sdkmanager --install "$pkg" > /dev/null 2>&1 || { echo "  FAILED to install"; ... }

`yes` never ends. The moment sdkmanager exits and closes the pipe, `yes` dies of SIGPIPE
with 141, and this script runs under `pipefail`, which takes the rightmost non-zero status.
So a package that installed perfectly reported "FAILED to install $pkg" and returned 1.

R32 filed this PLAUSIBLE on shell semantics, unexecuted. It is demonstrated now:

  set -o pipefail; yes | true             -> 141   (three runs, three times)
  set -o pipefail; yes | sh -c 'exit 3'   -> 3
  ${PIPESTATUS[1]} for those two          -> 0 and 3

The pipeline status genuinely cannot tell a clean install from a broken one; PIPESTATUS
can. That is the whole change -- no restructuring of the licence flow, so a fresh SDK still
gets its licences accepted exactly as before.

`echo no | avdmanager` eleven lines below is deliberately left alone, and the comment says
so. One line fits the pipe buffer, so echo has already exited before the close and there is
no signal to receive: `echo no | true` measured 0 on five consecutive runs against `yes |
true`'s 141 on three. Only an unbounded producer is exposed. Someone reading this fix later
would otherwise "fix" the echo too and change a line that was never wrong.

Why it went unnoticed: it only misfires when the image is ABSENT, and every existing
checkout already has the images. R32 noted the branch that made this the normal path. The
failure is also silent in the worst way -- the install succeeds, the script says it failed,
and the AVD is then created from a package that is really there.

shellcheck clean at the pinned digest (0.11.0, the version CI runs), bash -n clean.

Closes #41.
2026-08-25 00:16:27 -05:00
2 changed files with 16 additions and 2 deletions
View File
+16 -2
View File
@@ -236,10 +236,24 @@ ensure_avd() {
else
if [ ! -d "$img_dir" ]; then
echo " installing $pkg"
yes | sdkmanager --install "$pkg" > /dev/null 2>&1 || {
# Read sdkmanager's own status, not the pipeline's. `yes` never ends, so the moment
# sdkmanager exits and closes the pipe, `yes` dies of SIGPIPE with 141 -- and this
# script runs under `pipefail`, which takes the rightmost NON-ZERO status. A package
# that installed perfectly therefore reported "FAILED to install".
#
# Measured rather than reasoned: under `set -o pipefail`, `yes | true` exits 141 on
# every run, and `yes | sh -c 'exit 3'` exits 3 -- so the pipeline status cannot tell
# a clean install from a broken one, while ${PIPESTATUS[1]} reports 0 and 3.
#
# The `echo no | avdmanager` below is deliberately NOT changed. One line fits the pipe
# buffer, so echo has already exited before the close and there is no signal to
# receive; `echo no | true` measured 0 on every run. Only an unbounded producer is
# exposed to this.
yes | sdkmanager --install "$pkg" > /dev/null 2>&1
if [ "${PIPESTATUS[1]}" -ne 0 ]; then
echo " FAILED to install $pkg"
return 1
}
fi
fi
echo " creating AVD $avd from $pkg"
echo no | avdmanager create avd -n "$avd" -k "$pkg" -d pixel_6 --force > /dev/null 2>&1 || {