# Publishing guide Steps to generate signing credentials and publish both apps. See `docs/architecture.md` for what each app does and `CLAUDE.md` for build commands. ## 1. .gitignore Keystore files and credential properties must never be committed. Already added to `.gitignore`: ``` *.jks *.keystore keystore.properties ``` If you generate a keystore with a different name/extension, add that specific path too — don't rely on a broad glob you might forget to check. ## 2. Android companion app → Google Play ### 2.1 Generate an upload keystore ```bash keytool -genkeypair -v \ -keystore SchwertUndMagieOnPebbleCompanionApp/release.keystore \ -alias sum-release \ -keyalg RSA -keysize 2048 -validity 10000 ``` `keytool` will prompt for a keystore password, a key password (can be the same), and your name/org details for the certificate (these become public metadata in the signed APK, not secret). Store the keystore file and both passwords in a password manager — **losing this keystore means you can never publish an update to the same Play Store listing again** under the same app; Google cannot recover or reset it for you. ### 2.2 Store credentials in a gitignored properties file Create `SchwertUndMagieOnPebbleCompanionApp/keystore.properties` (already gitignored, never commit it): ```properties storeFile=release.keystore storePassword= keyAlias=sum-release keyPassword= ``` ### 2.3 Wire the signing config Add to `SchwertUndMagieOnPebbleCompanionApp/app/build.gradle.kts`, near the top: ```kotlin import java.util.Properties val keystoreProps = Properties().apply { rootProject.file("keystore.properties").takeIf { it.exists() } ?.reader()?.use { load(it) } } ``` Inside the `android { }` block: ```kotlin signingConfigs { create("release") { keystoreProps["storeFile"]?.let { storeFile = file(it as String) } storePassword = keystoreProps["storePassword"] as String? keyAlias = keystoreProps["keyAlias"] as String? keyPassword = keystoreProps["keyPassword"] as String? } } buildTypes { release { signingConfig = signingConfigs.getByName("release") // ...existing isMinifyEnabled / proguardFiles } } ``` This degrades gracefully (null signing config values) on any machine without `keystore.properties` — CI or a contributor's checkout — rather than failing 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 ``` Play Store requires the **AAB** (`bundleRelease` output), 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). ### 2.5 Play Console setup (one-time, per app listing) 1. Enroll in the Google Play Developer program (one-time account fee). 2. Create the app in Play Console, set its package name (`de.ladkau.schwertundmagieonpebblecompanionapp`) — this is permanent. 3. **App content**: privacy policy URL, content rating questionnaire, data safety form (declare what data the app collects — this app's only network activity is the loopback HTTP server talking to Core for Pebble, so "no data collected/shared" likely applies, but fill out the form yourself). 4. **Store listing**: title, short/full description, icon (512×512 PNG), feature graphic (1024×500), phone screenshots (min 2, current device's actual aspect ratio). 5. Enroll in **Play App Signing** when prompted on first upload — Google re-signs your AAB with its own key for distribution; your upload keystore (§2.1) only needs to be kept for *future uploads to this listing*, not for the keys end users' devices actually trust. 6. Upload the AAB to an **internal testing** track first, verify the install works on a real device, then promote to closed/open testing or production. ### 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 ``` ## 3. Android companion app → F-Droid F-Droid is a community-run FOSS app catalogue that builds apps from source on its own infrastructure and signs them with its own key. The companion app is a good fit: it is open source, ships no proprietary binaries in the repo, and the copyrighted ROM and disk files are handled entirely at runtime by the user. ### 3.1 Inclusion policy check F-Droid's key requirement is that everything needed to build the app is in the repo and is free/open-source. Verify: - VICE 3.8 source is GPLv2 (tarball in `res/`). ✓ - nibtools source is GPLv2 (tarball in `res/`). ✓ - No prebuilt binaries committed (`.a`/`.so` files are build outputs in `.gitignore`). ✓ - No non-free SDKs or closed-source dependencies in `build.gradle.kts`. ✓ - C64 ROMs and game disks are not bundled — user-supplied at runtime. ✓ ### 3.2 Prepare the fdroiddata metadata file F-Droid apps are registered by adding a YAML file to the [fdroiddata](https://gitlab.com/fdroid/fdroiddata) repository. Fork it, then create `metadata/de.ladkau.schwertundmagieonpebblecompanionapp.yml`: ```yaml Categories: - Games License: GPL-2.0-or-later AuthorName: Matthias Ladkau SourceCode: https://github.com/ IssueTracker: https://github.com//issues AutoName: Schwert und Magie on Pebble RepoType: git Repo: https://github.com/ Builds: - versionName: '1.0' versionCode: 1 commit: subdir: SchwertUndMagieOnPebbleCompanionApp gradle: - release ndk: 30.0.14904198 prebuild: - bash app/src/main/jni/build_vice.sh - bash app/src/main/jni/build_nibtools.sh AutoUpdateMode: None UpdateCheckMode: None ``` Key points: - `subdir` points to the Gradle project root (where `gradlew` lives). - `gradle: [release]` tells F-Droid to run `./gradlew assembleRelease`. - `ndk` must match the `ndkVersion` in `app/build.gradle.kts` (`30.0.14904198`). F-Droid's build server installs the requested NDK version automatically. - `prebuild` runs the VICE and nibtools cross-compilation before Gradle invokes CMake. The `NDK` environment variable is set by F-Droid's build server, matching what `build_vice.sh` and `build_nibtools.sh` expect. - F-Droid signs with its own key, so `keystore.properties` is not needed on the build server. The signing config in `build.gradle.kts` degrades gracefully when the file is absent. ### 3.3 Test the build locally with fdroid-server Before submitting, reproduce the F-Droid build environment locally: ```bash # Install fdroid-server (Debian/Ubuntu) sudo apt install fdroidserver # In the fdroiddata checkout: fdroid build de.ladkau.schwertundmagieonpebblecompanionapp --latest ``` This runs the build inside a Docker container that mirrors the F-Droid build server. A successful local build means the merge request is very likely to pass. ### 3.4 Submit the merge request Push your `metadata/` addition to your fdroiddata fork and open a merge request against `gitlab.com/fdroid/fdroiddata`. The F-Droid team reviews the metadata and, if the build passes on their infrastructure, merges it and 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`. ## 5. Pebble watch app → Rebble app store / direct distribution The official Pebble app store shut down years ago; the community-run **Rebble** store is the closest equivalent today, alongside the 2025 Core Devices relaunch of Pebble hardware. Check Rebble's current developer portal directly for their exact submission flow and requirements — that's outside this repo and changes independently of it. ### 5.1 Build the artifact ```bash cd SchwertUndMagieOnPebbleWatchApp pebble build # produces build/SchwertUndMagieOnPebbleWatchApp.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. ### 5.2 Direct distribution (no store) Anyone with the `.pbw` file and Core for Pebble installed can sideload it — this is the lowest-friction path and doesn't depend on any third party's store being operational. This watch app is tightly coupled to the companion app's HTTP bridge (see `docs/architecture.md` §1), so it's not really meaningful as a standalone listing anyway — distribute both together. ### 5.3 Store submission (if Rebble's process applies) Expect to need: an app icon resource (not yet configured in `package.json` — there's no `icon`/menu-icon entry currently, only the in-app `IMAGE_SPLASH` bitmap), a short description, and screenshots per platform. Generate screenshots the same way used during development: ```bash pebble install --emulator emery --vnc pebble screenshot --vnc --no-open docs/screenshots/emery.png ``` (repeat per target platform you want a store screenshot for). ### 5.4 Versioning Bump `version` in `SchwertUndMagieOnPebbleWatchApp/package.json` before each release build. ## 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) - [ ] 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