diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 3a10ca3e3c..19ca1146e8 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -155,8 +155,44 @@ jobs: AMETHYST_NOTARY_TEAM_ID: ${{ secrets.MAC_NOTARY_TEAM_ID }} with: max_attempts: 2 - timeout_minutes: 15 - command: ./gradlew --no-daemon :desktopApp:${{ matrix.tasks }} + # macOS needs far longer: notarizeReleaseDmg blocks on `notarytool + # submit --wait`, which is minutes-to-tens-of-minutes on Apple's side. + timeout_minutes: ${{ matrix.family == 'macos' && 45 || 15 }} + # Append notarization on the macOS leg. `packageReleaseDmg` only SIGNS + # the DMG — notarization is a separate Compose task, and because it was + # never invoked every release up to v1.13.1 shipped a signed but + # UNNOTARIZED DMG that Gatekeeper blocks on first launch. The task runs + # `notarytool submit --wait` and then `stapler staple`, in place, so the + # asset-collection step below still finds the same file. + # + # Gated on the cert AND all three notary secrets being present, so forks + # and credential-less runs keep producing a plain unsigned DMG exactly as + # before instead of failing. + command: ./gradlew --no-daemon :desktopApp:${{ matrix.tasks }}${{ (matrix.family == 'macos' && steps.mac_keychain.outputs.signing == 'true' && secrets.MAC_NOTARY_APPLE_ID != '' && secrets.MAC_NOTARY_PASSWORD != '' && secrets.MAC_NOTARY_TEAM_ID != '') && ' :desktopApp:notarizeReleaseDmg' || '' }} + + # Regression guard. The missing-notarization bug was invisible for many + # releases precisely because nothing ever asserted the outcome; assert it + # now so a silently-dropped notarization step can never ship again. + - name: Verify the DMG is notarized and stapled (macOS leg) + if: matrix.family == 'macos' + env: + EXPECT_NOTARIZED: ${{ (steps.mac_keychain.outputs.signing == 'true' && secrets.MAC_NOTARY_APPLE_ID != '' && secrets.MAC_NOTARY_PASSWORD != '' && secrets.MAC_NOTARY_TEAM_ID != '') && 'true' || 'false' }} + run: | + set -euo pipefail + DMG=$(find desktopApp/build/compose/binaries -name "*.dmg" -print -quit) + [[ -n "$DMG" ]] || { echo "::error::no DMG produced"; exit 1; } + echo "Checking $DMG" + + if [[ "$EXPECT_NOTARIZED" != "true" ]]; then + echo "::warning::Apple signing/notary credentials are not configured; this DMG is unsigned and unnotarized. Gatekeeper will block it, and it is not eligible for the Homebrew cask." + exit 0 + fi + + if ! xcrun stapler validate "$DMG"; then + echo "::error::$DMG has no stapled notarization ticket -- notarizeReleaseDmg did not run or failed" + exit 1 + fi + echo "notarization ticket stapled OK" # jpackage pins libicu to the build host's version (libicu74 on # ubuntu-24.04). Rewrite the .deb so it installs across Debian/Ubuntu. diff --git a/BUILDING.md b/BUILDING.md index 1fd7b61bf5..120a23172a 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -662,12 +662,19 @@ by any other channel. | OS | App location | State directories | |---|---|---| -| macOS | `/Applications/Amethyst.app` | `~/Library/Application Support/Amethyst`
`~/Library/Preferences/com.vitorpamplona.amethyst.desktop.plist`
`~/Library/Caches/Amethyst` | +| macOS | `/Applications/Amethyst.app` | `~/.amethyst` (accounts + keys)
`~/Library/Application Support/Amethyst` (Tor)
`~/Library/Caches/AmethystDesktop` (image cache)
`~/Library/Preferences/com.apple.java.util.prefs.plist` (**shared** — see below) | | Windows | `%LOCALAPPDATA%\Amethyst` or `C:\Program Files\Amethyst` | `%APPDATA%\Amethyst`
`%LOCALAPPDATA%\Amethyst` | | Linux (deb/rpm) | `/opt/amethyst` | `~/.config/amethyst`
`~/.local/share/amethyst`
`~/.cache/amethyst` | | Linux (AppImage/tar.gz) | user-chosen | Same as above | | Linux (Flatpak) | `/var/lib/flatpak` or `~/.local/share/flatpak` | `~/.var/app/com.vitorpamplona.amethyst.Desktop/` | +**macOS preferences are in a SHARED file.** `DesktopPreferences` uses the Java +Preferences API, which on macOS writes into +`~/Library/Preferences/com.apple.java.util.prefs.plist` — one plist for *every* +Java application on the machine, not a per-app file. Never delete it to "reset +Amethyst": that wipes unrelated apps' settings. This is why the Homebrew cask's +`zap` stanza deliberately omits it. + Uninstall: - Homebrew: `brew uninstall --cask amethyst-nostr && brew zap amethyst-nostr` diff --git a/desktopApp/packaging/homebrew/amethyst-nostr.rb b/desktopApp/packaging/homebrew/amethyst-nostr.rb index c63eef6ec1..0e0ae12854 100644 --- a/desktopApp/packaging/homebrew/amethyst-nostr.rb +++ b/desktopApp/packaging/homebrew/amethyst-nostr.rb @@ -16,13 +16,12 @@ # https://github.com/vitorpamplona/amethyst/releases/download/vX.Y.Z/amethyst-desktop-X.Y.Z-macos-arm64.dmg # shasum -a 256 amethyst.dmg cask "amethyst-nostr" do - version "1.12.6" - sha256 "69882e83ebcec6723e1ad5655ec2c9d1fa151b9d1a8ae51b869a9d62feabf093" + version "1.13.1" + sha256 "ead8f4f6263417d661b0d24be3853ec3284b330abc14748ebd77a2c2afcd2789" - url "https://github.com/vitorpamplona/amethyst/releases/download/v#{version}/amethyst-desktop-#{version}-macos-arm64.dmg", - verified: "github.com/vitorpamplona/amethyst/" + url "https://github.com/vitorpamplona/amethyst/releases/download/v#{version}/amethyst-desktop-#{version}-macos-arm64.dmg" name "Amethyst" - desc "Nostr client for desktop" + desc "Nostr client" homepage "https://github.com/vitorpamplona/amethyst" livecheck do @@ -30,9 +29,25 @@ cask "amethyst-nostr" do strategy :github_latest end + # The unrelated tiling window manager (cask `amethyst`, ianyh/Amethyst) also + # installs `Amethyst.app`, so the two cannot coexist in /Applications. + conflicts_with cask: "amethyst" depends_on arch: :arm64 + depends_on :macos app "Amethyst.app" - zap trash: "~/.amethyst" + # Verified against the source, not the docs: + # ~/.amethyst DesktopAccountStorage (accounts + keys) + # ~/Library/Application Support/Amethyst DesktopTorManager (tor/) + # ~/Library/Caches/AmethystDesktop Coil image cache + # + # Deliberately NOT zapped: ~/Library/Preferences/com.apple.java.util.prefs.plist. + # The app uses the Java Preferences API, which writes to that single SHARED + # plist — deleting it would wipe every other Java app's preferences too. + zap trash: [ + "~/.amethyst", + "~/Library/Application Support/Amethyst", + "~/Library/Caches/AmethystDesktop", + ] end