diff --git a/.github/release.yml b/.github/release.yml index 6ec1c03ba..a66aafea0 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -26,6 +26,9 @@ changelog: labels: - enhancement - feature + - title: 🖥️ Desktop + labels: + - desktop - title: 🛠️ Fixes labels: - bug diff --git a/.github/workflows/pr_enforce_labels.yml b/.github/workflows/pr_enforce_labels.yml index 92ed400d2..bf876b347 100644 --- a/.github/workflows/pr_enforce_labels.yml +++ b/.github/workflows/pr_enforce_labels.yml @@ -29,7 +29,7 @@ jobs: script: | // Extract labels from the payload directly to avoid extra API calls const latestLabels = context.payload.pull_request.labels.map(label => label.name); - const requiredLabels = ['bugfix', 'enhancement', 'automation', 'dependencies', 'repo', 'release', 'refactor', 'chore', 'ci', 'build', 'testing', 'documentation']; + const requiredLabels = ['bugfix', 'enhancement', 'automation', 'dependencies', 'repo', 'release', 'refactor', 'desktop', 'chore', 'ci', 'build', 'testing', 'documentation']; console.log('Labels from payload:', latestLabels); const hasRequiredLabel = latestLabels.some(label => requiredLabels.includes(label)); if (!hasRequiredLabel) { diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df78d3306..8c97ffd26 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,6 +53,14 @@ on: required: false INTERNAL_BUILDS_HOST_PAT: required: false + APPLE_SIGNING_IDENTITY: + required: false + APPLE_ID: + required: false + APPLE_APP_SPECIFIC_PASSWORD: + required: false + APPLE_TEAM_ID: + required: false concurrency: group: ${{ github.workflow }}-${{ inputs.tag_name }} @@ -284,7 +292,13 @@ jobs: - name: Package Native Distributions env: ORG_GRADLE_PROJECT_appVersionName: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }} + VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }} APPIMAGE_EXTRACT_AND_RUN: 1 + SIGN_MACOS: ${{ runner.os == 'macOS' && 'true' || 'false' }} + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} # Quote the -P flag: PowerShell on Windows interprets the dot in # `-PaboutLibraries.release=true` as member access on `-PaboutLibraries`, # splitting the token and feeding `.release=true` to Gradle as a task name. @@ -309,6 +323,18 @@ jobs: retention-days: 1 if-no-files-found: ignore + - name: Attest Desktop artifact provenance + if: success() + uses: actions/attest-build-provenance@v4 + with: + subject-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 + github-release: if: ${{ !cancelled() && !failure() }} runs-on: ubuntu-24.04-arm diff --git a/README.md b/README.md index 2cc1ffe1c..5db9bb130 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,10 @@ width="24%">](https://play.google.com/store/apps/details?id=com.geeksville.mesh& The play store is the last to update of these options, but if you want to join the Play Store testing program go to [this URL](https://play.google.com/apps/testing/com.geeksville.mesh) and opt-in to become a tester. If you encounter any problems or have questions, [ask us on the discord](https://discord.gg/meshtastic), [create an issue](https://github.com/meshtastic/Meshtastic-Android/issues), or [post in the forum](https://github.com/orgs/meshtastic/discussions) and we'll help as we can. +### Desktop + +**Meshtastic Desktop** installers (macOS DMG, Windows MSI/EXE, Linux DEB/RPM/AppImage) are available from [GitHub Releases](https://github.com/meshtastic/Meshtastic-Android/releases). A Flatpak package is maintained at [vidplace7/org.meshtastic.desktop](https://github.com/vidplace7/org.meshtastic.desktop). + ## Documentation The project's documentation is generated with [Dokka](https://kotlinlang.org/docs/dokka-introduction.html) and hosted on GitHub Pages. It is automatically updated on every push to the `main` branch. diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md index 8a1feb203..029b3edf8 100644 --- a/RELEASE_PROCESS.md +++ b/RELEASE_PROCESS.md @@ -1,21 +1,23 @@ -# Meshtastic-Android Release Process +# Meshtastic Release Process -This guide summarizes the steps for releasing a new version of Meshtastic-Android. The process is fully automated via GitHub Actions and Fastlane. +This guide summarizes the steps for releasing new versions of Meshtastic Android and Desktop. The process is fully automated via GitHub Actions and Fastlane. ## Overview The entire release process is managed by a single, manually-triggered GitHub Action: **`Create or Promote Release`**. - **Trigger:** To start a new release or promote an existing one, a developer manually runs the workflow from the GitHub Actions tab. -- **Inputs:** The workflow requires two inputs: +- **Inputs:** The workflow requires the following inputs: 1. `version`: The base version number you are releasing (e.g., `2.4.0`). 2. `channel`: The release channel you are targeting (`internal`, `closed`, `open`, or `production`). + 3. `build_desktop`: Whether to build and attach Desktop native installers (default: `false`). - **Automation:** The workflow handles everything automatically: - **Syncs Assets:** Fetches the latest firmware/hardware lists, protobuf definitions, and translations (Crowdin). - **Generates Changelog:** Creates a clean changelog from commits since the last production release and commits it to the repo. - **Updates Config:** Automatically bumps the `VERSION_NAME_BASE` in `config.properties`. - **Verifies & Tags:** Runs lint checks, builds the app, and *only* tags the release if successful. - - **Deploys:** Uploads the build to the correct Google Play track and attaches artifacts (`.aab`/`.apk`) to a GitHub Release. + - **Deploys Android:** Uploads the build to the correct Google Play track and attaches artifacts (`.aab`/`.apk`) to a GitHub Release. + - **Deploys Desktop** *(when enabled)*: Builds native installers (DMG, MSI, EXE, DEB, RPM, AppImage) on a matrix of runners and attaches them to the GitHub Release. - **Changelog:** Release notes are auto-generated from PR labels. Ensure PRs are labeled correctly to maintain an accurate changelog. ## Release Steps @@ -27,13 +29,15 @@ The entire release process is managed by a single, manually-triggered GitHub Act 3. Click the **"Run workflow"** dropdown. 4. Enter the base `version` (e.g., `2.4.0`). 5. Select the `internal` channel. -6. Click **"Run workflow"**. +6. Check **`build_desktop`** if you want Desktop installers included in this release. +7. Click **"Run workflow"**. The workflow will: 1. **Create a new commit** on the current branch containing updated assets, translations, and the new changelog. 2. **Tag** that commit with an incremental internal tag (e.g., `v2.4.0-internal.1`). -3. **Build & Deploy** the verified artifact to the Play Store Internal track. -4. Publish a **draft** pre-release on GitHub. +3. **Build & Deploy** the verified Android artifact to the Play Store Internal track. +4. **Build Desktop** *(if enabled)* native installers on macOS, Windows, and Linux runners. +5. Publish a **draft** pre-release on GitHub with all artifacts attached. ### 2. Promote to the Next Channel @@ -54,8 +58,43 @@ After testing is complete on all pre-release channels, you can create the final ### 4. Post-Release -1. **Verify:** Check the Google Play Console to ensure the build is available on the correct track. -2. **Merge:** Merge the release branch (if one was used for stabilization) back into `main`. +1. **Verify Android:** Check the Google Play Console to ensure the build is available on the correct track. +2. **Verify Desktop** *(if built)*: Download and smoke-test at least one installer (DMG, MSI, or AppImage) from the GitHub Release. +3. **Merge:** Merge the release branch (if one was used for stabilization) back into `main`. + +## Desktop Release Details + +Desktop native installers are built as part of the main release pipeline when `build_desktop` is enabled. There is no separate promotion flow for Desktop — installers are built once during the `internal` release and attached to the GitHub Release alongside Android artifacts. + +### Artifacts Produced + +| Platform | Format | Runner | +|---|---|---| +| macOS | `.dmg` | `macos-latest` | +| Windows | `.msi`, `.exe` | `windows-latest` | +| Linux (x86_64) | `.deb`, `.rpm`, `.AppImage` | `ubuntu-24.04` | +| Linux (ARM64) | `.deb`, `.rpm`, `.AppImage` | `ubuntu-24.04-arm` | + +### macOS Code Signing & Notarization + +macOS builds are signed and notarized when the following CI secrets are configured: + +| Secret | Source | +|---|---| +| `APPLE_SIGNING_IDENTITY` | Developer ID Application certificate (from Apple Developer account) | +| `APPLE_ID` | Apple ID email used for notarization | +| `APPLE_APP_SPECIFIC_PASSWORD` | App-specific password from [appleid.apple.com](https://appleid.apple.com) | +| `APPLE_TEAM_ID` | 10-character Apple Developer Team ID | + +Without these secrets, macOS builds are produced unsigned. Unsigned DMGs will trigger Gatekeeper warnings on end-user machines. + +### Version Alignment + +Desktop uses the same version resolution chain as Android — both read `VERSION_CODE_OFFSET` and `VERSION_NAME_BASE` from `config.properties`, with CI passing the resolved values as environment variables. Version names are sanitized to strict `X.Y.Z` format for native installer compatibility. + +### Flatpak + +Flatpak packaging is maintained externally at [vidplace7/org.meshtastic.desktop](https://github.com/vidplace7/org.meshtastic.desktop). It builds `:desktop:packageUberJarForCurrentOS` (not the native distribution pipeline) and includes its own AppStream metainfo, `.desktop` entry, and JBR bundling. ## Build Attestations & Provenance diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 2e66d6012..f9c5b7276 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -124,8 +124,8 @@ compose.desktop { mainClass = "org.meshtastic.desktop.MainKt" jvmArgs( "-Xmx2G", - "-Dapple.awt.application.name=Meshtastic", - "-Dcom.apple.mrj.application.apple.menu.about.name=Meshtastic", + "-Dapple.awt.application.name=Meshtastic Desktop", + "-Dcom.apple.mrj.application.apple.menu.about.name=Meshtastic Desktop", "-Dcom.apple.bundle.identifier=org.meshtastic.desktop", ) @@ -140,7 +140,7 @@ compose.desktop { } nativeDistributions { - packageName = "Meshtastic" + packageName = "Meshtastic Desktop" // 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. @@ -157,8 +157,8 @@ compose.desktop { // Increase max heap size to prevent OOM issues on complex maps/data jvmArgs( "-Xmx2G", - "-Dapple.awt.application.name=Meshtastic", - "-Dcom.apple.mrj.application.apple.name=Meshtastic", + "-Dapple.awt.application.name=Meshtastic Desktop", + "-Dcom.apple.mrj.application.apple.menu.about.name=Meshtastic Desktop", "-Dcom.apple.bundle.identifier=org.meshtastic.desktop", ) @@ -167,6 +167,7 @@ compose.desktop { iconFile.set(project.file("src/main/resources/icon.icns")) minimumSystemVersion = "12.0" bundleID = "org.meshtastic.desktop" + appCategory = "public.app-category.utilities" entitlementsFile.set(project.file("entitlements.plist")) infoPlist { extraKeysRawXml = @@ -191,22 +192,41 @@ compose.desktop { """ .trimIndent() } - // TODO: To prepare for real distribution on macOS, you'll need to sign and notarize. - // You can inject these from CI environment variables. - // sign = true - // notarize = true - // appleID = System.getenv("APPLE_ID") - // appStorePassword = System.getenv("APPLE_APP_SPECIFIC_PASSWORD") + // macOS code signing and notarization. + // Required CI secrets: + // APPLE_SIGNING_IDENTITY – e.g. "Developer ID Application: Meshtastic LLC (TEAMID)" + // APPLE_ID – Apple ID email used for notarization + // APPLE_APP_SPECIFIC_PASSWORD – App-specific password from appleid.apple.com + // APPLE_TEAM_ID – 10-character Apple Developer Team ID + val signMacOs = System.getenv("SIGN_MACOS")?.toBoolean() ?: false + if (signMacOs) { + signing { + sign.set(true) + identity.set(System.getenv("APPLE_SIGNING_IDENTITY")) + } + notarization { + appleID.set(System.getenv("APPLE_ID")) + password.set(System.getenv("APPLE_APP_SPECIFIC_PASSWORD")) + teamID.set(System.getenv("APPLE_TEAM_ID")) + } + } } 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" + shortcut = true + menu = true + dirChooser = true + // Stable UUID ensures MSI upgrades replace the previous installation + // rather than installing side-by-side. NEVER change this value. + upgradeUuid = "4974EA87-98AA-470E-B590-0BD5CF9FAE8E" } linux { iconFile.set(project.file("src/main/resources/icon.png")) menuGroup = "Network" + debMaintainer = "developers@meshtastic.org" + appCategory = "Network" + rpmLicenseType = "GPLv3+" } // Define target formats based on the current host OS to avoid configuration errors diff --git a/desktop/entitlements.plist b/desktop/entitlements.plist index f799a66e9..3941c7f0b 100644 --- a/desktop/entitlements.plist +++ b/desktop/entitlements.plist @@ -10,5 +10,9 @@ com.apple.security.device.bluetooth + com.apple.security.network.client + + com.apple.security.device.usb + diff --git a/docs/roadmap.md b/docs/roadmap.md index 8cff42c1f..02e7809e3 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -77,7 +77,7 @@ These items address structural gaps identified in the March 2026 architecture re | Notifications | ✅ Desktop native notifications with system tray icon support | | MenuBar | ✅ Removed — replaced with `onPreviewKeyEvent` keyboard shortcuts (⌘Q, ⌘,, ⌘⇧T, ⌘1-4, ⌘/) | | About | ✅ Shared `commonMain` screen (AboutLibraries KMP `produceLibraries` + per-platform JSON) | -| Packaging | ✅ Done — Native distribution pipeline in CI (DMG, MSI, DEB) | +| Packaging | ✅ Done — Native distribution pipeline in CI (DMG, MSI, DEB). Windows `upgradeUuid` set; macOS signing/notarization wired behind `SIGN_MACOS` env flag; desktop build attestation in release CI. Flatpak packaging maintained externally at [vidplace7/org.meshtastic.desktop](https://github.com/vidplace7/org.meshtastic.desktop) (includes AppStream metainfo, `.desktop` entry, and JBR bundling); see [PR #4807](https://github.com/meshtastic/Meshtastic-Android/pull/4807) for `flatpakGradleGenerator` integration | ## Near-Term Priorities (30 days)