Publishing a release
This page is for whoever publishes a release of the app: the number, the what’s-new notes, the licences list and the signed Android package. You do not need it to contribute code.
Before each release
Section titled “Before each release”-
The version number, in four places that have to match:
versioninCargo.toml(the[workspace.package]section, at the root);versioninapp/package.json;versioninapp/src-tauri/tauri.conf.json;- the key for that version in
app/src/whats-new.json(see point 2).
If the first non-zero number changes (from
0.2.xto0.3.0, for example), also change the version the app asks of the domain crate,kosmotheka-domain = { version = "…" }inapp/src-tauri/Cargo.toml: with a leading0., Cargo does not take a0.3as compatible with a0.2, and the build fails.The lock files (
Cargo.lock,app/package-lock.json) also carry the version: they are updated when you build or withnpm install.npm run check:whats-newcompares the versions inCargo.toml,package.jsonandtauri.conf.json.Every release uploaded to Google Play has to have a higher number: Google rejects a repeated
versionCode. Tauri calculates it fromversion(0.1.0→1000). -
The what’s-new notes. The lines about what changes for people live in
app/src/whats-new.json, under the keynextwhile the version has no number. When you choose it, renamenextto the number ("0.2.1", for example). Thencd app && npm run check:whats-newhas to pass: it checks that the app’s version has its entry, not that the entry is complete; read it. -
The licences list, with the exact versions.
Ventana de terminal cd appnpm run licenses # needs Docker and the networknpm run check:licenses:exactIn continuous integration the check only looks at package names, so that automatic dependency updates pass on their own. Before publishing, the «Licences» screen has to carry the versions that are published. If
app/src/licenses.jsonchanges, it goes in the release commit. -
The usual checks:
cargo fmt --all,cargo clippy --all-targets -- -D warnings,cargo test, and inapp/:npm run typecheck,npm run lintandnpm run check:spec(see Setting up the environment: the app).
With the workflow
Section titled “With the workflow”.github/workflows/release.yml builds and publishes only the app (D74): the server, the website and the documentation are deployed by hand with deploy/deploy.sh.
- Get the version ready (the “Before each release” section) and push it to
main. - Create and push the tag, which must be
vplus the version intauri.conf.json(for examplev0.3.0); if it does not match, the workflow fails before building. - The workflow does, in this order:
- Check the version: the versions of the three files and the entry in
whats-new.json. - Linux (Ubuntu 22.04):
.deb,.rpmand AppImage. Windows: the installer. The names carry no version:Kosmotheka-linux-x86_64.AppImage,Kosmotheka-linux-amd64.deb,Kosmotheka-linux-x86_64.rpm,Kosmotheka-windows-x64-setup.exe. - The draft release, with the four files, a
SHA256SUMSand, as its body, the English lines of the version inwhats-new.json. - Android: builds the
.aab, checks that it is signed and uploads it to Google Play as a draft release, to the track named by thePLAY_TRACKvariable of thereleaseenvironment (internalif it does not exist;alphais the closed test).
- Check the version: the versions of the three files and the entry in
- Review and publish the draft on GitHub (Releases). Until then nobody sees it, and the
…/releases/latest/download/…links still give the previous version. - In Google Play, open the track, review the draft release, add the notes (they come from
whats-new.json) and send it for review.
To rehearse without publishing, run the workflow by hand (Actions › Release › Run workflow): it builds everything and keeps the files as workflow artifacts (14 days), but creates no release and uploads nothing to Google Play. Without the Android secrets, the .aab is built unsigned and the summary says so.
The secrets and the environment are set up once, in the public repository, by whoever maintains it. Everything below, done by hand, still works if the workflow is not available.
The upload key
Section titled “The upload key”Google Play uses Play App Signing: the key that signs the app installed on phones is generated and kept by Google. Whoever publishes only has an upload key, which shows Google that the package is theirs. If it is lost or leaked, Google allows changing it (in Play Console, App integrity), but it has to be kept safe.
What is uploaded is an Android App Bundle (.aab), not an APK; Google generates the APK for each phone from it.
The key lives outside the repository. By default, in ~/.kosmotheka-release/:
~/.kosmotheka-release/ upload.jks the upload key keystore.properties where it is, and its passwordsapp/src-tauri/gen/android/app/build.gradle.kts reads keystore.properties from that folder, or from the path given in the KOSMOTHEKA_KEYSTORE_PROPERTIES environment variable. If the file does not exist, the release build comes out unsigned. The repository’s .gitignore ignores *.jks, *.keystore and keystore.properties anywhere, just in case. Never commit them.
Creating it (once)
Section titled “Creating it (once)”In your own terminal, and with a password you write yourself:
mkdir -p ~/.kosmotheka-release && chmod 700 ~/.kosmotheka-releasekeytool -genkeypair -v \ -keystore ~/.kosmotheka-release/upload.jks \ -keyalg RSA -keysize 2048 -validity 10000 -alias uploadIt asks for a password (twice) and some details (name, organisation, country). If at the end it asks for another password for the key, press Enter to use the same one. Then create ~/.kosmotheka-release/keystore.properties:
storeFile=/full/path/to/upload.jksstorePassword=<the password>keyAlias=uploadkeyPassword=<the password>and protect it: chmod 600 ~/.kosmotheka-release/*. Keep the password in a password manager and a copy of upload.jks off the computer.
Building the .aab
Section titled “Building the .aab”cd appnpm run tauri android build -- --aabIt builds for the four architectures and takes a while. The result is in:
app/src-tauri/gen/android/app/build/outputs/bundle/universalRelease/app-universal-release.aabCheck that it is signed with the upload key (it asks for no password):
keytool -printcert -jarfile app/src-tauri/gen/android/app/build/outputs/bundle/universalRelease/app-universal-release.aabIt must show the certificate with the details you gave when creating the key. If there is no certificate, the package came out unsigned: keystore.properties is missing or wrong.
To build with the Android tools, follow Setting up the environment: the app.
Uploading it
Section titled “Uploading it”You upload it yourself, by hand, from the Google Play console: under Testing you choose the track, Create release, upload the .aab, write the release notes and publish. Google Play’s notes (500 characters per language at most) come from the lines in app/src/whats-new.json.
A new personal Google Play account needs, before production, a closed test with at least 12 people for 14 days.
Something that surprises people
Section titled “Something that surprises people”The Google Play version and a debug APK have the same application identifier but a different signature: Android does not let you install one over the other. To go from the debug APK to the Play one you have to uninstall the first, and that deletes its data. Before that, make a backup (or have the libraries on another paired device) and restore it or pair again afterwards.
© 2026 Kosmotheka
Kosmotheka is free software: the app and the server are under the AGPL-3.0-or-later, and the operation spec is under the MIT licence. The name and the logo are not covered by the licence.
kosmotheka.com · Trademark policy · Privacy policy · info@kosmotheka.com