Build, sign and release
How Android is built on the Windows PC, how iOS is built headlessly on the Mac mini, and how a release reaches the stores.
Updated: 12 September 2026
The process copies Saymail’s, which is known to work on these two machines.
Build-time configuration
As built in phase 2, both apps read the same five values, in the same precedence order — Android: Gradle property > repo-root .env (walked up from the project dir) > environment variable > default; iOS: the repo-root .env read by release/gen-appconfig.sh into the gitignored Incico/Config/AppConfig.generated.swift, blank when absent:
| Value | Meaning |
|---|---|
INCICO_SUPABASE_URL | Supabase project URL; blank builds and runs, but the sign-in screen shows “This build is not configured” rather than crashing |
INCICO_SUPABASE_ANON_KEY | the staging publishable key (sb_publishable_…), not the legacy JWT anon key |
INCICO_ENV | staging | production, default staging |
INCICO_GOOGLE_WEB_CLIENT_ID | Google OAuth web client id, used by both platforms’ native Google sign-in |
INCICO_SOCIAL_PROVIDERS | comma list, default empty; only the providers named here get a Google/Apple button, because those providers are not yet configured on the Supabase side |
.env.example carries all five names with no values. CI supplies real values from repository secrets. There is no confidential value in either app (see Security design); unlike Saymail, neither the URL nor the key is baked into the repository as a default. FCM/APNs configuration is not wired yet (arrives phase 4).
Both apps share one version number and build number from mobile/version.json (for example {"version":"3.0.0","build":1}), read directly by Gradle (versionName/versionCode) and, on iOS, written into Incico/Config/Version.xcconfig by release/sync-version.sh.
Android (Windows PC)
Prerequisites, as for Saymail: JDK 21 (C:\Program Files\Microsoft\jdk-21*), Android SDK with platforms;android-36.1, adb.
$env:JAVA_HOME = "$(Get-ChildItem 'C:\Program Files\Microsoft' -Directory -Filter 'jdk-21*' | Select-Object -First 1 -Expand FullName)"
Set-Location C:\Source\IncicoV3\mobile\android
.\gradlew.bat :app:assembleDebug --console=plain
adb install -r -t app\build\outputs\apk\debug\app-debug.apk
adb shell am start -n nl.sefaja.incico/.MainActivity
Release:
.\gradlew.bat :app:testDebugUnitTest :app:bundleRelease --console=plain
# -> app\build\outputs\bundle\release\app-release.aab, upload in Play Console
- Signing: upload key in
%USERPROFILE%\.android-signing\incico-upload.jks(aliasincico-upload), referenced from a gitignoredmobile/android/key.properties; Play App Signing holds the app signing key. The first upload key was lost; a new one was created on 2026-10-30 and its reset requested in Play Console (old SHA-1F6:7A:40:3B:…:22:2B:B5), so builds signed with it are accepted once Google approves the reset. - Website: Netlify site
incico(incico.netlify.app), withwww.incico.euandincico.euon Hostinger DNS (ALIAS @ → apex-loadbalancer.netlify.com,CNAME www → incico.netlify.app) since 2026-10-30. The mail,api,accountandservicerecords stay as they were. Deploys are manual for now:npm run buildinweb/withPUBLIC_SUPABASE_URLandPUBLIC_SUPABASE_ANON_KEYfrom.env.production, thennetlify deploy --prod --dir web/dist --site incico. A new keystore is created for v3 because v1’s keystore was committed to source and must be treated as leaked. - R8: minify and shrink; obfuscation on with the mapping embedded in the AAB (Play reads it) and uploaded to PostHog for symbolication. Keep rules for Room entities and kotlinx.serialization models.
- PostHog mapping upload: every release build (
bundleRelease,assembleRelease) ends withuploadPosthogMapping, which runs@posthog/cli0.18.10 throughnpx:exp proguard uploadwith the build’s mapping under the map idnl.sefaja.incico@<version>+<build>, the release name, version and build, and--force, so rebuilding a version replaces its mapping with the one that goes to Play. The release app writes the same id intoBuildConfig.PROGUARD_MAP_ID, and every crash frame carries it asmap_id. The task needs a PostHog personal API key with the error tracking write and organization read scopes and the project id, asPOSTHOG_CLI_API_KEYandPOSTHOG_CLI_PROJECT_IDin the gitignored.envor the environment (POSTHOG_CLI_HOSTdefaults tohttps://eu.posthog.com). Without them the task is skipped and the build is unchanged; with a wrong key the release build fails, so a shipped build never lacks its mapping unnoticed. - Debug builds use a known debug keystore copied from the main checkout so the Google OAuth client’s SHA-1 matches (Saymail’s lesson).
run-e2e.ps1boots an emulator, seeds the local Supabase, installs both APKs, runs the instrumented suite and records a video, as Saymail’s does.
iOS (built and tested on the Mac mini, driven from the PC)
Phase 2 produces simulator debug/test builds only. mobile/ios/release/ today holds the two scripts that build configuration needs: gen-appconfig.sh (writes AppConfig.generated.swift from .env) and sync-version.sh (writes Version.xcconfig from mobile/version.json). The Saymail-style release pipeline (ios-build.sh, ios-release.sh, asc-compliance.mjs, publish-testflight-release.ps1) is phase 7 and does not exist yet.
Build and test both run through mobile/ios/mac-run.ps1, which syncs the tree to the Mac mini and drives xcodebuild there:
pwsh mobile\ios\mac-run.ps1 -Task build -Destination 'platform=iOS Simulator,name=iPhone 16'
pwsh mobile\ios\mac-run.ps1 -Task test
-OS defaults to 18.6 because the Mac mini’s iOS 26.5 simulator runtime has no iPhone 16 image; pass a different -OS once one is installed. -RemoteDir gives a run its own remote directory so more than one build/test can use the Mac mini at the same time.
Requirements on the Mac mini: Xcode 16+ (currently 26.6 with the iOS 26.5 SDK), key-based SSH, Apple team 47HUKYRBQZ, bundle id nl.sefaja.incico. Release signing (~/.config/incico-publish/macos-signing.env, the App Store Connect API key, key.properties-equivalent secrets) is set up in phase 7 alongside the release scripts above.
There is no Secrets.plist step: Incico has no client secret to inject, which removes the failure mode Saymail guards against with secrets-plist.sh verify.
Store listing
- Package / bundle id:
nl.sefaja.incico(Q9, decided 2026-09-12);com.sefaja.incicostays with the v1/v2 store records. - Both stores: screenshots generated by UI tests (phone 6.7”, tablet 12.9”), the same feature texts from the website’s i18n files, privacy policy URL
https://www.incico.eu/en/legal/privacy/, account deletion URLhttps://www.incico.eu/en/account/delete/. - Apple: Sign in with Apple enabled; App Privacy labels per Telemetry; age rating 4+.
- Google: Data Safety form;
READ_CONTACTS/WRITE_CONTACTSjustified by the device-contacts sync feature with an in-app prominent disclosure before the permission dialog.
Release checklist
developgreen: unit tests both platforms, pgTAP, Deno tests, i18n parity, vectors check.- Manual test lines in
test.mdchecked off. - Merge to
main; bumpmobile/version.json; commitchore(release): v3.x.y; tag. supabase/deploy.ps1(migrations first, always backward-compatible with the previous app version). The target project must haveRESEND_API_KEYin its.env.<target>: without it every function that mails answersmail_not_configured, so no verification code, deletion notice or export link is ever sent. The Mailpit path the end-to-end suite uses is refused unless the runtime’s ownSUPABASE_URLis a local one, so it never stands in for the key on a hosted project.- Android bundle → Play internal testing → production staged rollout 10 % → 100 %.
- iOS
ios-release.sh→ TestFlight → App Store review. - Website auto-deploys from
main. - Back-merge
mainintodevelop. - Update
mobile_releaseswithmin_buildonly when a migration breaks older clients.
CI (new versus Saymail)
Saymail has no mobile CI. Incico adds GitHub Actions on push to develop and main: Android unit tests and lint on ubuntu-latest; iOS unit tests on macos-latest; Supabase db reset + pgTAP + Deno tests with the Supabase CLI; website build and vitest; gitleaks. Release builds stay on the two physical machines because they hold the signing material.