Deploy via CI.

This commit is contained in:
Thomas SharedInbox
2026-05-12 08:15:47 +02:00
parent 7017947a9f
commit d4902d1395
6 changed files with 85 additions and 17 deletions
+41
View File
@@ -0,0 +1,41 @@
name: Release
on:
push:
branches: [main]
jobs:
deploy-playstore:
name: Build & Deploy to Play Store
runs-on: self-hosted
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Prepare Keystore
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
run: |
if [ -n "$ANDROID_KEYSTORE_BASE64" ]; then
echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > android/app/upload-keystore.jks
else
echo "Error: ANDROID_KEYSTORE_BASE64 secret is not set."
exit 1
fi
- name: Build App Bundle
env:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
run: nix develop --command task build-android-bundle
# Placeholder for Play Store upload.
# Once you have the Service Account JSON and everything is ready,
# you can uncomment this or use a specialized action.
# - name: Upload to Play Store
# env:
# PLAY_STORE_CONFIG_JSON: ${{ secrets.PLAY_STORE_CONFIG_JSON }}
# run: |
# echo "$PLAY_STORE_CONFIG_JSON" > play-store-key.json
# # Example using a tool like 'fastlane supply' or a dedicated flutter package
# # nix develop --command fvm flutter pub run ...
+4 -4
View File
@@ -2,7 +2,7 @@
IMAP/SMTP email client written in [Flutter](https://flutter.dev).
Targets **Android, iOS, and Desktop** (Linux done; macOS, Windows, Android, iOS scaffolded).
Targets **Android, iOS, and Desktop** (Linux done; macOS, Windows, Android, iOS scaffolded).
Supports **multiple accounts** — each synced independently via IMAP IDLE.
## Design philosophy: offline-first
@@ -41,9 +41,9 @@ The UI never touches the network. The sync engine runs in the background and wri
## For users
Download the latest release from the [Releases page](https://github.com/guettli/sharedinbox3/releases) *(not yet published)*.
Run the app, tap **+**, and enter your IMAP/SMTP server details. The app syncs your INBOX in the background using IMAP IDLE and works offline — the network is only needed during initial sync and when sending mail.
Run the app, tap **+**, and enter your IMAP/SMTP server details. The app syncs your INBOX in the
background using IMAP IDLE and works offline — the network is only needed during initial sync and
when sending mail.
---
+13
View File
@@ -268,6 +268,19 @@ tasks:
cmds:
- ANDROID_HOME=${ANDROID_HOME:-$HOME/Android/Sdk} fvm flutter build apk --release --no-pub | grep -Ev "was tree-shaken|Tree-shaking can be disabled"
build-android-bundle:
desc: Build a release App Bundle (AAB)
deps: [_preflight, _android-sdk-check, _pub-get, generate-changelog]
method: timestamp
sources:
- lib/**/*.dart
- android/**/*
- pubspec.yaml
generates:
- build/app/outputs/bundle/release/app-release.aab
cmds:
- ANDROID_HOME=${ANDROID_HOME:-$HOME/Android/Sdk} fvm flutter build appbundle --release --no-pub | grep -Ev "was tree-shaken|Tree-shaking can be disabled"
deploy-android:
desc: Build release APK and upload via scp to $ANDROID_APK_SCP_USER@$ANDROID_APK_SCP_HOST:$ANDROID_APK_SCP_PATH
deps: [check, build-android]
+22 -8
View File
@@ -6,7 +6,7 @@ plugins {
}
android {
namespace = "com.example.sharedinbox"
namespace = "de.sharedinbox.mua"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
@@ -19,9 +19,20 @@ android {
jvmTarget = JavaVersion.VERSION_17.toString()
}
signingConfigs {
create("release") {
// Hardcoded alias matching t.sh
keyAlias = "upload"
// Use the same password for both key and keystore
val pass = System.getenv("ANDROID_KEYSTORE_PASSWORD")
storePassword = pass
keyPassword = pass
storeFile = file("upload-keystore.jks")
}
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.sharedinbox"
applicationId = "de.sharedinbox.mua"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
@@ -32,11 +43,14 @@ android {
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
// Minification is disabled: R8 strips plugin classes (flutter_secure_storage,
// sqlite3) that are needed at runtime, causing an immediate crash on launch.
// Use the signing config defined above for release builds.
// If the keystore file exists (e.g. in CI or manually placed), sign it.
signingConfig = if (signingConfigs.getByName("release").storeFile?.exists() == true) {
signingConfigs.getByName("release")
} else {
signingConfigs.getByName("debug")
}
isMinifyEnabled = false
isShrinkResources = false
}
@@ -1,4 +1,4 @@
package com.example.sharedinbox
package de.sharedinbox.mua
import io.flutter.embedding.android.FlutterActivity
+4 -4
View File
@@ -135,10 +135,10 @@ cd "$ROOT"
# Clear any leftover app state from previous runs (stale DB, cached APK process).
# Only run if the package is installed — that way any failure is a real error.
if "$ADB" -s "$EMULATOR_ID" shell pm list packages | grep -qF "com.example.sharedinbox"; then
"$ADB" -s "$EMULATOR_ID" shell am force-stop com.example.sharedinbox
"$ADB" -s "$EMULATOR_ID" shell pm clear com.example.sharedinbox
"$ADB" -s "$EMULATOR_ID" uninstall com.example.sharedinbox
if "$ADB" -s "$EMULATOR_ID" shell pm list packages | grep -qF "de.sharedinbox.mua"; then
"$ADB" -s "$EMULATOR_ID" shell am force-stop de.sharedinbox.mua
"$ADB" -s "$EMULATOR_ID" shell pm clear de.sharedinbox.mua
"$ADB" -s "$EMULATOR_ID" uninstall de.sharedinbox.mua
fi
ts "flutter test start"