Naar de inhoud
Documentatie doorbladeren

De documentatie is alleen in het Engels geschreven. De rest van de site is ook in het Nederlands beschikbaar.

Besloten

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.

Bijgewerkt: 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:

ValueMeaning
INCICO_SUPABASE_URLSupabase project URL; blank builds and runs, but the sign-in screen shows “This build is not configured” rather than crashing
INCICO_SUPABASE_ANON_KEYthe staging publishable key (sb_publishable_…), not the legacy JWT anon key
INCICO_ENVstaging | production, default staging
INCICO_GOOGLE_WEB_CLIENT_IDGoogle OAuth web client id, used by both platforms’ native Google sign-in
INCICO_SOCIAL_PROVIDERScomma 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 (alias incico-upload), referenced from a gitignored mobile/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-1 F6: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), with www.incico.eu and incico.eu on Hostinger DNS (ALIAS @ → apex-loadbalancer.netlify.com, CNAME www → incico.netlify.app) since 2026-10-30. The mail, api, account and service records stay as they were. Deploys are manual for now: npm run build in web/ with PUBLIC_SUPABASE_URL and PUBLIC_SUPABASE_ANON_KEY from .env.production, then netlify 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 with uploadPosthogMapping, which runs @posthog/cli 0.18.10 through npx: exp proguard upload with the build’s mapping under the map id nl.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 into BuildConfig.PROGUARD_MAP_ID, and every crash frame carries it as map_id. The task needs a PostHog personal API key with the error tracking write and organization read scopes and the project id, as POSTHOG_CLI_API_KEY and POSTHOG_CLI_PROJECT_ID in the gitignored .env or the environment (POSTHOG_CLI_HOST defaults to https://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.ps1 boots 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.incico stays 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 URL https://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_CONTACTS justified by the device-contacts sync feature with an in-app prominent disclosure before the permission dialog.

Release checklist

  1. develop green: unit tests both platforms, pgTAP, Deno tests, i18n parity, vectors check.
  2. Manual test lines in test.md checked off.
  3. Merge to main; bump mobile/version.json; commit chore(release): v3.x.y; tag.
  4. supabase/deploy.ps1 (migrations first, always backward-compatible with the previous app version). The target project must have RESEND_API_KEY in its .env.<target>: without it every function that mails answers mail_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 own SUPABASE_URL is a local one, so it never stands in for the key on a hosted project.
  5. Android bundle → Play internal testing → production staged rollout 10 % → 100 %.
  6. iOS ios-release.sh → TestFlight → App Store review.
  7. Website auto-deploys from main.
  8. Back-merge main into develop.
  9. Update mobile_releases with min_build only 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.