feat(android-local): vendored Docker build for APK with no host SDK #163

Merged
forgejo_admin merged 2 commits from feat/local-apk-build into master 2026-05-20 15:10:28 +00:00

Summary

Adds a self-contained local APK build path. Only Docker is required on the host — no JDK, no Android SDK, no Node. Mirrors .github/workflows/build-android.yml inside a vendored image so anyone who clones the repo can build the APK with one command.

Motivation: previously the only fast local path was to install forgejo-stack and reuse its forgejo-stack/job-android:latest image, or to hand-install Android Studio + JDK 21. This PR removes that coupling.

Changes

  • docker/android-builder/Dockerfilenode:22-bookworm-slim base + Temurin JDK 21 + Android cmdline-tools 11076708 + platforms;android-35 + build-tools;35.0.0 and 34.0.0. The 34.0.0 build-tools is AGP 8.7.x's silent internal minimum; CI tolerates a missing 34.0.0 only because the Forgejo runner executes as root with a writable SDK dir. Local script runs as the host uid:gid, so we pre-install both.
  • scripts/build-apk-local.sh — orchestrator. Builds the image on first run, then runs the container as the host uid:gid with the repo bind-mounted and three repo-local cache dirs for Gradle / yarn / HOME. Uses --network host on both build and run because the default Docker bridge fails apt + adoptium downloads on hosts that share interfaces with co-resident bridges (e.g. forgejo-stack/internal).
  • docs/android-apk.md — splits Prerequisites into Docker (Path A, recommended) and Native (Path B), adds a Local Docker build section with the script's commands, timing table, and a pointer to the Dockerfile.
  • .gitignore — adds .docker-cache/ (repo-local Gradle + yarn caches).

Test plan

  • Cold ./scripts/build-apk-local.sh on a host with no Android SDK, no JDK, no Node — only Docker. Produces android/app/build/outputs/apk/debug/app-debug.apk (4.6 MB, valid Android package per file).
  • Warm Gradle re-run after first build: 1m 13s wall-clock (matches the timing table in the doc).
  • --clean removes .docker-cache/; --rebuild forces image rebuild.
  • Output APK is user-owned (not root-owned) thanks to --user flag.
  • (Not in scope) Test on macOS / Windows Docker Desktop — fix verified on Linux only. --network host is supported on Docker Desktop 4.34+; older versions may need a different network mode.

See the postmortem at forgejo-stack docs/postmortems/2026-05-17-android-apk-ci.md for context on the JDK 21 / build-tools 34.0.0 / network requirements.

## Summary Adds a self-contained local APK build path. Only Docker is required on the host — no JDK, no Android SDK, no Node. Mirrors `.github/workflows/build-android.yml` inside a vendored image so anyone who clones the repo can build the APK with one command. Motivation: previously the only fast local path was to install forgejo-stack and reuse its `forgejo-stack/job-android:latest` image, or to hand-install Android Studio + JDK 21. This PR removes that coupling. ## Changes - `docker/android-builder/Dockerfile` — `node:22-bookworm-slim` base + Temurin JDK 21 + Android cmdline-tools 11076708 + `platforms;android-35` + `build-tools;35.0.0` and `34.0.0`. The 34.0.0 build-tools is AGP 8.7.x's silent internal minimum; CI tolerates a missing 34.0.0 only because the Forgejo runner executes as root with a writable SDK dir. Local script runs as the host uid:gid, so we pre-install both. - `scripts/build-apk-local.sh` — orchestrator. Builds the image on first run, then runs the container as the host uid:gid with the repo bind-mounted and three repo-local cache dirs for Gradle / yarn / HOME. Uses `--network host` on both build and run because the default Docker bridge fails apt + adoptium downloads on hosts that share interfaces with co-resident bridges (e.g. forgejo-stack/internal). - `docs/android-apk.md` — splits Prerequisites into Docker (Path A, recommended) and Native (Path B), adds a `Local Docker build` section with the script's commands, timing table, and a pointer to the Dockerfile. - `.gitignore` — adds `.docker-cache/` (repo-local Gradle + yarn caches). ## Test plan - [x] Cold `./scripts/build-apk-local.sh` on a host with no Android SDK, no JDK, no Node — only Docker. Produces `android/app/build/outputs/apk/debug/app-debug.apk` (4.6 MB, valid `Android package` per `file`). - [x] Warm Gradle re-run after first build: 1m 13s wall-clock (matches the timing table in the doc). - [x] `--clean` removes `.docker-cache/`; `--rebuild` forces image rebuild. - [x] Output APK is user-owned (not root-owned) thanks to `--user` flag. - [ ] (Not in scope) Test on macOS / Windows Docker Desktop — fix verified on Linux only. `--network host` is supported on Docker Desktop 4.34+; older versions may need a different network mode. See the postmortem at forgejo-stack `docs/postmortems/2026-05-17-android-apk-ci.md` for context on the JDK 21 / build-tools 34.0.0 / network requirements.
forgejo_admin added 1 commit 2026-05-20 14:22:27 +00:00
feat(android-local): vendored Docker build for APK with no host SDK
All checks were successful
CI / Lint, Typecheck & Unit Tests (pull_request) Successful in 27s
CI / E2E (Standalone single-file) (pull_request) Successful in 1m11s
CI / E2E (Web) (pull_request) Successful in 2m15s
f86cd284e8
Adds a self-contained local APK build path that mirrors
.github/workflows/build-android.yml inside a Docker image we own.
Only Docker is required on the host — no JDK, no Android SDK, no Node.
Removes the previous hidden coupling between local builds and either
the forgejo-stack/job-android image or a hand-installed Android Studio.

- docker/android-builder/Dockerfile — node:22-bookworm-slim base +
  Temurin JDK 21 + Android cmdline-tools 11076708 + platforms;android-35
  + build-tools;35.0.0 and 34.0.0 (the latter is AGP 8.7.x's silent
  internal minimum; CI tolerates a missing 34.0.0 only because the
  Forgejo runner executes as root with a writable SDK dir).
- scripts/build-apk-local.sh — orchestrator that builds the image
  on first run, then runs the container as the host uid:gid with the
  repo bind-mounted and three repo-local cache dirs for Gradle/yarn/HOME.
  Uses --network host on both build and run because the default Docker
  bridge fails apt + adoptium downloads on hosts that share interfaces
  with co-resident bridges (e.g. forgejo-stack/internal).
- docs/android-apk.md — splits Prerequisites into Docker (Path A,
  recommended) and Native (Path B), adds a Local Docker build section
  with the script's commands, timing table, and a pointer to the
  Dockerfile.
- .gitignore — adds .docker-cache/ (repo-local Gradle + yarn caches).

Verified end-to-end: cold image build (~5 min on this host with
--network host vs. an apt-get hang on the default bridge), warm Gradle
build (1m 13s), produces a 4.6 MB app-debug.apk identical in structure
to CI's output (Android package with APK Signing Block + gradle
app-metadata.properties).
forgejo_admin added 1 commit 2026-05-20 15:00:17 +00:00
fixup(android-local): address PR #163 review findings
All checks were successful
CI / Lint, Typecheck & Unit Tests (pull_request) Successful in 25s
CI / E2E (Standalone single-file) (pull_request) Successful in 1m10s
CI / E2E (Web) (pull_request) Successful in 2m11s
f96710a1a6
P1.1 — JAVA_HOME: symlink temurin-21-jdk-<arch> to a canonical path so
the image works on arm64 (Apple Silicon Docker Desktop), not just amd64.
Pre-existing assumption in the forgejo-stack image is fine for CI (always
x86_64) but breaks local builds on M-series Macs.

P1.2 — Docs: step 1 now lists build-tools 35.0.0 + 34.0.0, matching the
Dockerfile. The 34.0.0 install was added late and the doc wasn't refreshed.

P2.3 — Docs: Path A now spells out the Docker Desktop 4.34+ requirement
for --network host. On older Docker Desktop the flag is silently ignored
and apt downloads hang for ~7 min before failing with "Unable to locate
package wget".

P3.5 — Help text: replace sed -n '2,21p' with an awk sentinel that stops
at the first non-comment line, so growing the header doesn't silently
truncate --help output.

P3.6 — Base image pinned by digest (node:22-bookworm-slim@sha256:7af0...)
for byte-reproducible rebuilds. Comment documents the bump procedure.

P3.7 — Timing table re-measured from this PR's verification runs
(7-10 min cold, 3-5 min warm-image, 1-2 min warm-everything). Previous
numbers were estimates.

Verified end-to-end: image rebuilds cleanly, ./gradlew assembleDebug
warm-cache completes in 8s, app-debug.apk is 4.6 MB valid Android package.
forgejo_admin merged commit 9c99c0b43c into master 2026-05-20 15:10:28 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: forgejo_admin/exifcleaner-web#163
No description provided.