mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 10:29:58 +02:00
Added a comprehensive collection of Compose development guidelines, best practices, and skill definitions in the .codex directory. Co-authored-by: Junie <junie@jetbrains.com>
7.4 KiB
7.4 KiB
CI/CD & Distribution
CI/CD and native distribution for Compose Multiplatform: Android, Desktop (JVM), and iOS.
1. Distribution Overview
| Platform | Output | Gradle Task | Notes |
|---|---|---|---|
| Android | APK/AAB | assembleRelease/bundleRelease |
Standard distribution |
| Desktop macOS | DMG | packageDmg |
Needs signing for Gatekeeper |
| Desktop Windows | MSI | packageMsi |
Optional signing |
| Desktop Linux | DEB | packageDeb |
Package manager format |
| iOS | .app/.ipa | Xcode Archive | Gradle builds framework only |
2. GitHub Actions — Android
name: Android Build
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v4
- run: ./gradlew :androidApp:assembleRelease
- uses: actions/upload-artifact@v4
with:
name: android-apk
path: androidApp/build/outputs/apk/release/*.apk
With Signing
- name: Decode Keystore
run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > release.keystore
- run: ./gradlew :androidApp:assembleRelease
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
3. GitHub Actions — Desktop Multi-Platform
name: Desktop Build
on:
workflow_dispatch:
inputs:
build_macos: { type: boolean, default: true }
build_windows: { type: boolean, default: true }
build_linux: { type: boolean, default: true }
jobs:
build-macos:
if: ${{ inputs.build_macos }}
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { java-version: '21', distribution: 'temurin' }
- uses: gradle/actions/setup-gradle@v4
- run: ./gradlew :desktopApp:packageDmg
- uses: actions/upload-artifact@v4
with:
name: macos-dmg
path: desktopApp/build/compose/binaries/main/dmg/*.dmg
build-windows:
if: ${{ inputs.build_windows }}
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { java-version: '21', distribution: 'temurin' }
- uses: gradle/actions/setup-gradle@v4
- run: ./gradlew :desktopApp:packageMsi
- uses: actions/upload-artifact@v4
with:
name: windows-msi
path: desktopApp/build/compose/binaries/main/msi/*.msi
build-linux:
if: ${{ inputs.build_linux }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { java-version: '21', distribution: 'temurin' }
- uses: gradle/actions/setup-gradle@v4
- run: ./gradlew :desktopApp:packageDeb
- uses: actions/upload-artifact@v4
with:
name: linux-deb
path: desktopApp/build/compose/binaries/main/deb/*.deb
4. Desktop App Module
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.compose.multiplatform)
alias(libs.plugins.compose.compiler)
}
kotlin {
jvm()
sourceSets {
jvmMain.dependencies {
implementation(compose.desktop.currentOs)
implementation(projects.composeApp)
}
}
}
compose.desktop {
application {
mainClass = "com.example.MainKt"
// Required for DataStore/serialization
jvmArgs += listOf(
"--add-opens", "java.base/java.lang=ALL-UNNAMED",
"--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED"
)
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "MyApp"
packageVersion = "1.0.0"
modules("jdk.unsupported")
macOS {
bundleID = "com.example.myapp"
iconFile.set(project.file("icons/icon.icns"))
// signing { sign.set(true); identity.set("Developer ID Application: ...") }
}
windows {
iconFile.set(project.file("icons/icon.ico"))
upgradeUuid = "YOUR-UUID" // Keep constant across versions
}
linux {
iconFile.set(project.file("icons/icon.png"))
}
}
}
}
5. iOS Xcode Integration
iOS uses Xcode, not Gradle. Gradle builds the shared framework; Xcode embeds it.
Framework in composeApp
kotlin {
listOf(iosArm64(), iosSimulatorArm64()).forEach {
it.binaries.framework {
baseName = "ComposeApp"
isStatic = true // Required for App Store
}
}
}
Xcode Build Phase Script
Add "Run Script" before "Compile Sources":
cd "$SRCROOT/.."
./gradlew :composeApp:embedAndSignAppleFrameworkForXcode
Swift Entry Point
import SwiftUI
import ComposeApp
@main
struct iOSApp: App {
init() { AppKt.doInitKoin() }
var body: some Scene {
WindowGroup {
ComposeViewControllerRepresentable().ignoresSafeArea()
}
}
}
struct ComposeViewControllerRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
MainViewControllerKt.MainViewController()
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
6. Signing
Android
android {
signingConfigs {
create("release") {
storeFile = file("release.keystore")
storePassword = System.getenv("KEYSTORE_PASSWORD")
keyAlias = System.getenv("KEY_ALIAS")
keyPassword = System.getenv("KEY_PASSWORD")
}
}
buildTypes {
release { signingConfig = signingConfigs.getByName("release") }
}
}
macOS (Direct Distribution)
macOS {
signing {
sign.set(true)
identity.set("Developer ID Application: Your Name (TEAM_ID)")
}
notarization {
appleID.set("your-email@example.com")
password.set("@keychain:AC_PASSWORD")
teamID.set("YOUR_TEAM_ID")
}
}
iOS
Handled by Xcode via CODE_SIGN_STYLE = Automatic and DEVELOPMENT_TEAM.
7. Adding Desktop to Existing CMP Project
-
Add
jvm()target incomposeApp:kotlin { jvm() sourceSets { jvmMain.dependencies { /* desktop deps */ } } } -
Add KSP for JVM:
add("kspJvm", libs.room.compiler) -
Create
desktopAppmodule withcompose.desktop {}config -
Add
include(":desktopApp")tosettings.gradle.kts
8. Gradle Tasks
| Platform | Build | Package | Run |
|---|---|---|---|
| Android | assembleRelease |
bundleRelease |
— |
| Desktop | jvmJar |
packageDmg/packageMsi/packageDeb |
run |
| iOS | compileKotlinIosArm64 |
Xcode Archive | Xcode |
9. Troubleshooting
| Issue | Solution |
|---|---|
InaccessibleObjectException |
Add --add-opens JVM args |
| "App is damaged" on macOS | Enable code signing |
| Framework not found in Xcode | Check FRAMEWORK_SEARCH_PATHS |
| Windows MSI won't upgrade | Keep upgradeUuid constant |