Skip to content

CI: resolve sdkmanager properly — the emulator pre-install never worked - #519

Merged
bernardladenthin merged 1 commit into
mainfrom
local/fix-emulator-sdkmanager
Oct 11, 2026
Merged

bernardladenthin merged 1 commit into
mainfrom
local/fix-emulator-sdkmanager

Conversation

@bernardladenthin

Copy link
Copy Markdown
Owner

The Android emulator jobs have been red in every run since the pre-install step was added, and the
step's own error message is what kept the cause hidden.

What actually happens

sdkmanager: command not found              ← four times
sdkmanager --install emulator failed (attempt 1 of 4) … (4 of 4)
::error::the Android emulator package could not be installed in 4 attempts
         (dl.google.com download or archive broken)

sdkmanager is not on PATH on the hosted runner: the SDK sits at $ANDROID_HOME and the command
lives under cmdline-tools/<version>/bin. The step called it bare, so it retried an impossibility
four times, slept 20 + 40 + 60 seconds in between, and then reported a cause that had nothing to do
with it.

Two consequences worth naming

The step was added to make a corrupt-archive failure diagnosable, and instead made the job
permanently red.
Before it, the emulator jobs failed only when dl.google.com served a bad archive
(run 38069595101, "Error on ZipFile unknown archive"). Since it, they fail always — runs
38146809329, 38148401395 and 38149667195.

The misleading message is what kept that invisible. A reader who sees "download or archive broken"
waits for Google to recover rather than looking at the step. A wrong cause is worse than no message.

The fix

Resolve the command — PATH, then $ANDROID_HOME, $ANDROID_SDK_ROOT and the image's
/usr/local/lib/android/sdk, taking the newest cmdline-tools version — and keep the two failure
modes apart
:

  • not found → "this is a runner-image change, not a download problem", naming every location searched;
  • install failed → the original message, now also naming the binary it used.

Both copies of the step (the AAR emulator job and the LLM Service UI job) are fixed identically.

What this does not prove

The emulator jobs have not been green since the step was added, so the corrupt archive it was built to
survive remains unverified. Recorded in TODO.md rather than claimed — the entry there ("the
emulator package pre-install is unproven"
) is answered with what was wrong, not closed as working.

Not in scope

The two red Verify GPG signing key jobs in the same run are expected on a pull request: they need
the maven-central environment secret, which a PR does not get. They failed for the same reason in run
38090550713.

Verification

check-run-scripts.py 160 scripts clean (it parses every run: block with bash -n, which is what
would catch a syntax error in the new resolution loop), check-release-gate.py 0 violations,
check-natives.py 0 disagreements, check-versions.py 0 differences against the three sibling repos,
114 buildcheck tests green. Rebased onto main after Dependabot's #516–#518.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E2h8gXyyE5UeimkL9vQv9G

The step that pre-installs the Android emulator package calls sdkmanager bare, and it is not on PATH on the hosted runner: the SDK sits at ANDROID_HOME with the command under cmdline-tools/<version>/bin. So it failed four times with sdkmanager: command not found, slept 20 plus 40 plus 60 seconds in between, and then printed that the package could not be installed because the dl.google.com download or archive was broken -- a cause with nothing to do with it.

Two consequences worth naming. The step was added to make a corrupt-archive failure diagnosable, and instead it converted a job that was only occasionally flaky into one that is always red: the Android emulator jobs have failed in every run since, 38146809329, 38148401395 and 38149667195. And the misleading message is what kept that invisible -- a reader sees a download problem at Google and waits for it to pass rather than looking at the step.

Fixed by resolving the command (PATH first, then ANDROID_HOME, ANDROID_SDK_ROOT and the image's /usr/local/lib/android/sdk, taking the newest cmdline-tools version) and by keeping the two failure modes apart: not-found now says it is a runner-image change and not a download problem, and the download failure names the binary it used. Both copies of the step are fixed identically.

What this does not prove: the emulator jobs have not been green since the step was added, so the corrupt archive it was built to survive is still unverified. Recorded in TODO.md rather than claimed.

check-run-scripts.py 160 scripts clean, check-release-gate.py 0 violations, check-natives.py 0 disagreements.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01E2h8gXyyE5UeimkL9vQv9G
@bernardladenthin
bernardladenthin merged commit 72750f6 into main Oct 11, 2026
9 of 14 checks passed
@bernardladenthin
bernardladenthin deleted the local/fix-emulator-sdkmanager branch October 11, 2026 16:23
@sonarqubecloud

Copy link
Copy Markdown

vaiju1981 pushed a commit to vaiju1981/java-llama.cpp that referenced this pull request Oct 12, 2026
…t is not

My fix in bernardladenthin#519 was off by one. It searched with find -maxdepth 2 under cmdline-tools, but the file sits at cmdline-tools/latest/bin/sdkmanager, three levels down -- so the search stopped at the bin directory and reported the file missing while it was there. Run 38164077038 said so in one line, which is what that PR's other half bought: the message named the right cause, so the real defect took one look instead of another suspicion about dl.google.com.

This replaces the find with the three known layouts in order -- cmdline-tools/latest/bin, cmdline-tools/<version>/bin and the old tools/bin -- which is both correct and readable, and cannot be off by one again. The outer loop over ANDROID_HOME, ANDROID_SDK_ROOT and the image path now stops once one answers: the inner break leaves only the inner loop, so a later root could otherwise overwrite the found path. That guard is an if rather than a trailing test-and-break, which under set -e would end the step the moment the test is false.

And the part bernardladenthin#519 still lacked: when nothing is found, the step now lists what IS under each root. Naming only the places searched leaves three different causes -- no directory, no version subdirectory, no file -- behind one message, and a reader cannot tell them apart. The listing loop deliberately has no early break, so it reports every root.

Validated locally against five faked layouts: latest (what CI has), a version directory without latest, the old tools/bin, an empty SDK that must fail, and a first root that is empty with the second carrying the file. Both copies of the step are fixed identically; check-run-scripts.py parses all 161 scripts, the release gate and the natives list are unchanged.

A trap recorded in TODO.md for whoever tests such a step on this checkout: chmod +x does not stick on a plain file here, so [ -x ] answers false and the first run of that probe called all five layouts broken -- including the one that demonstrably works in CI. The fixtures needed a shebang; it was the harness, not the logic.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01E2h8gXyyE5UeimkL9vQv9G

This branch had an error being deployed

1 failed deployment
maven-central — 91792909 Deployed Oct 11, 2026 by bernardladenthin via Verify GPG signing key (no secrets printed) #1214
startgate — 91792909 Deployed Oct 11, 2026 by bernardladenthin via Start gate (abort window) #1214
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