diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 76541d885..de1705d78 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -259,13 +259,9 @@ jobs: subject-path: app/build/outputs/apk/fdroid/release/*.apk release-desktop: - runs-on: ${{ matrix.os }} + runs-on: ubuntu-22.04 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 }} @@ -295,32 +291,21 @@ jobs: - name: Export Full Library Licenses run: ./gradlew exportLibraryDefinitions -Pci=true - - name: Install dependencies for AppImage - if: runner.os == 'Linux' - run: sudo apt-get update && sudo apt-get install -y libfuse2 + - name: Setup Conveyor + uses: hydraulic-software/setup-conveyor@v1.2 - - name: Package Native Distributions + - name: Build all Desktop Artifacts env: ORG_GRADLE_PROJECT_appVersionName: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }} - 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 + run: conveyor make site - name: Upload Desktop Artifacts if: always() uses: actions/upload-artifact@v7 with: - name: desktop-${{ runner.os }}-${{ runner.arch }} + name: desktop-all-platforms path: | - 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 + output/* retention-days: 1 if-no-files-found: ignore diff --git a/conveyor.conf b/conveyor.conf new file mode 100644 index 000000000..ea836f23f --- /dev/null +++ b/conveyor.conf @@ -0,0 +1,12 @@ +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 8d5f6a661..cc4e5cfac 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,71 +50,20 @@ 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) @@ -146,6 +95,12 @@ 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 f9de653b4..dedc92470 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,6 +135,13 @@ 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" } @@ -260,6 +267,7 @@ 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" }