GITEA_TOKEN is not set in these repos — the registry login sent a blank password and Gitea's /v2/ endpoint returned unauthorized. Switch the image-build login, the app template, and the README to PACKAGE_TOKEN, the PAT the other repos already use (Syrinx pairs gitea.actor + PACKAGE_TOKEN successfully). The PAT needs write:package here and read:package in app repos. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
android-builder
Native linux/arm64 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). The OCI Ampere (aarch64) runner has no /dev/kvm (the guest VM
boots at EL1, so KVM can't access HYP/EL2), so there's no accelerated emulator
here — and we don't need one.
What's in the image
- Eclipse Temurin JDK 21 (native arm64)
- 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
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 |
aapt2 and the build-tools binaries ship native arm64 for 36.x, so packaging
runs without emulation.
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.06producesgit.helu.ca/r/android:2026.06. This is what app repos pin to. - Moving — pushes to
main(and any tag build) refreshgit.helu.ca/r/android:latestfor convenience. Don't pin prod builds to this.
Cutting a new toolchain
- Update the
ARGdefaults in theDockerfileif you're moving SDK/tool versions. VerifyCMDLINE_TOOLS_VERSIONagainst the link above — a stale number 404s the image build. - Commit to
main. The workflow builds and pushes:latest; confirm it's green. - Tag the release with the year-month you want app repos to reference:
This publishes the immutable
git tag 2026.06 git push origin 2026.06git.helu.ca/r/android:2026.06. - 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-arm64
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.