mirror of
https://github.com/flectar/mail.git
synced 2026-11-11 17:57:54 +00:00
152 lines
7.5 KiB
Markdown
152 lines
7.5 KiB
Markdown
# Android signing and updates
|
||
|
||
Every published APK uses `com.flectar.mail` and the certificate pinned in
|
||
`platform/android/app-signing-certificate.sha256`. This includes alpha, beta,
|
||
release-candidate, and stable versions, starting with alpha 8. Keep the original
|
||
private key **and certificate** for the lifetime of this update path. Alpha 6
|
||
and alpha 7 used different identities and cannot update to this one.
|
||
|
||
The certificate's historical subject (`Flectar Mail Test`) and alias
|
||
(`androiddebugkey`) are names, not a measure of key security. The pin identifies
|
||
the actual certificate. Re-encrypting the existing keystore with a strong
|
||
password keeps the certificate and private key unchanged.
|
||
|
||
## One-time setup from the existing alpha key
|
||
|
||
Requirements: Python 3.11+, a JDK with `keytool` (Java 21 recommended), and
|
||
authenticated GitHub CLI access with permission to manage repository
|
||
environments and secrets. Keep keystores outside the checkout.
|
||
|
||
From the repository root:
|
||
|
||
```bash
|
||
python3 scripts/configure-android-signing.py \
|
||
"$HOME/.local/share/flectar-mail/android/alpha-signing.keystore" \
|
||
--output "$HOME/.local/share/flectar-mail/android/app-signing.p12" \
|
||
--upload
|
||
```
|
||
|
||
Use the path to your **existing backup** if it is elsewhere. The current
|
||
legacy keystore password is `android`; press Enter for the same key password.
|
||
Choose and save a new password of at least 16 characters in your password
|
||
manager. The script does not display or save passwords in plaintext.
|
||
|
||
The script verifies the original certificate and access to its private key,
|
||
imports that same entry into a new PKCS12 file with the new password, and
|
||
verifies it again. It refuses to overwrite an existing destination. The
|
||
original file is retained until you have verified your secure backup.
|
||
|
||
With `--upload`, it creates the `android-release` environment if absent,
|
||
restricts that environment to the repository's default branch and `v*` tags,
|
||
and sets these four **environment secrets**:
|
||
|
||
| Secret | Value |
|
||
| --- | --- |
|
||
| `FLECTAR_ANDROID_KEYSTORE_BASE64` | Base64 encoding of the re-encrypted keystore |
|
||
| `FLECTAR_ANDROID_KEYSTORE_PASSWORD` | New keystore password |
|
||
| `FLECTAR_ANDROID_KEY_ALIAS` | Existing alias, normally `androiddebugkey` |
|
||
| `FLECTAR_ANDROID_KEY_PASSWORD` | New private-key password; same as the store password for this PKCS12 file |
|
||
|
||
An existing environment's protection rules are preserved. An environment with
|
||
no branch restrictions is rejected. Review the environment's allowed branches
|
||
and tags when configuring a different repository. An empty edit field on
|
||
GitHub does not show whether a stored secret has a value: saved secret values
|
||
are not returned for inspection. Base64 is encoding, not encryption.
|
||
|
||
The old repository secret `FLECTAR_ANDROID_TEST_KEYSTORE_BASE64` belongs to
|
||
older workflow revisions. After the four environment secrets are configured
|
||
and the new workflows are merged, remove the old repository secret so the
|
||
same private key is no longer available outside the protected environment:
|
||
|
||
```bash
|
||
gh secret delete FLECTAR_ANDROID_TEST_KEYSTORE_BASE64 --repo flectar/mail
|
||
```
|
||
|
||
Do not dispatch releases from older workflow revisions after this migration;
|
||
some of those revisions could generate a replacement key when a secret was
|
||
missing. The setup script leaves the old secret in place so it can be run
|
||
before merging without breaking the workflow currently on `main`.
|
||
|
||
If upload fails after the new file was created, retry without re-encrypting:
|
||
|
||
```bash
|
||
python3 scripts/configure-android-signing.py \
|
||
"$HOME/.local/share/flectar-mail/android/app-signing.p12" --upload
|
||
```
|
||
|
||
Enter the **new** password this time. To update the secrets manually after
|
||
configuring the environment:
|
||
|
||
```bash
|
||
set -o pipefail
|
||
base64 < "$HOME/.local/share/flectar-mail/android/app-signing.p12" |
|
||
gh secret set FLECTAR_ANDROID_KEYSTORE_BASE64 --repo flectar/mail --env android-release
|
||
gh secret set FLECTAR_ANDROID_KEYSTORE_PASSWORD --repo flectar/mail --env android-release
|
||
gh secret set FLECTAR_ANDROID_KEY_ALIAS --repo flectar/mail --env android-release
|
||
gh secret set FLECTAR_ANDROID_KEY_PASSWORD --repo flectar/mail --env android-release
|
||
```
|
||
|
||
The final three commands prompt for the corresponding value. Do not pass
|
||
passwords on the command line or paste them into issues, chat, or logs.
|
||
|
||
## Release behavior
|
||
|
||
Both workflows restore and validate the shared key before compilation, use
|
||
`scripts/build-android-release.sh`, verify the finished APK against the same
|
||
pin, and remove the runner's keystore before emulator tests. Signing passwords
|
||
are supplied only to the restore and build steps. Pull request checks use
|
||
temporary fixture keys and never receive the release environment's secrets.
|
||
|
||
The native artifact workflow publishes the signed APK for both prereleases
|
||
and stable tags. `--preview` permits missing bundled OAuth registrations only
|
||
for prerelease versions; it never changes the signing key. The production
|
||
candidate workflow requires complete OAuth registrations. Both emit an APK
|
||
and an AAB into `target/android/release/`; the native release publishes the APK.
|
||
|
||
Version codes are derived by `scripts/android-version-code.py`:
|
||
|
||
```text
|
||
major × 100,000,000 + minor × 1,000,000 + patch × 10,000 + channel
|
||
channel: alpha.N = N; beta.N = 1,000 + N; rc.N = 2,000 + N; stable = 9,999
|
||
```
|
||
|
||
For example, `0.1.0-alpha.8` is `1000008` and `0.1.0` is `1009999`.
|
||
The supported ranges are major 0–20, minor and patch 0–99, and prerelease
|
||
number 1–999; values outside these ranges fail rather than collide. Every
|
||
released version must be greater than the previous release on the update path.
|
||
Rebuilding the same version produces the same code; use a new version for a
|
||
new Play upload. Do not supply unrelated workflow run numbers or reset codes
|
||
when moving to stable.
|
||
|
||
Local `scripts/build-android-test-apk.sh` builds may still use the conventional
|
||
`android` password for a developer-only key. CI redirects that entry point to
|
||
the pinned release builder, and no published workflow uses the developer key.
|
||
|
||
## Backups, recovery, and Google Play
|
||
|
||
Store the re-encrypted keystore in a secure backup outside this machine and
|
||
GitHub, and store its password in your password manager. Test the backup with
|
||
the verification/setup command before retiring the legacy copy. GitHub
|
||
secrets are not a downloadable backup. If a secret is deleted, restore the
|
||
same keystore and credentials. Never regenerate the key or change the pin to
|
||
make a failed release pass.
|
||
|
||
When enrolling in Play App Signing, supply this existing **app signing key**
|
||
so that Play and GitHub APKs have the same signing identity. Having Google
|
||
generate a different app key would break this update path. Configure a
|
||
separate **upload key** for later AAB submissions; that key authorizes uploads
|
||
and must not replace the key signing GitHub APKs. The current AAB is signed
|
||
with the app key for initial enrollment; separate upload-key automation must
|
||
be configured when Play is introduced.
|
||
|
||
Before shipping, verify an actual update on an emulator or test device:
|
||
install a first signed APK, create test app data, and install the newer APK
|
||
without uninstalling (`adb install -r newer.apk`). Confirm that the data and
|
||
account settings survive and that both APK certificate fingerprints match
|
||
the committed pin. The keystore regression tests alone do not exercise the
|
||
Android package manager or application data migrations.
|
||
|
||
References: [Android app signing](https://developer.android.com/studio/publish/app-signing),
|
||
[version codes](https://developer.android.com/studio/publish/versioning), and
|
||
[GitHub Actions secrets](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets).
|