From bbc92278f4f4cee86da02a86bef6a0897fb28509 Mon Sep 17 00:00:00 2001 From: Karim Abdul-Samad Date: Thu, 30 Jul 2026 20:13:32 -0400 Subject: [PATCH] feat: add keystore properties pipeline --- .gitignore | 5 +++++ app/build.gradle.kts | 28 ++++++++++++++++++++++++++++ keystore.properties.example | 11 +++++++++++ 3 files changed, 44 insertions(+) create mode 100644 keystore.properties.example diff --git a/.gitignore b/.gitignore index 3bb9e76..c355a63 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,11 @@ build/ *.aab local.properties +# Release signing — NEVER commit the key or its passwords +keystore.properties +*.jks +*.keystore + # Nix result result-* diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 03573f0..e1e5ec8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,9 +1,21 @@ +import java.util.Properties + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) } +// Release signing is loaded from keystore.properties, which exists only on a machine +// that owns the signing key (never committed — see .gitignore). F-Droid and CI have no +// such file and build unsigned; that unsigned output is exactly what F-Droid's +// reproducible-build check compares against before re-applying our signature. +val keystorePropertiesFile = rootProject.file("keystore.properties") +val keystoreProperties = + Properties().apply { + if (keystorePropertiesFile.exists()) keystorePropertiesFile.inputStream().use { load(it) } + } + android { namespace = "ca.ksamad.encore" compileSdk = 35 @@ -21,8 +33,24 @@ android { versionName = "0.1.0" } + signingConfigs { + // Present only when the developer keystore is configured; absent on F-Droid/CI. + if (keystorePropertiesFile.exists()) { + create("release") { + storeFile = file(keystoreProperties.getProperty("storeFile")) + storePassword = keystoreProperties.getProperty("storePassword") + keyAlias = keystoreProperties.getProperty("keyAlias") + keyPassword = keystoreProperties.getProperty("keyPassword") + } + } + } + buildTypes { release { + // Sign with the release key when configured; otherwise stay unsigned so + // F-Droid can build/verify reproducibly and re-apply our signature. + signingConfig = signingConfigs.findByName("release") + // R8 code shrinking + resource shrinking. Critical with // material-icons-extended: it bundles thousands of vector icons, and // R8 strips everything we don't reference (debug builds can't shrink, diff --git a/keystore.properties.example b/keystore.properties.example new file mode 100644 index 0000000..be876c4 --- /dev/null +++ b/keystore.properties.example @@ -0,0 +1,11 @@ +# Copy this file to `keystore.properties` (which is gitignored) and fill in your +# real values. This file — the .example — carries NO secrets and is safe to commit. +# +# `storeFile` is the path to your release keystore. Keep the keystore OUTSIDE the +# repo (e.g. ~/.keys/encore-release.jks) and back it up offline — losing it means +# you can never ship another signed update on your own channels. + +storeFile=/absolute/path/to/encore-release.jks +storePassword=CHANGE_ME +keyAlias=encore +keyPassword=CHANGE_ME