Fix the rebundle step: a non-matching grep must not kill it #130

Merged
bvandeusen merged 1 commits from dev into main 2026-09-10 10:49:40 -04:00
Owner

Repairs the main build broken by PR #129. CI green on 88508b53.

What broke

The first main build after the version rework failed on "Bundle latest release APK". :latest was never moved — "Build and push" was skipped — so nothing reached production, but every subsequent main push would have failed identically.

The runner invokes shell: bash as bash -e -o pipefail. Under pipefail a command substitution reports the first non-zero status in its pipeline, not the last, so a grep matching nothing kills the assignment even though head succeeded. The step died at the assignment, before reaching the if written to handle the empty case.

The case it died on is the one PR #129 described and then failed to handle in code: v2026.09.09 predates sidecar assets, so its .apk.version grep matched nothing.

The worse thing the audit found

The other two assignments in that step carried the same hazard and had simply never fired, because a release always has a tag_name and an .apk asset. So the step's documented promise — "degrades to an empty client/ (404 update channel) — never a wrong version — if no release or APK asset can be resolved" — was never reachable under pipefail. That predates this batch entirely.

All three now carry || true.

Verification

Reproduced under the runner's exact shell before fixing: without || true the script exits 1 with no output at all, proving it never reaches the branch; with it, the fallback runs and emits the name-only sidecar.

Guarded, because the degradation depends on it and the failure only surfaces on the one release that triggers it — the new test asserts every command-substitution grep in that step ends with || true, falsified by removing it from the sidecar line.

Unrelated, and not fixed here

Run 6270 (android lane) failed separately with lstat /root/.cache/act/…/jest.config.ts: no such file or directory — a corrupted actions/cache copy on the runner host, failing 60ms in on a file that does not exist in this repo. Runner infrastructure, not code.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SQ31KQpYbStyK5y58UmPLH

Repairs the `main` build broken by PR #129. CI green on `88508b53`. ## What broke The first `main` build after the version rework failed on "Bundle latest release APK". `:latest` was never moved — "Build and push" was skipped — so nothing reached production, but every subsequent `main` push would have failed identically. The runner invokes `shell: bash` as `bash -e -o pipefail`. Under pipefail a command substitution reports the **first** non-zero status in its pipeline, not the last, so a grep matching nothing kills the assignment even though `head` succeeded. The step died *at the assignment*, before reaching the `if` written to handle the empty case. The case it died on is the one PR #129 described and then failed to handle in code: `v2026.09.09` predates sidecar assets, so its `.apk.version` grep matched nothing. ## The worse thing the audit found The other two assignments in that step carried the same hazard and had simply never fired, because a release always has a `tag_name` and an `.apk` asset. So the step's documented promise — *"degrades to an empty client/ (404 update channel) — never a wrong version — if no release or APK asset can be resolved"* — was **never reachable** under pipefail. That predates this batch entirely. All three now carry `|| true`. ## Verification Reproduced under the runner's exact shell before fixing: without `|| true` the script exits 1 with no output at all, proving it never reaches the branch; with it, the fallback runs and emits the name-only sidecar. Guarded, because the degradation depends on it and the failure only surfaces on the one release that triggers it — the new test asserts every command-substitution grep in that step ends with `|| true`, falsified by removing it from the sidecar line. ## Unrelated, and not fixed here Run 6270 (android lane) failed separately with `lstat /root/.cache/act/…/jest.config.ts: no such file or directory` — a corrupted `actions/cache` copy on the runner host, failing 60ms in on a file that does not exist in this repo. Runner infrastructure, not code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01SQ31KQpYbStyK5y58UmPLH
bvandeusen added 1 commit 2026-09-10 10:49:33 -04:00
fix(release): a non-matching grep must not kill the rebundle step
test-go / test (push) Successful in 1m39s
release / Build signed APK (releases and dev) (push) Successful in 6m11s
release / Build + push container image (push) Successful in 14s
release / Verify release artifacts (tag releases only) (push) Skipped
test-go / integration (push) Successful in 5m59s
88508b536b
The first `main` build after the version rework failed, and the bug was
mine. :latest was never moved — "Build and push" was skipped — so nothing
reached production, but every subsequent main push would have failed the
same way.

The runner invokes `shell: bash` as `bash -e -o pipefail`. Under pipefail a
command substitution reports the FIRST non-zero status in its pipeline, not
the last, so

  VAR="$(printf ... | grep -oP ... | grep -E '\.apk\.version$' | head -1)"

exits non-zero when that grep matches nothing, even though `head` succeeded.
With -e the step dies AT THE ASSIGNMENT — before reaching the `if` written
to handle exactly the empty case.

Which is what happened: v2026.09.09 predates sidecar assets, so its
`.apk.version` grep matched nothing and the step aborted instead of falling
through to the name-only branch I added in 9f3e0b8c for precisely that
release. The transition case was described correctly in that commit message
and then not handled in code.

The other two assignments carried the same latent hazard and had simply
never fired, because a release always has a tag_name and an .apk asset. So
the step's documented promise — "degrades to an empty client/ (404 update
channel) — never a wrong version — if no release or APK asset can be
resolved" — was never actually reachable under pipefail. All three now
carry `|| true`.

Reproduced under the runner's exact shell before fixing: without `|| true`
the script exits 1 with no output at all, proving it never reaches the
branch; with it, the fallback runs and emits the name-only sidecar.

Guarded, since the graceful degradation depends on this and the failure is
invisible until the one release that triggers it: the new test asserts every
command-substitution grep in that step ends with `|| true`, and was
falsified by removing it from the sidecar line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SQ31KQpYbStyK5y58UmPLH
bvandeusen merged commit 439c8625d5 into main 2026-09-10 10:49:40 -04:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/minstrel#130