diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index de1705d78..76541d885 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -259,9 +259,13 @@ jobs: subject-path: app/build/outputs/apk/fdroid/release/*.apk release-desktop: - runs-on: ubuntu-22.04 + runs-on: ${{ matrix.os }} needs: [prepare-build-info] environment: Release + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-22.04, ubuntu-22.04-arm] env: GRADLE_CACHE_URL: ${{ secrets.GRADLE_CACHE_URL }} GRADLE_CACHE_USERNAME: ${{ secrets.GRADLE_CACHE_USERNAME }} @@ -291,21 +295,32 @@ jobs: - name: Export Full Library Licenses run: ./gradlew exportLibraryDefinitions -Pci=true - - name: Setup Conveyor - uses: hydraulic-software/setup-conveyor@v1.2 + - name: Install dependencies for AppImage + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y libfuse2 - - name: Build all Desktop Artifacts + - name: Package Native Distributions env: ORG_GRADLE_PROJECT_appVersionName: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }} - run: conveyor make site + APPIMAGE_EXTRACT_AND_RUN: 1 + run: ./gradlew :desktop:packageReleaseDistributionForCurrentOS --no-daemon + + - name: List Desktop Binaries + if: runner.os == 'Linux' + run: ls -R desktop/build/compose/binaries/main-release - name: Upload Desktop Artifacts if: always() uses: actions/upload-artifact@v7 with: - name: desktop-all-platforms + name: desktop-${{ runner.os }}-${{ runner.arch }} path: | - output/* + desktop/build/compose/binaries/main-release/*/*.dmg + desktop/build/compose/binaries/main-release/*/*.msi + desktop/build/compose/binaries/main-release/*/*.exe + desktop/build/compose/binaries/main-release/*/*.deb + desktop/build/compose/binaries/main-release/*/*.rpm + desktop/build/compose/binaries/main-release/*/*.AppImage retention-days: 1 if-no-files-found: ignore diff --git a/conveyor.conf b/conveyor.conf deleted file mode 100644 index ea836f23f..000000000 --- a/conveyor.conf +++ /dev/null @@ -1,12 +0,0 @@ -include "#!./gradlew -q :desktop:printConveyorConfig" - -app { - display-name = "Meshtastic" - rdns-name = "org.meshtastic.desktop" - vcs-url = "https://github.com/meshtastic/Meshtastic-Android" - license = "GPL-3.0" - - icons = "desktop/src/main/resources/icon.png" - - site.base-url = "https://github.com/meshtastic/Meshtastic-Android/releases/latest/download" -} \ No newline at end of file diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index cc4e5cfac..8d5f6a661 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -18,13 +18,13 @@ import com.mikepenz.aboutlibraries.plugin.DuplicateMode import com.mikepenz.aboutlibraries.plugin.DuplicateRule import io.gitlab.arturbosch.detekt.Detekt +import org.jetbrains.compose.desktop.application.dsl.TargetFormat import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { alias(libs.plugins.kotlin.jvm) alias(libs.plugins.compose.compiler) alias(libs.plugins.compose.multiplatform) - alias(libs.plugins.conveyor) alias(libs.plugins.meshtastic.detekt) alias(libs.plugins.meshtastic.spotless) alias(libs.plugins.meshtastic.koin) @@ -50,20 +50,71 @@ compose.desktop { isEnabled.set(false) configurationFiles.from(project.file("proguard-rules.pro")) } + + nativeDistributions { + packageName = "Meshtastic" + + // Ensure critical JVM modules are included in the custom JRE bundled with the app. + // jdeps might miss some of these if they are loaded via reflection or JNI. + modules( + "java.net.http", // Ktor Java client + "jdk.crypto.ec", // Required for SSL/TLS HTTPS requests + "jdk.unsupported", // sun.misc.Unsafe used by Coroutines & Okio + "java.sql", // Sometimes required by SQLite JNI + "java.naming", // Required by Ktor for DNS resolution + ) + + // Default JVM arguments for the packaged application + // Increase max heap size to prevent OOM issues on complex maps/data + jvmArgs("-Xmx2G") + + // App Icon & OS Specific Configurations + macOS { + iconFile.set(project.file("src/main/resources/icon.icns")) + // TODO: To prepare for real distribution on macOS, you'll need to sign and notarize. + // You can inject these from CI environment variables. + // bundleID = "org.meshtastic.desktop" + // sign = true + // notarize = true + // appleID = System.getenv("APPLE_ID") + // appStorePassword = System.getenv("APPLE_APP_SPECIFIC_PASSWORD") + } + windows { + iconFile.set(project.file("src/main/resources/icon.ico")) + menuGroup = "Meshtastic" + // TODO: Must generate and set a consistent UUID for Windows upgrades. + // upgradeUuid = "YOUR-UPGRADE-UUID-HERE" + } + linux { + iconFile.set(project.file("src/main/resources/icon.png")) + menuGroup = "Network" + } + + // Define target formats based on the current host OS to avoid configuration errors + // (e.g., trying to configure Linux AppImage notarization on macOS). + val currentOs = System.getProperty("os.name").lowercase() + when { + currentOs.contains("mac") -> targetFormats(TargetFormat.Dmg) + currentOs.contains("win") -> targetFormats(TargetFormat.Msi, TargetFormat.Exe) + else -> targetFormats(TargetFormat.Deb, TargetFormat.Rpm, TargetFormat.AppImage) + } + + // Read version from project properties (passed by CI) or default to 1.0.0 + // Native installers require strict numeric semantic versions (X.Y.Z) without suffixes + val rawVersion = + project.findProperty("android.injected.version.name")?.toString() + ?: project.findProperty("appVersionName")?.toString() + ?: System.getenv("VERSION_NAME") + ?: "1.0.0" + val sanitizedVersion = Regex("^\\d+\\.\\d+\\.\\d+").find(rawVersion)?.value ?: "1.0.0" + packageVersion = sanitizedVersion + + description = "Meshtastic Desktop Application" + vendor = "Meshtastic LLC" + } } } -// Read version from project properties (passed by CI) or default to 1.0.0 -// Native installers require strict numeric semantic versions (X.Y.Z) without suffixes -val rawVersion = - project.findProperty("android.injected.version.name")?.toString() - ?: project.findProperty("appVersionName")?.toString() - ?: System.getenv("VERSION_NAME") - ?: "1.0.0" -val sanitizedVersion = Regex("^\\d+\\.\\d+\\.\\d+").find(rawVersion)?.value ?: "1.0.0" - -project.version = sanitizedVersion - dependencies { implementation(libs.aboutlibraries.core) implementation(libs.aboutlibraries.compose.m3) @@ -95,12 +146,6 @@ dependencies { // Compose Desktop implementation(compose.desktop.currentOs) - linuxAmd64(libs.compose.multiplatform.desktop.linux.x64) - linuxAarch64(libs.compose.multiplatform.desktop.linux.arm64) - macAmd64(libs.compose.multiplatform.desktop.macos.x64) - macAarch64(libs.compose.multiplatform.desktop.macos.arm64) - windowsAmd64(libs.compose.multiplatform.desktop.windows.x64) - implementation(libs.compose.multiplatform.material3) implementation(libs.compose.multiplatform.materialIconsExtended) implementation(libs.compose.multiplatform.runtime) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dedc92470..f9de653b4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -62,7 +62,7 @@ vico = "3.0.3" dependency-guard = "0.5.0" nordic-ble = "2.0.0-alpha16" nordic-common = "2.9.2" -conveyor = "2.0" + [libraries] # AndroidX @@ -135,13 +135,6 @@ compose-multiplatform-resources = { module = "org.jetbrains.compose.components:c compose-multiplatform-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "compose-multiplatform" } compose-multiplatform-materialIconsExtended = { module = "org.jetbrains.compose.material:material-icons-extended", version = "1.7.3" } -# Compose Desktop Native Distributions -compose-multiplatform-desktop-linux-x64 = { module = "org.jetbrains.compose.desktop:desktop-jvm-linux-x64", version.ref = "compose-multiplatform" } -compose-multiplatform-desktop-linux-arm64 = { module = "org.jetbrains.compose.desktop:desktop-jvm-linux-arm64", version.ref = "compose-multiplatform" } -compose-multiplatform-desktop-macos-x64 = { module = "org.jetbrains.compose.desktop:desktop-jvm-macos-x64", version.ref = "compose-multiplatform" } -compose-multiplatform-desktop-macos-arm64 = { module = "org.jetbrains.compose.desktop:desktop-jvm-macos-arm64", version.ref = "compose-multiplatform" } -compose-multiplatform-desktop-windows-x64 = { module = "org.jetbrains.compose.desktop:desktop-jvm-windows-x64", version.ref = "compose-multiplatform" } - # JetBrains Material 3 Adaptive (multiplatform — Android, Desktop, iOS) jetbrains-compose-material3-adaptive = { module = "org.jetbrains.compose.material3.adaptive:adaptive", version.ref = "jetbrains-adaptive" } jetbrains-compose-material3-adaptive-layout = { module = "org.jetbrains.compose.material3.adaptive:adaptive-layout", version.ref = "jetbrains-adaptive" } @@ -267,7 +260,6 @@ spotless-gradlePlugin = { module = "com.diffplug.spotless:spotless-plugin-gradle test-retry-gradlePlugin = { module = "org.gradle:test-retry-gradle-plugin", version.ref = "testRetry" } [plugins] -conveyor = { id = "dev.hydraulic.conveyor", version.ref = "conveyor" } # Android android-application = { id = "com.android.application", version.ref = "agp" } android-kotlin-multiplatform-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }