diff --git a/Cargo.lock b/Cargo.lock index bc0cd9c..d89085d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3375,7 +3375,7 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rsnap" -version = "0.2.5" +version = "0.2.6" dependencies = [ "color-eyre", "directories", @@ -3388,7 +3388,7 @@ dependencies = [ [[package]] name = "rsnap-capture-core" -version = "0.2.5" +version = "0.2.6" dependencies = [ "color-eyre", "fast_image_resize", @@ -3399,7 +3399,7 @@ dependencies = [ [[package]] name = "rsnap-host-ffi" -version = "0.2.5" +version = "0.2.6" dependencies = [ "rsnap-capture-core", "rsnap-overlay", @@ -3407,7 +3407,7 @@ dependencies = [ [[package]] name = "rsnap-overlay" -version = "0.2.5" +version = "0.2.6" dependencies = [ "block2 0.6.2", "color-eyre", @@ -3449,7 +3449,7 @@ dependencies = [ [[package]] name = "rsnap-perf" -version = "0.2.5" +version = "0.2.6" dependencies = [ "color-eyre", "image", diff --git a/Cargo.toml b/Cargo.toml index a2743ba..6d88f36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://hack.ink/rsnap" license = "GPL-3.0" readme = "README.md" repository = "https://github.com/hack-ink/rsnap" -version = "0.2.5" +version = "0.2.6" [workspace.dependencies] arboard = { version = "3.6" } @@ -54,9 +54,9 @@ wgpu = { version = "29.0" } winit = { version = "0.30", features = ["rwh_06"] } xcap = { version = "0.9" } -rsnap-capture-core = { version = "0.2.5", path = "packages/rsnap-capture-core" } -rsnap-host-ffi = { version = "0.2.5", path = "packages/rsnap-host-ffi" } -rsnap-overlay = { version = "0.2.5", path = "packages/rsnap-overlay" } +rsnap-capture-core = { version = "0.2.6", path = "packages/rsnap-capture-core" } +rsnap-host-ffi = { version = "0.2.6", path = "packages/rsnap-host-ffi" } +rsnap-overlay = { version = "0.2.6", path = "packages/rsnap-overlay" } [profile.final-release] inherits = "release" diff --git a/Makefile.toml b/Makefile.toml index 5a0f0eb..658bfa7 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -393,6 +393,12 @@ test -f "$APP_PATH/Contents/Resources/AppIcon.icns" test -d "$APP_PATH/Contents/Frameworks/Sparkle.framework" otool -L "$APP_PATH/Contents/MacOS/RsnapNativeHost" | grep -q '@rpath/Sparkle.framework' otool -l "$APP_PATH/Contents/MacOS/RsnapNativeHost" | grep -q '@executable_path/../Frameworks' +if otool -l "$APP_PATH/Contents/MacOS/RsnapNativeHost" \ + | awk '$1 == "cmd" && $2 == "LC_RPATH" { in_rpath = 1; next } in_rpath && $1 == "path" { sub(/^[[:space:]]*path /, ""); sub(/[[:space:]]+\(offset [0-9]+\)$/, ""); print; in_rpath = 0 }' \ + | grep -E '^(/Applications/.*\.app/Contents/Developer/Toolchains/.*/usr/lib/swift[^/]*/macosx|/Library/Developer/CommandLineTools/usr/lib/swift[^/]*/macosx|/Users/.*/Applications/.*\.app/Contents/Developer/Toolchains/.*/usr/lib/swift[^/]*/macosx)$'; then + echo "RsnapNativeHost must not ship local Swift toolchain rpaths" >&2 + exit 1 +fi plutil -extract CFBundleName raw "$APP_PATH/Contents/Info.plist" | grep -qx 'Rsnap' plutil -extract CFBundleDisplayName raw "$APP_PATH/Contents/Info.plist" | grep -qx 'Rsnap' plutil -extract CFBundleIdentifier raw "$APP_PATH/Contents/Info.plist" | grep -qx 'ink.hack.rsnap' diff --git a/native/macos-host/Sources/RsnapNativeHostKit/NativeHostSoftwareUpdater.swift b/native/macos-host/Sources/RsnapNativeHostKit/NativeHostSoftwareUpdater.swift index 1272e7f..cfee00d 100644 --- a/native/macos-host/Sources/RsnapNativeHostKit/NativeHostSoftwareUpdater.swift +++ b/native/macos-host/Sources/RsnapNativeHostKit/NativeHostSoftwareUpdater.swift @@ -94,7 +94,7 @@ final class NativeHostSoftwareUpdater { isConfigured: true, canCheckForUpdates: updater.canCheckForUpdates, allowsAutomaticUpdates: updater.allowsAutomaticUpdates, - mode: Self.mode( + mode: SoftwareUpdateModeResolution.mode( automaticallyChecksForUpdates: updater.automaticallyChecksForUpdates, automaticallyDownloadsUpdates: updater.automaticallyDownloadsUpdates), currentVersion: Self.currentAppVersionLabel, @@ -158,19 +158,6 @@ final class NativeHostSoftwareUpdater { && nonEmptyInfoValue(forKey: "SUPublicEDKey") != nil } - private static func mode( - automaticallyChecksForUpdates: Bool, - automaticallyDownloadsUpdates: Bool - ) -> Mode { - if automaticallyDownloadsUpdates { - return .install - } - if automaticallyChecksForUpdates { - return .check - } - return .off - } - private static var currentAppVersionLabel: String { nonEmptyInfoValue(forKey: "CFBundleShortVersionString") ?? "Development Build" } @@ -202,6 +189,32 @@ final class NativeHostSoftwareUpdater { } } +package enum SoftwareUpdateModeResolution { + fileprivate static func mode( + automaticallyChecksForUpdates: Bool, + automaticallyDownloadsUpdates: Bool + ) -> NativeHostSoftwareUpdater.Mode { + guard automaticallyChecksForUpdates else { + return .off + } + if automaticallyDownloadsUpdates { + return .install + } + return .check + } + + package static func modeRawValue( + automaticallyChecksForUpdates: Bool, + automaticallyDownloadsUpdates: Bool + ) -> String { + mode( + automaticallyChecksForUpdates: automaticallyChecksForUpdates, + automaticallyDownloadsUpdates: automaticallyDownloadsUpdates + ) + .rawValue + } +} + package enum SoftwareUpdateManualCheckAvailability { package static func isEnabled(sparkleCanCheckForUpdates: Bool) -> Bool { // Sparkle flips canCheckForUpdates off while a session is active, but Rsnap's diff --git a/native/macos-host/Sources/RsnapNativeHostKitProbe/main.swift b/native/macos-host/Sources/RsnapNativeHostKitProbe/main.swift index 8ed4dcd..ca021a2 100644 --- a/native/macos-host/Sources/RsnapNativeHostKitProbe/main.swift +++ b/native/macos-host/Sources/RsnapNativeHostKitProbe/main.swift @@ -11,6 +11,7 @@ enum RsnapNativeHostKitProbe { assertCaptureFrameEffectExpandsExportCanvas() assertScrollCaptureViewportPointAcceptsFlippedGlobalMouseCoordinates() assertScrollCaptureObservedInputAcceptsSourceWindowGutter() + assertSoftwareUpdateModeResolution() assertManualUpdateCheckRemainsAvailable() let minimapExportSize = CGSize(width: 100, height: 200) guard @@ -68,6 +69,25 @@ enum RsnapNativeHostKitProbe { assertLaunchAtLoginStateMapping() } + private static func assertSoftwareUpdateModeResolution() { + guard + SoftwareUpdateModeResolution.modeRawValue( + automaticallyChecksForUpdates: false, + automaticallyDownloadsUpdates: false) == "off", + SoftwareUpdateModeResolution.modeRawValue( + automaticallyChecksForUpdates: false, + automaticallyDownloadsUpdates: true) == "off", + SoftwareUpdateModeResolution.modeRawValue( + automaticallyChecksForUpdates: true, + automaticallyDownloadsUpdates: false) == "check", + SoftwareUpdateModeResolution.modeRawValue( + automaticallyChecksForUpdates: true, + automaticallyDownloadsUpdates: true) == "install" + else { + fatalError("software update mode should treat disabled checks as off") + } + } + private static func assertManualUpdateCheckRemainsAvailable() { guard SoftwareUpdateManualCheckAvailability.isEnabled(sparkleCanCheckForUpdates: true), diff --git a/scripts/build_and_run.sh b/scripts/build_and_run.sh index e64ff18..f907597 100755 --- a/scripts/build_and_run.sh +++ b/scripts/build_and_run.sh @@ -221,6 +221,45 @@ stage_sparkle_framework() { fi } +staged_app_rpaths() { + otool -l "$APP_BINARY" | awk ' + $1 == "cmd" && $2 == "LC_RPATH" { + in_rpath = 1 + next + } + in_rpath && $1 == "path" { + sub(/^[[:space:]]*path /, "") + sub(/[[:space:]]+\(offset [0-9]+\)$/, "") + print + in_rpath = 0 + } + ' +} + +is_local_toolchain_rpath() { + case "$1" in + /Applications/*.app/Contents/Developer/Toolchains/*/usr/lib/swift*/macosx | \ + /Library/Developer/CommandLineTools/usr/lib/swift*/macosx | \ + /Users/*/Applications/*.app/Contents/Developer/Toolchains/*/usr/lib/swift*/macosx) + return 0 + ;; + *) + return 1 + ;; + esac +} + +sanitize_staged_app_rpaths() { + local rpath + while IFS= read -r rpath; do + [[ -n "$rpath" ]] || continue + if is_local_toolchain_rpath "$rpath"; then + install_name_tool -delete_rpath "$rpath" "$APP_BINARY" + STAGED_APP_DIRTY=1 + fi + done < <(staged_app_rpaths) +} + write_if_changed() { local destination="$1" local contents="$2" @@ -273,6 +312,7 @@ stage_app_bundle() { install_name_tool -add_rpath '@executable_path/../Frameworks' "$APP_BINARY" STAGED_APP_DIRTY=1 fi + sanitize_staged_app_rpaths if [[ -f "$APP_ICON_SOURCE" ]]; then if copy_if_changed "$APP_ICON_SOURCE" "$APP_RESOURCES/$APP_ICON_NAME"; then