dist.sh builds versioned, signed artifacts for both apps from a git tag
release / build (push) Failing after 0s

(semver drives Android versionCode/versionName and package.json). The
build-image/ Dockerfile pins the same toolchain for a portable,
containerized Gitea Actions runner (build-image.sh builds and pushes
it) so releases don't depend on any one machine's local setup.
Pushing vX.Y.Z now builds and publishes a Gitea Release automatically.
This commit is contained in:
ml
2026-07-02 13:07:52 +02:00
parent 73c6c90c8d
commit f3f7442849
9 changed files with 469 additions and 22 deletions
+139 -21
View File
@@ -89,14 +89,30 @@ the whole Gradle configuration.
### 2.4 Build the release bundle
```bash
cd SchwertUndMagieOnPebbleCompanionApp
./gradlew bundleRelease # produces app/build/outputs/bundle/release/app-release.aab
./gradlew assembleRelease # produces app/build/outputs/apk/release/app-release.apk, for sideload testing
./dist.sh # builds both apps, copies signed artifacts into dist/
```
Play Store requires the **AAB** (`bundleRelease` output), not the APK — Play
This runs `./gradlew bundleRelease assembleRelease` in
`SchwertUndMagieOnPebbleCompanionApp/` and `pebble build` in
`SchwertUndMagieOnPebbleWatchApp/`, then copies the outputs to
`dist/schwert-und-magie.{aab,apk,pbw}`.
Before building, the script hard-stops (nonzero exit, nothing written to
`dist/`) if the `pebble` CLI, `gradlew`, the Android SDK, the NDK version
pinned in `app/build.gradle.kts`, or the VICE/nibtools source tarballs are
missing — these are required for the build to succeed at all. It warns but
still builds if `keystore.properties` (§2.1-2.3) is missing or points at a
nonexistent keystore file, and checks the resulting APK's signature with
`apksigner` afterward, warning if it's unsigned or debug-signed. In practice
an incomplete signing config also makes Gradle's own `signReleaseBundle` task
fail outright, so `dist/` won't be overwritten with an unusable artifact
either way — but don't rely on that as the primary check; heed the warning.
Play Store requires the **AAB** (`schwert-und-magie.aab`), not the APK — Play
re-packages per-device APKs from it (including ABI splits, so `arm64-v8a`/
`x86_64` native VICE libraries each ship only to matching devices).
`x86_64` native VICE libraries each ship only to matching devices). The APK
(`schwert-und-magie.apk`) is for direct sideload distribution (§2.6 below)
and F-Droid-style testing, not Play Store upload.
### 2.5 Play Console setup (one-time, per app listing)
@@ -119,12 +135,11 @@ re-packages per-device APKs from it (including ABI splits, so `arm64-v8a`/
### 2.6 Versioning for future releases
Bump both fields in `app/build.gradle.kts` before every release build:
```kotlin
versionCode = 2 // must strictly increase on every Play Store upload
versionName = "1.1" // user-visible, free-form
```
Comes from the git tag automatically — see §4. Pushing `vX.Y.Z` (or running
`VERSION=X.Y.Z ./dist.sh` locally) patches `versionCode` (derived as
`X*10000 + Y*100 + Z`, which strictly increases as long as X.Y.Z itself does)
and `versionName` in `app/build.gradle.kts` at build time, then reverts them —
no manual edit needed.
## 3. Android companion app → F-Droid
@@ -220,8 +235,102 @@ includes the app in the next index update (published roughly weekly).
### 3.5 Versioning for F-Droid updates
Each new release requires a new `Builds` entry in the metadata file with an
incremented `versionCode` and the corresponding git commit or tag. `versionCode`
must match the value in `app/build.gradle.kts`.
incremented `versionCode` and the corresponding git commit or tag.
`versionCode` must match what that tag produces — `X*10000 + Y*100 + Z` for
tag `vX.Y.Z` (§2.6, §4).
## 4. Automated releases via Gitea Actions
Pushing a tag `vX.Y.Z` builds both apps and publishes a Gitea Release with
`schwert-und-magie-X.Y.Z.{aab,apk,pbw}` attached — no local `./dist.sh` run
needed. The tag is the only source of truth for the version; nothing needs to
be bumped in source files beforehand (`dist.sh` patches `versionCode`/
`versionName`/`package.json` at build time and reverts them — see §4.4 for
running the same thing locally).
The whole toolchain (Android SDK/NDK, Pebble SDK) lives in
`build-image/Dockerfile`, built and pushed to a container registry by
`build-image.sh`. This keeps the setup portable: the runner just needs Docker
and pulls that image, so it isn't tied to any one machine's local toolchain
install and can be moved or re-registered elsewhere without touching this
repo's build scripts.
### 4.1 Build environment image
`build-image/Dockerfile` bakes in the Android SDK/NDK and Pebble SDK versions
pinned in `app/build.gradle.kts`, matching what's validated for local builds.
It does **not** contain the release keystore — that's injected at job runtime
from Actions secrets (§4.3), never baked into the image.
```bash
cp registry.env.example registry.env # fill in your registry credentials
./build-image.sh # builds + pushes :latest and the pinned VERSION tag
```
Rebuild and push whenever `build-image/Dockerfile` changes (e.g. a Pebble SDK
or Android NDK version bump) — bump `build-image/VERSION` first so the tag is
meaningful. The workflow (§4.2) pulls `:latest` by default; if you need a
release to be reproducible against an exact toolchain image, pin the `image:`
line in `.gitea/workflows/release.yml` to the versioned tag instead.
### 4.2 Runner setup (one-time)
1. Enable Actions for the repo: repo Settings → Actions → enable, if not
already on by default for this Gitea instance.
2. Generate a runner registration token: Site Admin → Actions → Runners (or
the repo/org-scoped equivalent) → "Create new runner".
3. On any machine with Docker that can reach both your Gitea instance and
your container registry:
```bash
# https://gitea.com/gitea/act_runner — grab the latest release binary
./act_runner register --no-interactive \
--instance <your gitea URL> --token <token> \
--name <runner-name> --labels ubuntu-latest:docker://node:20-bookworm
./act_runner daemon
```
The image after `docker://` in `--labels` is only a fallback for jobs that
don't specify their own `container:` — irrelevant here since
`.gitea/workflows/release.yml` always pins its own image, but the runner
still needs a Docker-executor label registered to use that executor at
all. The label name itself (`ubuntu-latest` above) must match `runs-on:`
in `.gitea/workflows/release.yml` — edit both together if you rename it,
or reuse a label an existing runner already advertises (check Site Admin →
Actions → Runners) to skip registering a new one entirely.
4. The runner's Docker daemon needs pull access to the registry — run
`docker login <registry>` once on that machine with the same credentials
as `registry.env`.
### 4.3 Repo secrets
Settings → Actions → Secrets, add:
| Secret | Value |
|---|---|
| `RELEASE_KEYSTORE_B64` | `base64 -w0 SchwertUndMagieOnPebbleCompanionApp/release.keystore` |
| `RELEASE_KEYSTORE_PROPERTIES` | the full contents of `SchwertUndMagieOnPebbleCompanionApp/keystore.properties` (§2.2) |
`secrets.GITEA_TOKEN` (used to create the release and upload assets) is
Gitea's own auto-generated per-job token — nothing to create or add yourself.
`.gitea/workflows/release.yml` requests `contents: write` explicitly so
release creation works regardless of this instance's default Actions
permission mode.
### 4.4 Cutting a release
```bash
git tag v1.2.3
git push origin v1.2.3
```
Watch the run under the repo's Actions tab. On success, the release appears
under the repo's Releases page with the three versioned artifacts attached.
To build the same versioned artifacts locally without pushing a tag (e.g. to
test before releasing):
```bash
VERSION=1.2.3 ./dist.sh
```
## 5. Pebble watch app → Rebble app store / direct distribution
@@ -233,12 +342,19 @@ this repo and changes independently of it.
### 5.1 Build the artifact
```bash
./dist.sh # also builds the Android side; see §2.4
```
or, to build just the watch app:
```bash
cd SchwertUndMagieOnPebbleWatchApp
pebble build # produces build/SchwertUndMagieOnPebbleWatchApp.pbw
```
The `.pbw` is the complete distributable — it bundles all target platforms
`dist.sh` copies the result to `dist/schwert-und-magie.pbw`. The `.pbw` is
the complete distributable — it bundles all target platforms
(`aplite`/`basalt`/`chalk`/`diorite`/`emery`/`flint`/`gabbro`) declared in
`package.json`. There is no signing step analogous to Android; Pebble apps
are not cryptographically signed by the developer.
@@ -267,19 +383,21 @@ pebble screenshot --vnc --no-open docs/screenshots/emery.png
### 5.4 Versioning
Bump `version` in `SchwertUndMagieOnPebbleWatchApp/package.json` before each
release build.
Comes from the git tag automatically — see §4. `dist.sh` patches
`SchwertUndMagieOnPebbleWatchApp/package.json`'s `version` field at build
time and reverts it afterward; no manual edit needed.
## 6. Pre-publish checklist
- [ ] Release keystore generated, passwords saved in a password manager, both
gitignored (§1, §2.1)
- [ ] `keystore.properties` exists locally and is **not** tracked by git
- [ ] `./gradlew bundleRelease` succeeds and installs/runs on a real device
from the resulting AAB (test via `bundletool` or Play internal testing)
- [ ] `./dist.sh` (or a tag push, §4) succeeds and produces
`dist/schwert-und-magie-<version>.{aab,apk,pbw}`
- [ ] AAB installs/runs on a real device (test via `bundletool` or Play
internal testing)
- [ ] Play Console store listing content complete (icon, screenshots,
privacy policy, content rating, data safety form)
- [ ] F-Droid metadata YAML created and `fdroid build` passes locally (§3)
- [ ] `pebble build` succeeds for all target platforms; `.pbw` sideloads and
runs correctly against the signed companion app build
- [ ] `versionCode`/`versionName` (Android) and `version` (Pebble) bumped
- [ ] `.pbw` sideloads and runs correctly against the signed companion app
build