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). 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. Supports **multiple accounts** — each synced independently via IMAP IDLE.
## Design philosophy: offline-first ## 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 ## 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
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. when sending mail.
--- ---
+13
View File
@@ -268,6 +268,19 @@ tasks:
cmds: 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" - 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: deploy-android:
desc: Build release APK and upload via scp to $ANDROID_APK_SCP_USER@$ANDROID_APK_SCP_HOST:$ANDROID_APK_SCP_PATH 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] deps: [check, build-android]
+22 -8
View File
@@ -6,7 +6,7 @@ plugins {
} }
android { android {
namespace = "com.example.sharedinbox" namespace = "de.sharedinbox.mua"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = flutter.ndkVersion
@@ -19,9 +19,20 @@ android {
jvmTarget = JavaVersion.VERSION_17.toString() 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 { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId = "de.sharedinbox.mua"
applicationId = "com.example.sharedinbox"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion minSdk = flutter.minSdkVersion
@@ -32,11 +43,14 @@ android {
buildTypes { buildTypes {
release { release {
// TODO: Add your own signing config for the release build. // Use the signing config defined above for release builds.
// Signing with the debug keys for now, so `flutter run --release` works. // If the keystore file exists (e.g. in CI or manually placed), sign it.
signingConfig = signingConfigs.getByName("debug") signingConfig = if (signingConfigs.getByName("release").storeFile?.exists() == true) {
// Minification is disabled: R8 strips plugin classes (flutter_secure_storage, signingConfigs.getByName("release")
// sqlite3) that are needed at runtime, causing an immediate crash on launch. } else {
signingConfigs.getByName("debug")
}
isMinifyEnabled = false isMinifyEnabled = false
isShrinkResources = false isShrinkResources = false
} }
@@ -1,4 +1,4 @@
package com.example.sharedinbox package de.sharedinbox.mua
import io.flutter.embedding.android.FlutterActivity 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). # 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. # 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 if "$ADB" -s "$EMULATOR_ID" shell pm list packages | grep -qF "de.sharedinbox.mua"; then
"$ADB" -s "$EMULATOR_ID" shell am force-stop com.example.sharedinbox "$ADB" -s "$EMULATOR_ID" shell am force-stop de.sharedinbox.mua
"$ADB" -s "$EMULATOR_ID" shell pm clear com.example.sharedinbox "$ADB" -s "$EMULATOR_ID" shell pm clear de.sharedinbox.mua
"$ADB" -s "$EMULATOR_ID" uninstall com.example.sharedinbox "$ADB" -s "$EMULATOR_ID" uninstall de.sharedinbox.mua
fi fi
ts "flutter test start" ts "flutter test start"