oficina / meet Public

Bring Sparkle updates onto the renamed Mac app

Sparkle was never on main. It lived only on native/sparkle-updates
(68fc7b7), built from pre-rename sources and never merged, which is why
the published Mac download was still Meet long after the rename landed.
This carries those six files forward onto the renamed tree.

The Mac app is a NEW lineage, not a continuation. Sparkle keys updates off
the bundle identifier, so net.amadan.oficina.calls will never be offered an
update published under net.amadan.oficina.meet — an install of the old one
goes quiet rather than migrating. Nobody has it installed, so the cost is
nil, but it is why this starts at build 1 rather than continuing Meet's
build 2: the old numbering would imply a continuity that does not exist.

One thing worth recording, because doing it the obvious way breaks the
app. bundle-mac.py exists on both sides: the Sparkle branch added the
configuration argument, the products path and the resource-bundle copy,
while main separately grew the CFBundleURLTypes block for native app
links. Taking the Sparkle branch's file wholesale silently drops the URL
scheme and the Mac app stops answering oficina-calls:// links. So main's
file is the base here and only the Sparkle hunks are applied on top.

RastrilloMacUpdates is conditioned on macOS, and the iPhone build was
checked to confirm it stays that way: CallsPhone.app embeds CallsBridge
and WebRTC and no Sparkle.

Verified: GOTOOLCHAIN=go1.26.5 make ci passes, the iOS simulator build
succeeds, and the release is notarized, stapled and accepted as
"Notarized Developer ID".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 925af0cc805059890320c4f8a07548925c900da8 parent bb364ca
6 files changed, +53 −8
  • native/apple/Package.resolved +17 −0
  • native/apple/Package.swift +6 −1
  • native/apple/Sources/CallsMac/CallsMac.swift +10 −1
  • native/mac-updates.json +6 −0
  • native/scripts/build-mac.sh +6 −5
  • native/scripts/bundle-mac.py +8 −1

CI passed — run details

diff --git a/native/apple/Package.resolved b/native/apple/Package.resolved
index c68b7f5..d797920 100644
--- a/native/apple/Package.resolved
+++ b/native/apple/Package.resolved
@@ -1,5 +1,22 @@
{
"pins" : [
+ {
+ "identity" : "native",
+ "kind" : "remoteSourceControl",
+ "location" : "https://amadan.net/rastrillo/native",
+ "state" : {
+ "revision" : "4c61d6283a188c95647e1d0e609e69d29c2e7fa1"
+ }
+ },
+ {
+ "identity" : "sparkle",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/sparkle-project/Sparkle",
+ "state" : {
+ "revision" : "ac2def288cbff5cfc7df3ffef6abdf45b72bcb0a",
+ "version" : "2.9.6"
+ }
+ },
{
"identity" : "webrtc",
"kind" : "remoteSourceControl",
diff --git a/native/apple/Package.swift b/native/apple/Package.swift
index 9f3b436..522f2fd 100644
--- a/native/apple/Package.swift
+++ b/native/apple/Package.swift
@@ -1,5 +1,9 @@
// swift-tools-version: 5.9
import PackageDescription
+import Foundation
+
+let sharedNative: Package.Dependency = ProcessInfo.processInfo.environment["OFICINA_NATIVE_PACKAGE_PATH"].map { .package(path: $0) }
+ ?? .package(url: "https://amadan.net/rastrillo/native", revision: "4c61d6283a188c95647e1d0e609e69d29c2e7fa1")
let package = Package(
name: "CallsNative",
@@ -10,13 +14,14 @@ let package = Package(
.executable(name: "CallsInterop", targets: ["CallsInterop"]),
],
dependencies: [
+ sharedNative,
.package(url: "https://github.com/stasel/WebRTC.git", exact: "152.0.0")
],
targets: [
.binaryTarget(name: "CallsBridge", path: "../build/CallsBridge.xcframework"),
.target(
name: "CallsKit", dependencies: ["CallsBridge", .product(name: "WebRTC", package: "WebRTC")]),
- .executableTarget(name: "CallsMac", dependencies: ["CallsKit"], resources: [.copy("Resources/AppIcon.png"), .copy("Resources/MacIcon.png")]),
+ .executableTarget(name: "CallsMac", dependencies: [.product(name: "RastrilloMacUpdates", package: "native", condition: .when(platforms: [.macOS])), "CallsKit"], resources: [.copy("Resources/AppIcon.png"), .copy("Resources/MacIcon.png")]),
.executableTarget(name: "CallsInterop", dependencies: ["CallsKit"]),
.testTarget(name: "CallsKitTests", dependencies: ["CallsKit"]),
]
diff --git a/native/apple/Sources/CallsMac/CallsMac.swift b/native/apple/Sources/CallsMac/CallsMac.swift
index 8252709..c059dfa 100644
--- a/native/apple/Sources/CallsMac/CallsMac.swift
+++ b/native/apple/Sources/CallsMac/CallsMac.swift
@@ -1,3 +1,6 @@
+#if os(macOS)
+import RastrilloMacUpdates
+#endif
import AppKit
@preconcurrency import CallsBridge
import CallsKit
@@ -5,6 +8,9 @@ import SwiftUI
import WebRTC
@main struct CallsMacApp: App {
+ #if os(macOS)
+ @State private var updater = NativeUpdater()
+ #endif
@NSApplicationDelegateAdaptor(CallsAppDelegate.self) private var appDelegate
@StateObject private var model = CallsModel()
var body: some Scene {
@@ -13,7 +19,10 @@ import WebRTC
.onOpenURL { model.openMeetingURL($0) }
}
.defaultSize(width: 1000, height: 760)
- .commands { CommandGroup(replacing: .newItem) {} }
+ .commands {
+ CommandGroup(replacing: .newItem) {}
+ NativeUpdateCommands(updater: updater)
+ }
}
}
diff --git a/native/mac-updates.json b/native/mac-updates.json
new file mode 100644
index 0000000..bad934c
--- /dev/null
+++ b/native/mac-updates.json
@@ -0,0 +1,6 @@
+{
+ "bundle_id": "net.amadan.oficina.calls",
+ "feed_url": "https://raw.amadan.net/oficina/native-releases/main/calls/appcast.xml",
+ "public_ed_key": "/FxO7cZnM7P5c9ZrUPUJvpCnR65sNmrzte+I5vHarE8=",
+ "keychain_account": "oficina"
+}
diff --git a/native/scripts/build-mac.sh b/native/scripts/build-mac.sh
index 0e9ad65..12a3634 100755
--- a/native/scripts/build-mac.sh
+++ b/native/scripts/build-mac.sh
@@ -1,9 +1,10 @@
#!/bin/sh
set -eu
cd "$(dirname "$0")/.."
-./scripts/build-bridge.sh
-swift scripts/generate-mac-icon.swift
-swift build --package-path apple
-python3 scripts/bundle-mac.py
-codesign --force --deep --sign - build/Calls.app
+if [ "${OFICINA_SKIP_PREPARE:-0}" != 1 ]; then ./scripts/build-bridge.sh; fi
+configuration="${OFICINA_BUILD_CONFIGURATION:-debug}"
+swift build --package-path apple --configuration "$configuration" --product CallsMac
+python3 scripts/bundle-mac.py "$configuration"
+shared_native="${OFICINA_NATIVE_PACKAGE_PATH:-apple/.build/checkouts/native}"
+python3 "$shared_native/tools/mac_updates.py" build/Calls.app --package apple "$@"
printf 'Built %s/build/Calls.app\n' "$PWD"
diff --git a/native/scripts/bundle-mac.py b/native/scripts/bundle-mac.py
index 3df0828..94c30b7 100644
--- a/native/scripts/bundle-mac.py
+++ b/native/scripts/bundle-mac.py
@@ -3,13 +3,18 @@ from pathlib import Path
import plistlib
import shutil
import subprocess
+import sys
+configuration = sys.argv[1] if len(sys.argv) > 1 else 'debug'
+if configuration not in ('debug', 'release'): raise ValueError('invalid build configuration')
root = Path(__file__).resolve().parent.parent
+products = Path(subprocess.check_output(['swift', 'build', '--package-path', str(root / 'apple'), '--configuration', configuration, '--show-bin-path'], text=True).strip())
bundle = root / 'build/Calls.app/Contents'
+if bundle.parent.exists(): shutil.rmtree(bundle.parent)
(bundle / 'MacOS').mkdir(parents=True, exist_ok=True)
(bundle / 'Frameworks').mkdir(parents=True, exist_ok=True)
(bundle / 'Resources').mkdir(parents=True, exist_ok=True)
-shutil.copy2(root / 'apple/.build/debug/CallsMac', bundle / 'MacOS/CallsMac')
+shutil.copy2(products / 'CallsMac', bundle / 'MacOS/CallsMac')
subprocess.run(['swift', str(root / 'scripts/generate-mac-icon.swift')], check=True)
icon = root / 'apple/Sources/CallsMac/Resources/MacIcon.png'
shutil.copy2(icon, bundle / 'Resources/MacIcon.png')
@@ -53,4 +58,6 @@ for xc in [root / 'build/CallsBridge.xcframework', *list((root / 'apple/.build/a
dest = bundle / 'Frameworks' / source.name
if dest.exists(): shutil.rmtree(dest)
shutil.copytree(source, dest, symlinks=True)
+for resource in products.glob('*.bundle'):
+ shutil.copytree(resource, bundle / 'Resources' / resource.name, symlinks=True)
subprocess.run(['install_name_tool', '-add_rpath', '@executable_path/../Frameworks', str(bundle / 'MacOS/CallsMac')], check=True)