Files
android/README.md
Robert Helewka 8a05323905 docs: add reusable Heluca Google Play publishing guide
Playbook for taking any Heluca app from repo to Google Play, learned from
shipping Dade to internal testing: one-time publisher account + upload keystore
setup, per-app checklist (signing via -Pandroid.injected.signing.*, AAB,
versionCode encoding, native symbols), internal-testing flow, gotchas, and
production promotion. Bakes in Heluca conventions and documents where Dade
deliberately diverges (custom signingConfig, UPLOAD_* secret names, :latest pin)
so the next app can follow the template instead.

Link it from the README.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 13:52:15 -04:00

4.4 KiB

android-builder

Native linux/amd64 Android build toolchain for CI, published to the Gitea container registry at git.helu.ca/r/android.

App repos consume this image to build signed release artifacts. Instrumented tests are not part of this toolchain — by design, CI builds are promotions of code already tested in Dev (on Apple silicon, where the emulator runs natively).

Publishing to Google Play? See docs/google-play-publishing.md — the reusable Heluca playbook (publisher setup, upload keystore, per-app checklist, internal testing, promotion to production), learned from shipping Dade.

Why amd64: Google publishes the Linux Android build-tools (aapt2, d8, …) for x86_64 only — there is no arm64 Linux build-tools package. Running the toolchain on an aarch64 runner fails at resource packaging (aapt2: Syntax error: newline unexpected, an x86_64 binary the kernel can't exec). The produced APK/AAB is architecture-neutral regardless of build host, so building on amd64 costs nothing on the output side.

What's in the image

  • Eclipse Temurin JDK 21 (native amd64)
  • Android cmdline-tools, platform, and build-tools — baked in, so prod builds don't depend on Google's download endpoint at job time
  • git, curl, unzip
  • Node.js 24 — so Gitea Actions JS actions (actions/checkout, upload-artifact, …) run when this image is used as a job container

Pinned versions live as ARGs at the top of the Dockerfile:

ARG Default Where to check
CMDLINE_TOOLS_VERSION 13114758 https://developer.android.com/studio#command-line-tools-only
BUILD_TOOLS_VERSION 36.0.0 SDK Manager / release notes
PLATFORM_VERSION android-36 your app's compileSdk

The build-tools binaries (aapt2, d8) are x86_64 ELF — this is why the image is amd64 (see "Why amd64" above).

Tagging model

The whole point of owning this image is that app repos pin a tag and a new toolchain never lands in a prod build by accident.

  • Immutable — a git tag like 2026.06 produces git.helu.ca/r/android:2026.06. This is what app repos pin to.
  • Moving — pushes to main (and any tag build) refresh git.helu.ca/r/android:latest for convenience. Don't pin prod builds to this.

Cutting a new toolchain

  1. Update the ARG defaults in the Dockerfile if you're moving SDK/tool versions. Verify CMDLINE_TOOLS_VERSION against the link above — a stale number 404s the image build.
  2. Commit to main. The workflow builds and pushes :latest; confirm it's green.
  3. Tag the release with the year-month you want app repos to reference:
    git tag 2026.06
    git push origin 2026.06
    
    This publishes the immutable git.helu.ca/r/android:2026.06.
  4. Roll app repos forward deliberately by bumping their pinned tag (see below) — one at a time if you want to validate, all at once if you trust the bump.

Using it in an app repo

Drop .gitea/workflows/build.yml (the template alongside this repo) into the app and pin the toolchain:

jobs:
  build:
    runs-on: ubuntu-24.04
    container:
      image: git.helu.ca/r/android:2026.06
      credentials:
        username: ${{ gitea.actor }}
        password: ${{ secrets.PACKAGE_TOKEN }}

The build task is selectable: assembleRelease (APK, the default) or bundleRelease (AAB — only needed for Google Play distribution).

Required secrets in each app repo (or org-level)

Signing happens at job time; nothing sensitive lives in the repo or the image.

Secret What it is
KEYSTORE_BASE64 release keystore, base64-encoded: base64 -w0 release.keystore
KEYSTORE_PASSWORD keystore password
KEY_ALIAS signing key alias
KEY_PASSWORD key password

PACKAGE_TOKEN (a PAT, the same secret the other repos here use) needs write:package in this repo (to push the image) and read:package in app repos (to pull it). Set it as a repo or org-level Actions secret. The built-in gitea.token is not used because it isn't scoped for the registry.

First-run sequencing

The image must exist in the registry before any app workflow can pull it. So: cut and push 2026.06 from this repo first, confirm it's in the registry, then the app workflows that pin it will resolve.