feat(desktop): add Conveyor packaging for cross-platform distribution

Replace the 4-OS matrix desktop build in release.yml with a single
Conveyor runner on ubuntu-24.04 that cross-compiles all platform
packages (macOS, Windows, Linux) from one machine.

- Add Conveyor Gradle plugin (dev.hydraulic.conveyor v2.0)
- Add conveyor.conf with app metadata, icons, JVM modules, entitlements
- Add ci.conveyor.conf for CI overrides (production URL, Apple notarization)
- Add defaults.conf.example template for local signing keys
- Update release.yml: single Conveyor job replaces 4-OS matrix
- Add 4 optional secrets: CONVEYOR_SIGNING_KEY, APPLE_TEAM_ID,
  APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD
- Add Conveyor Maven repo to settings.gradle.kts

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
James Rich 2026-04-14 17:22:45 -05:00
parent 0a37635a40
commit a76d5ee0a1
7 changed files with 152 additions and 25 deletions

View file

@ -32,6 +32,7 @@ plugins {
alias(libs.plugins.meshtastic.koin)
id("meshtastic.kover")
alias(libs.plugins.aboutlibraries)
id("dev.hydraulic.conveyor") version "2.0"
}
// ── Version resolution (mirrors app/build.gradle.kts) ────────────────────────
@ -52,6 +53,11 @@ val resolvedIsDebug: Boolean = project.findProperty("desktop.release")?.toString
val resolvedMinFwVersion: String = configProperties.getProperty("MIN_FW_VERSION") ?: ""
val resolvedAbsMinFwVersion: String = configProperties.getProperty("ABS_MIN_FW_VERSION") ?: ""
// Set project version for the Conveyor Gradle plugin (requires strict semver X.Y.Z)
val sanitizedVersion = Regex("^\\d+\\.\\d+\\.\\d+").find(resolvedVersionName)?.value ?: "1.0.0"
version = sanitizedVersion
group = "org.meshtastic"
// ── Generate DesktopBuildConfig ──────────────────────────────────────────────
// Mirrors AGP's BuildConfig for Android so the desktop runtime has access to the
// same version metadata without hardcoding.
@ -209,9 +215,7 @@ compose.desktop {
else -> targetFormats(TargetFormat.Deb, TargetFormat.Rpm, TargetFormat.AppImage)
}
// Reuse the resolved version from the top of this script (mirrors app/build.gradle.kts).
// Native installers require strict numeric semantic versions (X.Y.Z) without suffixes.
val sanitizedVersion = Regex("^\\d+\\.\\d+\\.\\d+").find(resolvedVersionName)?.value ?: "1.0.0"
// Reuse the project-level version for native installers.
packageVersion = sanitizedVersion
description = "Meshtastic Desktop Application"

15
desktop/ci.conveyor.conf Normal file
View file

@ -0,0 +1,15 @@
// Conveyor CI overrides — used by GitHub Actions release workflow.
// Reads signing key and Apple notarization credentials from environment variables.
include required("conveyor.conf")
app {
// Production update channel
site.base-url = "https://github.com/meshtastic/Meshtastic-Android/releases/latest/download"
// Apple notarization (optional — only used when APPLE_NOTARIZATION_TEAM_ID is set)
mac.notarization {
team-id = ${?env.APPLE_NOTARIZATION_TEAM_ID}
apple-id = ${?env.APPLE_NOTARIZATION_APPLE_ID}
app-specific-password = ${?env.APPLE_NOTARIZATION_PASSWORD}
}
}

73
desktop/conveyor.conf Normal file
View file

@ -0,0 +1,73 @@
// Meshtastic Desktop — Conveyor packaging configuration
// https://conveyor.hydraulic.dev/latest/configs/jvm/
// Import Gradle-generated config (classpath, mainClass, version, vendor, JVM args, JDK)
// Regenerate with: ./gradlew :desktop:writeConveyorConfig
include required("generated.conveyor.conf")
// Library compatibility for native extraction (JNA, SQLite, etc.)
include required("https://raw.githubusercontent.com/hydraulic-software/conveyor/master/configs/jvm/extract-native-libraries.conf")
app {
display-name = Meshtastic
fsname = meshtastic
license = GPL-3.0-or-later
vcs-url = "https://github.com/meshtastic/Meshtastic-Android"
// Conveyor renders all required formats from a single high-res PNG
icons = src/main/resources/icon.png
// Update channel — published to GitHub Releases
site.base-url = "localhost:3000"
// ── JVM ──────────────────────────────────────────────────────────────────────
jvm {
// Modules that jdeps may miss (reflection, JNI)
modules += java.net.http
modules += jdk.accessibility
modules += jdk.crypto.ec
modules += jdk.unsupported
modules += java.sql
modules += java.naming
// Extract native libs from JARs for faster startup and clean uninstalls
extract-native-libraries = true
}
// ── macOS ────────────────────────────────────────────────────────────────────
mac {
info-plist {
NSBluetoothAlwaysUsageDescription = "Meshtastic uses Bluetooth to communicate with your Meshtastic radio device."
NSLocalNetworkUsageDescription = "Meshtastic uses your local network to discover Meshtastic devices connected via WiFi."
NSUserNotificationAlertStyle = alert
LSMinimumSystemVersion = "12.0"
CFBundleURLTypes = [{
CFBundleURLName = "Meshtastic deep link"
CFBundleURLSchemes = [meshtastic]
}]
}
entitlements-plist {
com.apple.security.cs.allow-jit = true
com.apple.security.cs.allow-unsigned-executable-memory = true
com.apple.security.cs.disable-library-validation = true
com.apple.security.device.bluetooth = true
}
}
// ── Windows ──────────────────────────────────────────────────────────────────
windows {
// Stable upgrade UUID for in-place MSI upgrades
upgrade-uuid = "b8f3d4a1-7e52-4c89-9a1b-3f6e8d2c5a70"
}
// ── Linux ────────────────────────────────────────────────────────────────────
linux {
desktop-file {
"Desktop Entry" {
Categories = "Network;"
}
}
}
}
conveyor.compatibility-level = 22

View file

@ -0,0 +1,11 @@
// Local Conveyor defaults — copy to defaults.conf and fill in your values.
// This file is checked in as a template; defaults.conf is gitignored.
conveyor.billing-email = "you@example.com"
// Uncomment for real code signing:
// app.signing-key = "<your-root-key>"
// app.mac.notarization {
// team-id = "YOUR_TEAM_ID"
// apple-id = "you@example.com"
// app-specific-password = ${env.APPLE_ASP}
// }