diff --git a/.github/homebrew/meshtastic-desktop.rb b/.github/homebrew/meshtastic-desktop.rb new file mode 100644 index 000000000..59a805a25 --- /dev/null +++ b/.github/homebrew/meshtastic-desktop.rb @@ -0,0 +1,34 @@ +# Homebrew Cask template for Meshtastic Desktop. +# +# Source of truth for the cask published to the meshtastic/homebrew-tap tap +# (alongside the meshtasticd formula). The `update-homebrew-cask` job in +# promote.yml substitutes {{VERSION}} and {{SHA256}} and opens a PR against the +# tap on every production release, so its brew test-bot CI validates the bump. +# The download URL depends on the jpackage DMG being named +# "Meshtastic Desktop-.dmg" (GitHub rewrites the space to a dot). +# +# NOTE: arm64-only — CI has no Intel macOS runner, so no x86_64 DMG exists. +# When one is added, switch to `arch arm: ..., intel: ...` with per-arch sha256. +cask "meshtastic-desktop" do + version "{{VERSION}}" + sha256 "{{SHA256}}" + + url "https://github.com/meshtastic/Meshtastic-Android/releases/download/v#{version}/Meshtastic.Desktop-#{version}.dmg", + verified: "github.com/meshtastic/Meshtastic-Android/" + name "Meshtastic Desktop" + desc "Companion app for Meshtastic mesh-networking radios" + homepage "https://meshtastic.org/" + + livecheck do + url :url + strategy :github_latest + end + + auto_updates false + depends_on arch: :arm64 + depends_on macos: :monterey + + app "Meshtastic Desktop.app" + + zap trash: "~/.meshtastic" +end diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml index 8ba75e1f4..02d1df3cd 100644 --- a/.github/workflows/promote.yml +++ b/.github/workflows/promote.yml @@ -52,6 +52,8 @@ on: required: true DISCORD_WEBHOOK_ANDROID: required: false + HOMEBREW_TAP_TOKEN: + required: false concurrency: group: ${{ github.workflow }}-${{ inputs.tag_name }} @@ -327,3 +329,64 @@ jobs: ) curl -H "Content-Type: application/json" -d "$PAYLOAD" "$DISCORD_WEBHOOK" + + # Runs in-workflow rather than on `release: published` because the undraft above + # uses github.token, whose events never trigger other workflows. Once the cask is + # promoted to homebrew/cask, replace the tap PR with `brew bump-cask-pr`. + update-homebrew-cask: + if: ${{ inputs.channel == 'production' }} + runs-on: ubuntu-24.04-arm + needs: [ update-github-release ] + steps: + - name: Checkout code + uses: actions/checkout@v7.0.0 + with: + ref: ${{ inputs.commit_sha || inputs.tag_name }} + + - name: Render cask and open PR against meshtastic/homebrew-tap + env: + TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + TAG: ${{ inputs.final_tag }} + run: | + if [[ -z "$TAP_TOKEN" ]]; then + echo "HOMEBREW_TAP_TOKEN not set; skipping Homebrew cask update." + exit 0 + fi + # Tags cut before the template landed check out without it — skip, don't fail. + if [[ ! -f .github/homebrew/meshtastic-desktop.rb ]]; then + echo "Cask template not present at this tag; skipping Homebrew cask update." + exit 0 + fi + + VERSION=${TAG#v} + # GitHub rewrites spaces in asset names to dots: + # "Meshtastic Desktop-X.dmg" is served as "Meshtastic.Desktop-X.dmg". + # --retry-all-errors covers the brief 404 window while the undraft propagates. + curl -fsSL --retry 5 --retry-delay 30 --retry-all-errors -o /tmp/app.dmg \ + "https://github.com/${{ github.repository }}/releases/download/${TAG}/Meshtastic.Desktop-${VERSION}.dmg" + SHA256=$(sha256sum /tmp/app.dmg | cut -d' ' -f1) + + sed -e "s/{{VERSION}}/${VERSION}/" -e "s/{{SHA256}}/${SHA256}/" \ + .github/homebrew/meshtastic-desktop.rb > /tmp/meshtastic-desktop.rb + + # PR (not a direct push to main) so the tap's brew test-bot CI validates the bump. + git clone "https://x-access-token:${TAP_TOKEN}@github.com/meshtastic/homebrew-tap.git" /tmp/tap + cd /tmp/tap + BRANCH="meshtastic-desktop-${VERSION}" + git checkout -b "$BRANCH" + mkdir -p Casks + cp /tmp/meshtastic-desktop.rb Casks/meshtastic-desktop.rb + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Casks/meshtastic-desktop.rb + if git diff --cached --quiet; then + echo "Cask already up to date." + exit 0 + fi + git commit -m "meshtastic-desktop ${VERSION}" + git push -fu origin "$BRANCH" + GH_TOKEN="$TAP_TOKEN" gh pr create --repo meshtastic/homebrew-tap \ + --head "$BRANCH" --base main \ + --title "meshtastic-desktop ${VERSION}" \ + --body "Automated cask bump for the ${TAG} production release of [Meshtastic-Android](https://github.com/${{ github.repository }}/releases/tag/${TAG})." \ + || echo "PR already exists for $BRANCH; branch updated." diff --git a/desktopApp/README.md b/desktopApp/README.md index cd6383b0c..ec6313ffa 100644 --- a/desktopApp/README.md +++ b/desktopApp/README.md @@ -23,6 +23,14 @@ A Compose Desktop application target — the first full non-Android target for t ./gradlew :desktopApp:packageReleaseDistributionForCurrentOS ``` +## Install via Homebrew (macOS, Apple Silicon) + +```bash +brew install --cask meshtastic/tap/meshtastic-desktop +``` + +The cask lives in the [meshtastic/homebrew-tap](https://github.com/meshtastic/homebrew-tap) tap (alongside the `meshtasticd` formula) and is regenerated from the template at `.github/homebrew/meshtastic-desktop.rb` by the `update-homebrew-cask` job in `promote.yml`, which opens a PR against the tap for production releases when `HOMEBREW_TAP_TOKEN` is configured. + ## ProGuard / Minification Release builds use ProGuard for tree-shaking (unused code removal), significantly reducing distribution size. Obfuscation is disabled since the project is open-source. Rules are aligned with the Android R8 rules in `androidApp/proguard-rules.pro` — both targets share the same anti-class-merging philosophy.