7.2 KiB
Gitea Actions builds
The workflow in .gitea/workflows/build.yml builds the pinned toolchain image,
then compiles the Linux and Android packages. Its toolchain is:
- Qt 6.11.1 from Qt's public binary repository, installed with
aqtinstall; - Ubuntu 24.04 and JDK 21;
- Android API and Build Tools 36;
- Android NDK 27.2.12479018;
- a pinned KDAB Android OpenSSL revision;
arm64-v8aandarmeabi-v7adevice ABIs.
The complete toolchain image is stored in registry.brunner.ninja. The
first run for a new toolchain revision downloads Qt and the Android SDK, then
publishes the image. Ordinary runs—including runs on a new or freshly cleaned
runner—pull that image and skip the image build. Bump the rN suffix of
BUILD_IMAGE in the workflow whenever the Dockerfile toolchain changes. Tags
are treated as immutable; never reuse an existing revision for changed content.
Self-hosted Actions
The workflow does not fetch Actions from gitea.com or GitHub. Its action
implementations are mirrored on code.brunner.ninja and use their local v4
tags:
actions/checkout;actions/cache;actions/upload-artifact.
The tags are resolved entirely through the local mirrors. Do not change these references back to upstream URLs. This command audits all workflow action references:
rg '^[[:space:]]*uses:' .gitea/workflows
Documentation-only commits do not start this workflow. Source, build-system, toolchain, tag, and manually dispatched runs continue to build the application.
Qt's current Gradle template is intentionally not copied into the source tree.
androiddeployqt overlays the custom manifest and resources from
evcharger-app/android onto the template supplied by Qt 6.11.1. This keeps Qt,
Gradle, and the Android Gradle Plugin in sync.
Runner requirements
Enable Actions in the repository settings and register a trusted Gitea Runner
with the workflow label linux_amd64. Use host mode because the workflow itself
starts rootless Podman build containers:
runner:
labels:
- "linux_amd64:host"
The runner host needs Podman, Bash, Git, and Node.js. Keep this host-mode runner restricted to repositories you trust: workflows execute commands as the runner user and can access everything that user can access.
For example, after downloading act_runner, obtain a repository runner token
from Settings > Actions > Runners, then run:
./act_runner register --no-interactive \
--instance https://your-gitea.example \
--token YOUR_REGISTRATION_TOKEN \
--name evcharger-builder \
--labels linux_amd64:host
./act_runner daemon
The workflow timeout is three hours because the uncached first image build is large. The toolchain image is cached independently from the ccache and Gradle caches stored by Gitea Actions.
Toolchain registry credentials
The registry is backed by Quay. Use a Quay robot account with write access to
the feedc0de/evcharger-app repository; this avoids tying CI to a person's
account. Add its credentials as repository secrets under
Settings > Actions > Secrets:
| Secret | Value |
|---|---|
REGISTRY_USERNAME |
Quay robot username, including its namespace prefix |
REGISTRY_PASSWORD |
Quay robot token |
The image is published as
registry.brunner.ninja/feedc0de/evcharger-app:qt-6.11.1-r1. The workflow logs
in before pulling because this repository is not anonymously readable. The
robot needs write permission so the first run of a new revision can push it.
Credentials are written to a per-job temporary authentication file and removed
before the step exits.
Signing secrets
Without a keystore, the workflow deliberately produces unsigned validation packages. This allows pull requests to test Android compilation without release credentials.
In Settings > Actions > Secrets, add these repository secrets:
| Secret | Value |
|---|---|
ANDROID_KEYSTORE_BASE64 |
Release keystore encoded as one base64 line |
ANDROID_KEYSTORE_ALIAS |
Private-key alias in the keystore |
ANDROID_KEYSTORE_STORE_PASS |
Keystore password |
ANDROID_KEYSTORE_KEY_PASS |
Private-key password |
Encode the keystore locally with:
base64 -w0 android_release.keystore
Never commit the keystore. Root-level .keystore and .jks files are ignored.
The workflow's decoded temporary copy is deleted when the Android script exits.
Check an uncertain password and alias safely
keytool is included in a JDK. This command prompts for the store password and
lists all aliases; the password is not placed in shell history or the process
list:
keytool -list -v -keystore android_release.keystore
That only proves the store password. To verify the selected alias and its private-key password without modifying the keystore, sign a disposable JAR:
probe_dir="$(mktemp -d)"
trap 'rm -rf -- "$probe_dir"' EXIT
printf 'keystore test\n' > "$probe_dir/probe.txt"
jar --create --file "$probe_dir/probe.jar" -C "$probe_dir" probe.txt
jarsigner -keystore android_release.keystore "$probe_dir/probe.jar" YOUR_ALIAS
jarsigner -verify "$probe_dir/probe.jar"
jarsigner prompts first for the store password and, when different, for the
private-key password. A successful signing followed by jar verified confirms
all three values. Do not use -storepass or -keypass on an interactive shell,
because command-line arguments can leak through history and process listings.
Artifacts and Android versions
Each successful workflow exposes three downloads in the Gitea Actions run:
evcharger-linux/
evcharger-app
evcharger-apk/
evcharger-<version>-universal.apk
evcharger-aab/
evcharger-<version>.aab
Artifacts are retained for 30 days. APK and AAB packages are separate downloads so phone testing does not also download the Play bundle. Gitea Actions serves artifacts as ZIP archives, but compression is disabled because APK and AAB files are already ZIP containers. A signed universal APK is suitable for sideloading; an unsigned one is only a validation artifact. Upload the signed AAB to Google Play, which produces device-specific APKs.
The Android versionCode defaults to the monotonically increasing Gitea Actions
run number. Tags become the versionName after an optional leading v is
removed; other builds use 0.1.0-ci.<run number>. Override these with workflow
environment values ANDROID_VERSION_CODE and ANDROID_VERSION_NAME if needed.
Local container builds
podman build -t evcharger-build:qt-6.11.1 .
podman run --rm --userns=keep-id -v "$PWD:/workspace" -w /workspace \
evcharger-build:qt-6.11.1 bash ci/build-linux.sh
podman run --rm --userns=keep-id -v "$PWD:/workspace" -w /workspace \
evcharger-build:qt-6.11.1 bash ci/build-android.sh
Pass the signing variables through --env or an environment file for a signed
local Android build. Do not add secrets to the Dockerfile or image.
Qt open-source licensing note
The public Qt binaries do not require a commercial account. Distribution must still follow every linked module's license. Qt Charts is GPLv3-only for open-source users (or commercially licensed), so distributing this application with Qt Charts requires GPLv3-compatible distribution of the combined program and corresponding source. Review and document that licensing choice before publishing the APK/AAB, or use a commercial Qt license.