Compare commits
1 Commits
73c6c90c8d
..
v0.9.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 4be350d758 |
@@ -0,0 +1,56 @@
|
||||
name: release
|
||||
|
||||
# Push a tag matching vX.Y.Z (e.g. v1.2.3) to build and publish a release.
|
||||
# The tag drives the version — nothing to bump in source files beforehand.
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# Needed to create the release and upload assets with GITEA_TOKEN below,
|
||||
# regardless of this instance's default Actions permission mode.
|
||||
permissions:
|
||||
contents: write
|
||||
# Must match the label your self-hosted act_runner registers with.
|
||||
runs-on: self-hosted
|
||||
container:
|
||||
# :latest — always the most recently pushed build-image.sh output.
|
||||
# Pin to a specific tag here (see build-image/VERSION) if you need a
|
||||
# release build to be reproducible against an exact toolchain image.
|
||||
image: cr.ladkau.de/schwert-und-magie/builder:latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Write release signing credentials
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "${{ secrets.RELEASE_KEYSTORE_B64 }}" | base64 -d \
|
||||
> SchwertUndMagieOnPebbleCompanionApp/release.keystore
|
||||
printf '%s\n' "${{ secrets.RELEASE_KEYSTORE_PROPERTIES }}" \
|
||||
> SchwertUndMagieOnPebbleCompanionApp/keystore.properties
|
||||
|
||||
- name: Build artifacts
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="${{ gitea.ref_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
VERSION="$VERSION" ./dist.sh
|
||||
|
||||
- name: Create release and upload artifacts
|
||||
run: |
|
||||
set -euo pipefail
|
||||
API="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}"
|
||||
AUTH="Authorization: token ${{ secrets.GITEA_TOKEN }}"
|
||||
|
||||
RELEASE_ID="$(curl -sf -X POST "$API/releases" \
|
||||
-H "$AUTH" -H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\": \"${{ gitea.ref_name }}\", \"name\": \"${{ gitea.ref_name }}\"}" \
|
||||
| jq -r .id)"
|
||||
|
||||
for f in dist/*; do
|
||||
curl -sf -X POST "$API/releases/$RELEASE_ID/assets?name=$(basename "$f")" \
|
||||
-H "$AUTH" -F "attachment=@$f"
|
||||
done
|
||||
@@ -32,3 +32,6 @@ local.properties
|
||||
*.jks
|
||||
*.keystore
|
||||
keystore.properties
|
||||
|
||||
# Container registry push credentials for build-image.sh. See docs/publish.md.
|
||||
registry.env
|
||||
|
||||
@@ -38,9 +38,18 @@ SchwertUndMagieOnPebbleWatchApp/ Pebble watchapp (C + PebbleKit JS)
|
||||
SchwertUndMagieOnPebbleCompanionApp/ Android companion app (Kotlin + NDK)
|
||||
versions/ Original .d64 disk images (4 disks)
|
||||
docs/ Architecture notes and diagrams
|
||||
dist/ Built APKs (gitignored)
|
||||
build-image/ Dockerfile for the release build environment
|
||||
dist/ Build artifacts (gitignored) — see dist.sh
|
||||
```
|
||||
|
||||
Run `./dist.sh` to build release artifacts for both apps in one step:
|
||||
`dist/schwert-und-magie-<version>.{aab,apk,pbw}` — the signed Android AAB
|
||||
(Play Store), APK (sideload), and Pebble `.pbw` (Rebble / direct install).
|
||||
Requires the Android release keystore to already be configured
|
||||
(`docs/publish.md` §2.1-2.3). The version comes from the current git tag by
|
||||
default (`git tag v1.2.3`); see `docs/publish.md` §4 for pushing that tag to
|
||||
trigger an automated, containerized build via Gitea Actions instead.
|
||||
|
||||
## How it works
|
||||
|
||||
Three processes cooperate across two devices:
|
||||
@@ -135,6 +144,28 @@ ensure register consistency — see `docs/architecture.md §4.4`.
|
||||
See [`docs/publish.md`](docs/publish.md) for keystore setup, Google Play,
|
||||
F-Droid, and Rebble submission.
|
||||
|
||||
### Automated releases (Gitea Actions)
|
||||
|
||||
Pushing a tag `vX.Y.Z` builds both apps in a containerized runner and
|
||||
publishes a Gitea Release with the versioned artifacts attached — see
|
||||
`docs/publish.md` §4. One-time setup:
|
||||
|
||||
1. Build and push the build environment image (`./build-image.sh`, needs
|
||||
`registry.env` — copy from `registry.env.example`).
|
||||
2. Register a self-hosted `act_runner` with a Docker executor.
|
||||
3. Add repo secrets under **Settings → Actions → Secrets**:
|
||||
|
||||
| Secret | Value |
|
||||
|---|---|
|
||||
| `RELEASE_KEYSTORE_B64` | `base64 -w0 SchwertUndMagieOnPebbleCompanionApp/release.keystore` |
|
||||
| `RELEASE_KEYSTORE_PROPERTIES` | full contents of `SchwertUndMagieOnPebbleCompanionApp/keystore.properties` |
|
||||
|
||||
`GITEA_TOKEN` is injected automatically per job — nothing to add for it.
|
||||
4. `git tag v1.2.3 && git push origin v1.2.3`.
|
||||
|
||||
Full details, including runner registration commands, are in
|
||||
`docs/publish.md` §4.
|
||||
|
||||
## License
|
||||
|
||||
The watch app and companion app source code in this repository are released
|
||||
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds and pushes the release build environment image (build-image/Dockerfile)
|
||||
# to the container registry. The Gitea Actions release workflow pulls this
|
||||
# image to run dist.sh. Bump build-image/VERSION whenever the Dockerfile
|
||||
# changes so the workflow can pin a stable tag.
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
ROOT="$(pwd)"
|
||||
|
||||
fail() { echo "PREFLIGHT FAIL: $*" >&2; exit 1; }
|
||||
|
||||
command -v docker >/dev/null 2>&1 \
|
||||
|| fail "docker not found in PATH"
|
||||
|
||||
[ -f "$ROOT/registry.env" ] \
|
||||
|| fail "registry.env not found — copy registry.env.example to registry.env and fill in your cr.ladkau.de credentials"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "$ROOT/registry.env"
|
||||
|
||||
: "${REGISTRY:?registry.env must set REGISTRY}"
|
||||
: "${REGISTRY_IMAGE:?registry.env must set REGISTRY_IMAGE}"
|
||||
: "${REGISTRY_USER:?registry.env must set REGISTRY_USER}"
|
||||
: "${REGISTRY_PASSWORD:?registry.env must set REGISTRY_PASSWORD}"
|
||||
|
||||
VERSION="$(<"$ROOT/build-image/VERSION")"
|
||||
[ -n "$VERSION" ] || fail "build-image/VERSION is empty"
|
||||
|
||||
echo "== Logging in to $REGISTRY =="
|
||||
echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin
|
||||
|
||||
echo "== Building $REGISTRY_IMAGE:$VERSION =="
|
||||
docker build \
|
||||
-t "$REGISTRY_IMAGE:$VERSION" \
|
||||
-t "$REGISTRY_IMAGE:latest" \
|
||||
-f "$ROOT/build-image/Dockerfile" \
|
||||
"$ROOT/build-image"
|
||||
|
||||
echo "== Pushing $REGISTRY_IMAGE:$VERSION and :latest =="
|
||||
docker push "$REGISTRY_IMAGE:$VERSION"
|
||||
docker push "$REGISTRY_IMAGE:latest"
|
||||
|
||||
echo "== Done =="
|
||||
echo "Image: $REGISTRY_IMAGE:$VERSION"
|
||||
@@ -0,0 +1,66 @@
|
||||
# Build environment for schwert_und_magie_on_pebble release artifacts:
|
||||
# Android SDK/NDK (companion app) + Pebble SDK (watch app), matching the
|
||||
# versions pinned in app/build.gradle.kts and validated on the maintainer's
|
||||
# dev machine. Rebuild and push with ../build-image.sh whenever a version
|
||||
# below, or the companion app's ndkVersion/compileSdk, changes.
|
||||
#
|
||||
# Does NOT contain the release keystore or any secrets — those are injected
|
||||
# at job runtime from Gitea Actions secrets, never baked into this image.
|
||||
FROM eclipse-temurin:21-jdk-jammy
|
||||
|
||||
ARG ANDROID_CMDLINE_TOOLS_VERSION=11076708
|
||||
ARG ANDROID_PLATFORM=android-36
|
||||
ARG ANDROID_BUILD_TOOLS=36.1.0
|
||||
ARG ANDROID_NDK=30.0.14904198
|
||||
ARG PEBBLE_TOOL_VERSION=5.0.35
|
||||
ARG PEBBLE_SDK_CORE_VERSION=4.9.169
|
||||
ARG NODE_VERSION=24.16.0
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
ANDROID_HOME=/opt/android-sdk \
|
||||
ANDROID_SDK_ROOT=/opt/android-sdk \
|
||||
ANDROID_NDK_HOME=/opt/android-sdk/ndk/30.0.14904198 \
|
||||
PATH=/root/.local/bin:/opt/android-sdk/cmdline-tools/latest/bin:/opt/android-sdk/platform-tools:${PATH}
|
||||
|
||||
# git/unzip/curl/jq: checkout, SDK downloads, and the release workflow's
|
||||
# calls to the Gitea API (create release, upload assets).
|
||||
# python3-venv: pebble-tool's `sdk install` creates a venv per SDK version.
|
||||
# dos2unix/autoconf/automake/pkg-config/xa65/build-essential/gettext: host
|
||||
# tools required by the companion app's build_vice.sh (see that file's own
|
||||
# preflight check) to cross-compile VICE via autotools before NDK clang
|
||||
# takes over for the actual target compilation.
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl ca-certificates git unzip tar xz-utils python3 python3-venv file jq \
|
||||
dos2unix autoconf automake pkg-config xa65 build-essential gettext \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# --- Android SDK: cmdline-tools, platform, build-tools, NDK ---
|
||||
RUN mkdir -p "$ANDROID_HOME/cmdline-tools" \
|
||||
&& curl -sSL -o /tmp/cmdline-tools.zip \
|
||||
"https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_CMDLINE_TOOLS_VERSION}_latest.zip" \
|
||||
&& unzip -q /tmp/cmdline-tools.zip -d "$ANDROID_HOME/cmdline-tools" \
|
||||
&& mv "$ANDROID_HOME/cmdline-tools/cmdline-tools" "$ANDROID_HOME/cmdline-tools/latest" \
|
||||
&& rm /tmp/cmdline-tools.zip \
|
||||
&& yes | sdkmanager --licenses >/dev/null \
|
||||
&& sdkmanager --install \
|
||||
"platform-tools" \
|
||||
"platforms;${ANDROID_PLATFORM}" \
|
||||
"build-tools;${ANDROID_BUILD_TOOLS}" \
|
||||
"ndk;${ANDROID_NDK}" \
|
||||
>/dev/null
|
||||
|
||||
# Node.js: `pebble sdk install` below runs `npm install` for the SDK-core's
|
||||
# bundled webpack tooling — it does not bring its own node/npm, only the JS
|
||||
# deps themselves. Version matches what's validated on the maintainer's
|
||||
# machine; sdk-core's own bundled arm-none-eabi toolchain needs nothing extra.
|
||||
RUN curl -sSL -o /tmp/node.tar.xz \
|
||||
"https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz" \
|
||||
&& tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1 \
|
||||
&& rm /tmp/node.tar.xz
|
||||
|
||||
# --- Pebble SDK: pebble-tool + sdk-core ---
|
||||
RUN curl -LsSf https://astral.sh/uv/install.sh | sh \
|
||||
&& /root/.local/bin/uv tool install "pebble-tool==${PEBBLE_TOOL_VERSION}" \
|
||||
&& /root/.local/bin/pebble sdk install "${PEBBLE_SDK_CORE_VERSION}"
|
||||
|
||||
WORKDIR /workspace
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds release artifacts for both apps and drops them in dist/, named
|
||||
# schwert-und-magie-<version>.{aab,apk,pbw}.
|
||||
#
|
||||
# Version comes from the current git tag (vX.Y.Z) by default — push a tag to
|
||||
# drive a release. Override with VERSION=1.2.3 ./dist.sh, or just run it
|
||||
# untagged for a local dev build (gets a 0.0.0-dev+<sha> placeholder version).
|
||||
#
|
||||
# Android output requires a release signing config — see docs/publish.md §2.1-2.3
|
||||
# (keystore.properties + release.keystore in SchwertUndMagieOnPebbleCompanionApp/).
|
||||
# Without it, Gradle still produces an unsigned/debug-signed build; that's not
|
||||
# fatal (useful for local testing) so it's a warning, not a hard stop.
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
ROOT="$(pwd)"
|
||||
DIST="$ROOT/dist"
|
||||
COMPANION="$ROOT/SchwertUndMagieOnPebbleCompanionApp"
|
||||
WATCH="$ROOT/SchwertUndMagieOnPebbleWatchApp"
|
||||
GRADLE_KTS="$COMPANION/app/build.gradle.kts"
|
||||
WATCH_PKG_JSON="$WATCH/package.json"
|
||||
|
||||
fail() { echo "PREFLIGHT FAIL: $*" >&2; exit 1; }
|
||||
warn() { echo "WARNING: $*" >&2; }
|
||||
|
||||
echo "== Preflight checks =="
|
||||
|
||||
command -v pebble >/dev/null 2>&1 \
|
||||
|| fail "pebble CLI not found in PATH (needed to build the watch app)"
|
||||
|
||||
[ -x "$COMPANION/gradlew" ] \
|
||||
|| fail "$COMPANION/gradlew missing or not executable"
|
||||
|
||||
SDK_DIR="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-}}"
|
||||
if [ -f "$COMPANION/local.properties" ]; then
|
||||
LOCAL_SDK="$(sed -n 's/^sdk\.dir=//p' "$COMPANION/local.properties")"
|
||||
[ -n "$LOCAL_SDK" ] && SDK_DIR="$LOCAL_SDK"
|
||||
fi
|
||||
[ -n "$SDK_DIR" ] && [ -d "$SDK_DIR" ] \
|
||||
|| fail "Android SDK not found (checked local.properties sdk.dir, \$ANDROID_SDK_ROOT, \$ANDROID_HOME)"
|
||||
|
||||
NDK_VERSION="$(sed -n 's/.*ndkVersion *= *"\(.*\)".*/\1/p' "$GRADLE_KTS")"
|
||||
[ -n "$NDK_VERSION" ] \
|
||||
|| fail "could not read ndkVersion from app/build.gradle.kts"
|
||||
[ -d "$SDK_DIR/ndk/$NDK_VERSION" ] \
|
||||
|| fail "NDK $NDK_VERSION not installed under $SDK_DIR/ndk (Android Studio > SDK Manager > SDK Tools > NDK side by side)"
|
||||
|
||||
[ -f "$COMPANION/res/vice-3.8.tar.gz" ] \
|
||||
|| fail "$COMPANION/res/vice-3.8.tar.gz missing (VICE source tarball required by the buildVice Gradle task)"
|
||||
compgen -G "$COMPANION/res/nibtools-*.tar.gz" >/dev/null \
|
||||
|| fail "$COMPANION/res/nibtools-*.tar.gz missing (nibtools source tarball required by the buildNibtools Gradle task)"
|
||||
|
||||
# Signing config: missing/broken keystore.properties still builds (unsigned),
|
||||
# so warn here and rely on the post-build apksigner check for the real answer.
|
||||
if [ ! -f "$COMPANION/keystore.properties" ]; then
|
||||
warn "keystore.properties not found — release build will likely be unsigned (see docs/publish.md §2.1-2.3)"
|
||||
else
|
||||
STORE_FILE="$(sed -n 's/^storeFile=//p' "$COMPANION/keystore.properties")"
|
||||
if [ -z "$STORE_FILE" ] || [ ! -f "$COMPANION/$STORE_FILE" ]; then
|
||||
warn "keystore.properties found but its storeFile ('$STORE_FILE') does not exist — release build will likely be unsigned"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "== Resolving version =="
|
||||
|
||||
if [ -z "${VERSION:-}" ]; then
|
||||
if TAG="$(git describe --tags --exact-match --match 'v[0-9]*.[0-9]*.[0-9]*' 2>/dev/null)"; then
|
||||
VERSION="${TAG#v}"
|
||||
else
|
||||
VERSION="0.0.0-dev+$(git rev-parse --short HEAD)"
|
||||
warn "HEAD is not on a vX.Y.Z tag — building placeholder version $VERSION (push a tag to drive a real release version)"
|
||||
fi
|
||||
fi
|
||||
[[ "$VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+) ]] \
|
||||
|| fail "VERSION '$VERSION' is not a semantic version (expected X.Y.Z, optionally with a -pre+meta suffix)"
|
||||
VERSION_CODE=$(( ${BASH_REMATCH[1]} * 10000 + ${BASH_REMATCH[2]} * 100 + ${BASH_REMATCH[3]} ))
|
||||
echo "Version: $VERSION (Android versionCode $VERSION_CODE)"
|
||||
|
||||
# Patch versions into the tracked source files for this build only, then
|
||||
# restore them — dist.sh must never leave the working tree dirty.
|
||||
restore_version_files() {
|
||||
git -C "$ROOT" checkout -- "$GRADLE_KTS" "$WATCH_PKG_JSON" 2>/dev/null || true
|
||||
}
|
||||
trap restore_version_files EXIT
|
||||
|
||||
sed -i \
|
||||
-e "s/versionCode = [0-9]\+/versionCode = $VERSION_CODE/" \
|
||||
-e "s/versionName = \"[^\"]*\"/versionName = \"$VERSION\"/" \
|
||||
"$GRADLE_KTS"
|
||||
sed -i -e "s/\"version\": \"[^\"]*\"/\"version\": \"$VERSION\"/" "$WATCH_PKG_JSON"
|
||||
|
||||
mkdir -p "$DIST"
|
||||
|
||||
echo "== Android companion app =="
|
||||
cd "$COMPANION"
|
||||
./gradlew bundleRelease assembleRelease
|
||||
|
||||
cp -f app/build/outputs/bundle/release/app-release.aab "$DIST/schwert-und-magie-$VERSION.aab"
|
||||
cp -f app/build/outputs/apk/release/app-release.apk "$DIST/schwert-und-magie-$VERSION.apk"
|
||||
|
||||
echo "== Pebble watch app =="
|
||||
cd "$WATCH"
|
||||
pebble build
|
||||
|
||||
cp -f build/SchwertUndMagieOnPebbleWatchApp.pbw "$DIST/schwert-und-magie-$VERSION.pbw"
|
||||
|
||||
echo "== Verifying APK signature =="
|
||||
APK="$DIST/schwert-und-magie-$VERSION.apk"
|
||||
APKSIGNER="$(compgen -G "$SDK_DIR/build-tools/*/apksigner" | sort -V | tail -1 || true)"
|
||||
if [ -z "$APKSIGNER" ]; then
|
||||
warn "apksigner not found under $SDK_DIR/build-tools — could not verify APK signature"
|
||||
elif ! "$APKSIGNER" verify "$APK" >/dev/null 2>&1; then
|
||||
warn "$APK is UNSIGNED (failed apksigner verification) — not installable/publishable as-is"
|
||||
elif "$APKSIGNER" verify --print-certs "$APK" 2>/dev/null | grep -qi "CN=Android Debug"; then
|
||||
warn "$APK is signed with the Android debug cert, not the release keystore"
|
||||
fi
|
||||
|
||||
echo "== Done =="
|
||||
ls -la "$DIST"
|
||||
+137
-21
@@ -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,100 @@ 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 self-hosted: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 (`self-hosted`) must match `runs-on:` in
|
||||
`.gitea/workflows/release.yml` — edit both together if you rename it.
|
||||
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 +340,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 +381,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
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Copy to registry.env (gitignored, never commit the real values) and fill in.
|
||||
# Used by build-image.sh to push the build environment image to cr.ladkau.de.
|
||||
REGISTRY=cr.ladkau.de
|
||||
REGISTRY_IMAGE=cr.ladkau.de/schwert-und-magie/builder
|
||||
REGISTRY_USER=
|
||||
REGISTRY_PASSWORD=
|
||||
Reference in New Issue
Block a user