Skip to Content
ArchitectureBuild flavors

Build flavors

Oter has two independent flavor systems, one per build pipeline. They share the same names (dev, qa, prod) but are wired up differently.

Android (androidApp/) — real product flavors

Defined in androidApp/build.gradle.kts under flavorDimensions += "environment". Each flavor injects its own BASE_URL, sockets host/port, app name, and applicationId suffix via buildConfigField / resValue.

FlavorDefault?App ID suffixApp nameBASE_URL
devyes.devOter DEVhttp://192.168.5.100:8080/api/v1/
qano.qaOter QAhttps://api-qa.estebanruano.com/api/v1/
prodnoOterhttps://api.estebanruano.com/api/v1/

The dev BASE_URL points at a LAN IP so a physical Android device on the same network can reach a local server. Change it in build.gradle.kts if your host IP differs.

Commands

Gradle task pattern is <task><Flavor><BuildType>:

# Install debug build of a flavor on a connected device/emulator ./gradlew :androidApp:installDevDebug ./gradlew :androidApp:installQaDebug ./gradlew :androidApp:installProdDebug # Release builds (signed when keystore.properties is configured) ./gradlew :androidApp:assembleProdRelease ./gradlew :androidApp:bundleProdRelease # AAB for Play Store

dev is isDefault = true, so assembleDebug resolves to assembleDevDebug.

Compose Multiplatform (composeApp/ — desktop, iOS, wasmJS) — Gradle-property switch

KMP targets don’t support Android-style flavors, so composeApp simulates them with two Gradle properties read at configure time and baked into a generated BuildConfig.kt:

Gradle propertyDefaultBecomes
oter.flavordevBuildConfig.FLAVOR ("dev", "qa", "prod")
oter.releasefalseBuildConfig.IS_RELEASE (Boolean)

Generation lives in composeApp/build.gradle.kts (registerBuildConfigTask) and produces BuildConfig.kt for desktopMain, wasmJsMain, and iosMain. Each platform’s *Constants.kt (e.g. DesktopConstants.kt) then maps FLAVOR to per-env BASE_URL / sockets host / sockets port:

FlavorBASE_URLSockets host:port
devhttp://localhost:8080/api/v1localhost:8080
qahttps://api-qa.estebanruano.com/api/v1api-qa.estebanruano.com:443
prodhttps://api.estebanruano.com/api/v1api.estebanruano.com:443

Note: desktop/iOS/web dev points at localhost — different from Android dev’s LAN IP. They are independent constants.

effectiveFlavor is always FLAVOR (not gated by IS_RELEASE), so you can debug-build any environment locally — isRelease = false only changes the app-name suffix ((DEBUG)) and enables the debug HTTP proxy toggle.

Commands

Desktop:

# Dev (default) ./gradlew :composeApp:run # QA / prod ./gradlew :composeApp:run -Poter.flavor=qa ./gradlew :composeApp:run -Poter.flavor=prod -Poter.release=true # Package native distribution ./gradlew :composeApp:packageReleaseDistributionForCurrentOS -Poter.flavor=prod -Poter.release=true

iOS framework:

./gradlew :composeApp:iosSimulatorArm64Framework # dev ./gradlew :composeApp:iosSimulatorArm64Framework -Poter.flavor=prod -Poter.release=true

Web (wasmJS):

./gradlew :composeApp:wasmJsBrowserDevelopmentRun # dev ./gradlew :composeApp:wasmJsBrowserDistribution -Poter.flavor=prod -Poter.release=true

Switching environments without rebuilding everything

Changing oter.flavor invalidates the generated BuildConfig.kt, which triggers recompilation of the affected target’s *Main source set. There is no runtime override — choose the flavor at build time.

Server (server/)

The Ktor server has no Gradle flavors. Dev vs prod is selected by environment variables at runtime:

  • ENVIRONMENT (development | production, default production) — read by logback.xml for log format/level.
  • LOG_LEVELDEBUG in dev, typically INFO/WARN in prod.
  • JAVA_TOOL_OPTIONS=-Dio.ktor.development=true — enables Ktor dev mode.

See docker-compose.dev.yml for the canonical dev wiring and docs/infra/gcp-server-cloud-run.md for prod deploy.

Quick reference

I want to…Command
Run Android dev build./gradlew :androidApp:installDevDebug
Build prod APK/AAB./gradlew :androidApp:assembleProdRelease / :bundleProdRelease
Run desktop against local server./gradlew :composeApp:run
Run desktop against prod API./gradlew :composeApp:run -Poter.flavor=prod -Poter.release=true
Run web (wasmJS) dev./gradlew :composeApp:wasmJsBrowserDevelopmentRun
Run Ktor server (dev)ENVIRONMENT=development LOG_LEVEL=DEBUG ./gradlew :server:run
Build server prod JAR./gradlew :server:shadowJarjava -jar server/build/libs/server-all.jar

See also: root README — Building and running, INCLUDE_ANDROID Gradle flag.