Releasing
The three release channels, what each build flag does, and the exact steps for GitHub, F-Droid and Play.
WM Keyboard ships through three channels, and they are not the same build. The differences are real (a Play build carries Google libraries that an F-Droid build must not have) but they are all driven by two flags and a flavor, so nothing here needs a branch or a patched source tree.
What the channels actually differ on
Section titled “What the channels actually differ on”| GitHub release | F-Droid | Play Store | |
|---|---|---|---|
| Flavor | full | lite | full |
wmkb.enablePlayStore | false | false | true |
wmkb.enableGms | true | false | true |
wmkb.splitApks | true | not used | ignored by bundle* |
| Artifact | one APK per ABI | one APK, built by F-Droid | one AAB |
| ML Kit | bundled in the APK | absent | unbundled, fetched from Play services |
| Local LLM | bundled | absent | on-demand :feature:llm module |
| In-app updates | no | no | yes |
The flags read from local.properties first, then the environment, then
default to false. A clean checkout has no local.properties, which is why
an F-Droid builder gets the right answer without being told.
Cutting a GitHub release
Section titled “Cutting a GitHub release”The Release workflow
does the build. It runs on any tag starting with v, refuses to run if the
tag disagrees with wmkb.versionName, signs with your release key, and
attaches one APK per ABI plus a SHA256SUMS.txt to the release.
One-time setup
Section titled “One-time setup”-
Base64 your keystore so it can live in a secret:
Terminal window base64 -i release.keystore | pbcopy -
Add the signing secrets under Settings → Secrets and variables → Actions → New repository secret:
Secret Value KEYSTORE_BASE64the base64 blob from step 1 RELEASE_STORE_PASSWORDkeystore password RELEASE_KEY_ALIASkey alias RELEASE_KEY_PASSWORDkey password -
Add the API-key secrets you want baked into public builds:
WMKB_KLIPY_API_KEY,WMKB_GIPHY_API_KEY,WMKB_BRAVE_API_KEY,WMKB_TRANSLATE_API_KEY,WMKB_UNSPLASH_API_KEY,WMKB_PEXELS_API_KEY,WMKB_DROPBOX_APP_KEY. Any you leave out simply make that tool show its “needs an API key” state, so a release without them still builds and runs.
Every release
Section titled “Every release”-
Bump the version.
wmkb.versionCodeandwmkb.versionNameingradle.properties. The code must increase; the name is what the tag has to match. -
Write the changelog.
fastlane/metadata/android/en-US/changelogs/<versionCode>.txt. F-Droid and Play both read this file, so it is the only place you write it. -
Tag and push.
Terminal window git tag v0.3.1 && git push origin v0.3.1 -
Watch the run. It builds the APKs, builds the Play AAB as a separate artifact, and publishes the release. The AAB is deliberately not attached to the public release: an AAB is not installable, and pushing to Play stays a decision you make by hand.
Getting into F-Droid
Section titled “Getting into F-Droid”F-Droid does not take your APK. It builds from source on its own machines and
signs with its own key, which is why the lite flavor exists: every
proprietary dependency in the tree (ML Kit, LiteRT, Play’s update and feature
delivery libraries, play-services-auth) is declared fullImplementation or
sits behind a channel flag, so lite has no Google artifact on its compile
classpath at all.
The build recipe is staged in the repo at fdroid/com.wasimaster.wmkeyboard.yml.
F-Droid never reads it from there; it is versioned next to the code so the
recipe and the build stay in step.
-
Check the app is buildable as-is. Nothing generated may be missing from source. The dictionaries are fine here:
.wmdictand.wmngfiles are compiled at build time by:tools:dictc, and no binary dictionary is committed. The only prebuilt binaries in the tree are the Harper.sofiles undercore/intelligence/src/full/and the LLM module’s, both of which belong to variants alitebuild never assembles. The recipe deletes them before building so the scanner has nothing to complain about. -
Test the exact build locally before you submit, because a failed build on their side means a round trip:
Terminal window ./gradlew clean assembleLiteRelease -Pwmkb.enablePlayStore=false -Pwmkb.enableGms=false -
Fork fdroiddata on GitLab and copy the recipe to
metadata/com.wasimaster.wmkeyboard.yml. -
Lint it with their tooling, which catches format problems before a human sees them:
Terminal window fdroid lint com.wasimaster.wmkeyboard -
Open a merge request titled
New app: WM Keyboard. Expect review comments and a wait measured in weeks rather than days. The two questions that come up most are whether every network service is optional (it is, and theNonFreeNetanti-feature in the recipe says so plainly) and whether anything prebuilt ends up in the APK (nothing does). -
After it is accepted, every release is just a new
Builds:entry with the newversionName,versionCodeandcommit, plus a bumpedCurrentVersion. SwitchingUpdateCheckModetoTagslets their bot do that for you once the tag pattern has proven itself.
Publishing to Play
Section titled “Publishing to Play”-
Create the app in the Play Console with package name
com.wasimaster.wmkeyboard. -
Fill in the policy answers before you upload anything, since they gate the release. A keyboard is a sensitive category and reviewers do read these:
- Privacy policy URL:
https://wmkeyboard.pages.dev/privacy/policy/. - Data safety: the honest answer is no collection and no sharing. The optional network tools send what you typed into that tool to the service you chose, which belongs under the tool’s own disclosure rather than as app-wide collection. Read Network policy while filling the form.
- Permissions: every one is optional and requested in context. The prominent-disclosure screens the app already shows are what the policy asks for.
- Privacy policy URL:
-
Build the bundle. The
Releaseworkflow produces it as theplay-bundleartifact, or locally:Terminal window ./gradlew bundleFullRelease -Pwmkb.enablePlayStore=true -Pwmkb.enableGms=true -
Upload to internal testing first. Play’s own device catalogue will tell you within minutes if the on-demand
:feature:llmmodule or the unbundled ML Kit path is misconfigured, and that is much cheaper to learn there than in production. -
Promote when it looks right. Play signs with its own upload key flow, so the APK a user installs is not byte-identical to anything you built. That is expected, and it is why the GitHub release exists for people who want to verify a checksum.
Deploying the docs
Section titled “Deploying the docs”The site at wmkeyboard.pages.dev is a
Cloudflare Pages project connected to this repository, so a push to main
that touches docs/ deploys itself. The settings are:
| Setting | Value |
|---|---|
| Project type | Pages, not Workers |
| Project name | wmkeyboard (this is what makes the hostname) |
| Framework preset | Astro |
| Build command | npm run build |
| Build output directory | dist |
| Root directory | docs |
| Node version | 22 (set NODE_VERSION=22 if the default is older) |
CI builds the site with npm run check on every pull request, which turns on
the link validator, so a broken internal link fails there rather than shipping.
