mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 20:55:44 +00:00
Re-enable Sparkle auto-update integration for Mac builds (#5318)
* Add native Sparkle plugin to enable Mac auto-updates The Sparkle framework was being injected into the app bundle, but nothing was initializing it. This adds: - Native Objective-C plugin (SparklePlugin.m) that initializes SPUStandardUpdaterController at runtime - C# wrapper (SparkleUpdater.cs) for Unity to call the native plugin - SparkleInitializer.cs uses RuntimeInitializeOnLoadMethod to automatically initialize Sparkle at app startup - Build script to compile the plugin as a universal binary The plugin is weak-linked against Sparkle.framework, which is injected separately by inject_sparkle.sh during the build process. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Convert Sparkle plugin build from clang to Bazel - Add Sparkle framework as http_archive dependency in MODULE.bazel - Add BUILD.sparkle to import the framework - Add BUILD.bazel for SparklePlugin using macos_bundle rule - Update build_sparkle_plugin.sh to use Bazel instead of direct clang - Register Apple CC toolchain extension 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix symbol exports for SparklePlugin native library The C functions need to be exported with visibility("default") and explicit linker flags for Unity P/Invoke to find them. Without this, the bundle binary had no exported symbols. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Re-enable Sparkle auto-update integration for Mac builds Restores Sparkle integration that was temporarily removed in #5317: - Restore inject_sparkle.sh script - Add Sparkle injection step to mac_build.yml - Re-enable Sparkle signing and appcast updates in deploy step Combined with native SparklePlugin that initializes Sparkle at runtime. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Build SparklePlugin.bundle before Unity build The SparklePlugin.bundle.meta file tells Unity to include the plugin, but the actual bundle needs to be built by Bazel first. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Convert SparklePlugin Info.plist to XML format Unity's build system requires Info.plist files in XML format, but Bazel outputs them in binary plist format. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ on:
|
||||
- "src/main/go/net/eagle0/build/mac_build_handler/**"
|
||||
- "scripts/build_protos.sh"
|
||||
- "scripts/build_mac_plugin.sh"
|
||||
- "scripts/inject_sparkle.sh"
|
||||
- "scripts/codesign_mac_app.sh"
|
||||
- "scripts/notarize_submit.sh"
|
||||
- "scripts/notarize_wait.sh"
|
||||
@@ -31,6 +32,7 @@ on:
|
||||
- "src/main/protobuf/net/eagle0/eagle/views/**"
|
||||
- "src/main/go/net/eagle0/build/mac_build_handler/**"
|
||||
- "scripts/build_mac_plugin.sh"
|
||||
- "scripts/inject_sparkle.sh"
|
||||
- "scripts/codesign_mac_app.sh"
|
||||
- "scripts/notarize_submit.sh"
|
||||
- "scripts/notarize_wait.sh"
|
||||
@@ -74,6 +76,14 @@ jobs:
|
||||
- name: Persist Library/
|
||||
run: ./ci/github_actions/persist_library.sh
|
||||
|
||||
- name: Inject Sparkle Framework
|
||||
if: success()
|
||||
env:
|
||||
SPARKLE_EDDSA_PUBLIC_KEY: ${{ secrets.SPARKLE_EDDSA_PUBLIC_KEY }}
|
||||
run: |
|
||||
chmod +x ./scripts/inject_sparkle.sh
|
||||
./scripts/inject_sparkle.sh "/tmp/eagle0/eagle0MAC/eagle0.app"
|
||||
|
||||
- name: Check if should deploy
|
||||
id: check-deploy
|
||||
run: |
|
||||
@@ -239,11 +249,20 @@ jobs:
|
||||
env:
|
||||
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
|
||||
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
||||
SPARKLE_EDDSA_PRIVATE_KEY: ${{ secrets.SPARKLE_EDDSA_PRIVATE_KEY }}
|
||||
run: |
|
||||
# Write private key to temp file for signing
|
||||
SPARKLE_PRIVATE_KEY_PATH="/tmp/sparkle_private_key"
|
||||
echo "$SPARKLE_EDDSA_PRIVATE_KEY" > "$SPARKLE_PRIVATE_KEY_PATH"
|
||||
chmod 600 "$SPARKLE_PRIVATE_KEY_PATH"
|
||||
|
||||
VERSION=$(git describe --tags --always)
|
||||
BUILD_NUMBER=$(git rev-list --count HEAD)
|
||||
|
||||
bazel run //src/main/go/net/eagle0/build/mac_build_handler:mac_build_handler -- \
|
||||
"/tmp/eagle0/eagle0MAC/eagle0.app" \
|
||||
"$VERSION" \
|
||||
"$BUILD_NUMBER"
|
||||
"$BUILD_NUMBER" \
|
||||
"$SPARKLE_PRIVATE_KEY_PATH"
|
||||
|
||||
rm "$SPARKLE_PRIVATE_KEY_PATH"
|
||||
|
||||
@@ -130,6 +130,13 @@ bazel_dep(name = "apple_support", version = "1.21.1", repo_name = "build_bazel_a
|
||||
bazel_dep(name = "rules_apple", version = "4.3.3", repo_name = "build_bazel_rules_apple")
|
||||
bazel_dep(name = "rules_swift", version = "2.4.0", repo_name = "build_bazel_rules_swift")
|
||||
|
||||
# Register Apple CC toolchain for Objective-C compilation
|
||||
apple_cc_configure = use_extension(
|
||||
"@build_bazel_apple_support//crosstool:setup.bzl",
|
||||
"apple_cc_configure_extension",
|
||||
)
|
||||
use_repo(apple_cc_configure, "local_config_apple_cc")
|
||||
|
||||
#
|
||||
# Protocol Buffers & RPC
|
||||
#
|
||||
@@ -293,6 +300,17 @@ http_archive(
|
||||
],
|
||||
)
|
||||
|
||||
# Sparkle framework for macOS auto-updates
|
||||
SPARKLE_VERSION = "2.6.4"
|
||||
|
||||
http_archive(
|
||||
name = "sparkle",
|
||||
build_file = "@//external:BUILD.sparkle",
|
||||
sha256 = "50612a06038abc931f16011d7903b8326a362c1074dabccb718404ce8e585f0b",
|
||||
strip_prefix = "",
|
||||
url = "https://github.com/sparkle-project/Sparkle/releases/download/%s/Sparkle-%s.tar.xz" % (SPARKLE_VERSION, SPARKLE_VERSION),
|
||||
)
|
||||
|
||||
# Busybox static binary for Docker health checks (provides nc, wget, etc.)
|
||||
# Primary: DigitalOcean Spaces (public, reliable)
|
||||
# Fallback: busybox.net (can be unreliable/slow)
|
||||
|
||||
Generated
+1
-1
@@ -396,7 +396,7 @@
|
||||
"@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "Z3yAd66IJL0GAZUTSeMOjoHiE1SZPPwiIs/XQui5BvE=",
|
||||
"usagesDigest": "tl3VVeQX3Hzh7FhM2gjnkCwEJpRMlY5S6a850WY/xvc=",
|
||||
"usagesDigest": "DqQsfZN5lA8z+nLEEY+EpKGzQ8M73mDm/A8lofDSyus=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
"envVariables": {},
|
||||
|
||||
@@ -10,6 +10,9 @@ COMMIT=$(/usr/bin/git rev-parse --short HEAD)
|
||||
/bin/echo "build Mac plugin"
|
||||
./scripts/build_mac_plugin.sh
|
||||
|
||||
/bin/echo "build Sparkle plugin"
|
||||
./scripts/build_sparkle_plugin.sh
|
||||
|
||||
git log -3
|
||||
|
||||
/bin/echo "build Mac"
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
load("@build_bazel_rules_apple//apple:apple.bzl", "apple_dynamic_framework_import")
|
||||
|
||||
# Import pre-built Sparkle framework
|
||||
apple_dynamic_framework_import(
|
||||
name = "Sparkle",
|
||||
framework_imports = glob(["Sparkle.framework/**"]),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -8,3 +8,6 @@ ZIP_LOCATION=$(bazel cquery --config=mactools --output=files @net_eagle0_unity_g
|
||||
/usr/bin/unzip -o $ZIP_LOCATION -d src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Plugins/
|
||||
|
||||
/usr/bin/plutil -convert xml1 src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Plugins/DarwinGodiceBundle.bundle/Contents/Info.plist
|
||||
|
||||
/bin/echo "building sparkle plugin"
|
||||
./scripts/build_sparkle_plugin.sh
|
||||
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Build the SparklePlugin native library for Unity using Bazel
|
||||
#
|
||||
# Usage: build_sparkle_plugin.sh [output_dir]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
OUTPUT_DIR="${1:-$PROJECT_ROOT/src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Plugins/macOS}"
|
||||
|
||||
echo "=== Building SparklePlugin with Bazel ==="
|
||||
|
||||
bazel build --config=mactools //src/main/objc/net/eagle0/sparkle:SparklePlugin
|
||||
|
||||
# Get the zip path from bazel
|
||||
ZIP_PATH=$(bazel cquery --config=mactools --output=files //src/main/objc/net/eagle0/sparkle:SparklePlugin 2>/dev/null)
|
||||
|
||||
echo "=== Extracting SparklePlugin.bundle ==="
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
rm -rf "$OUTPUT_DIR/SparklePlugin.bundle"
|
||||
unzip -o "$ZIP_PATH" -d "$OUTPUT_DIR/"
|
||||
|
||||
# Convert Info.plist from binary to XML format (Unity requires XML)
|
||||
/usr/bin/plutil -convert xml1 "$OUTPUT_DIR/SparklePlugin.bundle/Contents/Info.plist"
|
||||
|
||||
echo "=== SparklePlugin built successfully ==="
|
||||
ls -la "$OUTPUT_DIR/SparklePlugin.bundle/"
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Inject Sparkle framework into a macOS .app bundle for auto-updates
|
||||
# Usage: inject_sparkle.sh <app_path>
|
||||
#
|
||||
# Environment variables (required):
|
||||
# SPARKLE_EDDSA_PUBLIC_KEY - EdDSA public key for verifying updates
|
||||
#
|
||||
# Optional environment variables:
|
||||
# SPARKLE_FEED_URL - Appcast URL (default: https://assets.eagle0.net/mac/appcast.xml)
|
||||
# SPARKLE_VERSION - Sparkle version to use (default: 2.6.4)
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
APP_PATH="$1"
|
||||
SPARKLE_VERSION="${SPARKLE_VERSION:-2.6.4}"
|
||||
SPARKLE_FEED_URL="${SPARKLE_FEED_URL:-https://assets.eagle0.net/mac/appcast.xml}"
|
||||
SPARKLE_CACHE_DIR="/tmp/sparkle-cache"
|
||||
|
||||
if [ ! -d "$APP_PATH" ]; then
|
||||
echo "ERROR: App not found at $APP_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${SPARKLE_EDDSA_PUBLIC_KEY:-}" ]; then
|
||||
echo "ERROR: SPARKLE_EDDSA_PUBLIC_KEY environment variable not set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Download Sparkle if not cached
|
||||
SPARKLE_DIR="$SPARKLE_CACHE_DIR/Sparkle-$SPARKLE_VERSION"
|
||||
if [ ! -d "$SPARKLE_DIR/Sparkle.framework" ]; then
|
||||
echo "=== Downloading Sparkle $SPARKLE_VERSION ==="
|
||||
mkdir -p "$SPARKLE_CACHE_DIR"
|
||||
SPARKLE_URL="https://github.com/sparkle-project/Sparkle/releases/download/${SPARKLE_VERSION}/Sparkle-${SPARKLE_VERSION}.tar.xz"
|
||||
curl -L "$SPARKLE_URL" | tar -xJ -C "$SPARKLE_CACHE_DIR"
|
||||
mv "$SPARKLE_CACHE_DIR/Sparkle-$SPARKLE_VERSION" "$SPARKLE_DIR" 2>/dev/null || true
|
||||
# If the extracted directory doesn't match version pattern, it may just be "Sparkle"
|
||||
if [ ! -d "$SPARKLE_DIR" ]; then
|
||||
mkdir -p "$SPARKLE_DIR"
|
||||
mv "$SPARKLE_CACHE_DIR/Sparkle.framework" "$SPARKLE_DIR/" 2>/dev/null || true
|
||||
mv "$SPARKLE_CACHE_DIR/bin" "$SPARKLE_DIR/" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "=== Injecting Sparkle framework ==="
|
||||
FRAMEWORKS_DIR="$APP_PATH/Contents/Frameworks"
|
||||
mkdir -p "$FRAMEWORKS_DIR"
|
||||
|
||||
# Copy Sparkle framework
|
||||
cp -R "$SPARKLE_DIR/Sparkle.framework" "$FRAMEWORKS_DIR/"
|
||||
|
||||
# Also copy the XPC services if present
|
||||
if [ -d "$SPARKLE_DIR/Sparkle.framework/Versions/B/XPCServices" ]; then
|
||||
echo "Sparkle XPC services present"
|
||||
fi
|
||||
|
||||
echo "=== Updating Info.plist ==="
|
||||
PLIST_PATH="$APP_PATH/Contents/Info.plist"
|
||||
|
||||
# Add Sparkle configuration to Info.plist
|
||||
/usr/libexec/PlistBuddy -c "Delete :SUFeedURL" "$PLIST_PATH" 2>/dev/null || true
|
||||
/usr/libexec/PlistBuddy -c "Add :SUFeedURL string '$SPARKLE_FEED_URL'" "$PLIST_PATH"
|
||||
|
||||
/usr/libexec/PlistBuddy -c "Delete :SUPublicEDKey" "$PLIST_PATH" 2>/dev/null || true
|
||||
/usr/libexec/PlistBuddy -c "Add :SUPublicEDKey string '$SPARKLE_EDDSA_PUBLIC_KEY'" "$PLIST_PATH"
|
||||
|
||||
/usr/libexec/PlistBuddy -c "Delete :SUEnableAutomaticChecks" "$PLIST_PATH" 2>/dev/null || true
|
||||
/usr/libexec/PlistBuddy -c "Add :SUEnableAutomaticChecks bool true" "$PLIST_PATH"
|
||||
|
||||
# Set bundle version from git for Sparkle version comparison
|
||||
VERSION=$(git describe --tags --always 2>/dev/null || echo "1.0.0")
|
||||
BUILD_NUMBER=$(git rev-list --count HEAD 2>/dev/null || echo "1")
|
||||
|
||||
echo "Setting version: $VERSION (build $BUILD_NUMBER)"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION" "$PLIST_PATH" 2>/dev/null || \
|
||||
/usr/libexec/PlistBuddy -c "Add :CFBundleShortVersionString string '$VERSION'" "$PLIST_PATH"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$PLIST_PATH" 2>/dev/null || \
|
||||
/usr/libexec/PlistBuddy -c "Add :CFBundleVersion string '$BUILD_NUMBER'" "$PLIST_PATH"
|
||||
|
||||
# Add URL scheme for invitation codes (eagle0://invite?code=XXXX)
|
||||
echo "=== Adding URL scheme for invitation codes ==="
|
||||
/usr/libexec/PlistBuddy -c "Delete :CFBundleURLTypes" "$PLIST_PATH" 2>/dev/null || true
|
||||
/usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes array" "$PLIST_PATH"
|
||||
/usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:0 dict" "$PLIST_PATH"
|
||||
/usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:0:CFBundleURLName string 'com.Shardok-Games.eagle0'" "$PLIST_PATH"
|
||||
/usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:0:CFBundleURLSchemes array" "$PLIST_PATH"
|
||||
/usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:0:CFBundleURLSchemes:0 string 'eagle0'" "$PLIST_PATH"
|
||||
|
||||
echo "=== Sparkle injection complete ==="
|
||||
echo "App: $APP_PATH"
|
||||
echo "Feed URL: $SPARKLE_FEED_URL"
|
||||
echo "Version: $VERSION (build $BUILD_NUMBER)"
|
||||
@@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes Sparkle auto-updater at app startup.
|
||||
/// Uses RuntimeInitializeOnLoadMethod to ensure early initialization.
|
||||
/// </summary>
|
||||
public static class SparkleInitializer {
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
private static void Initialize() {
|
||||
Debug.Log("SparkleInitializer: Initializing Sparkle at startup");
|
||||
SparkleUpdater.Initialize();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b4c6d8e0f1a3b5c7d9e1f3a5b7c9d1e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -100
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,120 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// C# wrapper for the native SparklePlugin on macOS.
|
||||
/// Provides auto-update functionality via the Sparkle framework.
|
||||
/// On non-macOS platforms, all methods are no-ops.
|
||||
/// </summary>
|
||||
public static class SparkleUpdater {
|
||||
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
|
||||
private const string PluginName = "SparklePlugin";
|
||||
|
||||
[DllImport(PluginName)]
|
||||
private static extern void SparklePlugin_Initialize();
|
||||
|
||||
[DllImport(PluginName)]
|
||||
private static extern void SparklePlugin_CheckForUpdates();
|
||||
|
||||
[DllImport(PluginName)]
|
||||
private static extern void SparklePlugin_CheckForUpdatesInBackground();
|
||||
|
||||
[DllImport(PluginName)]
|
||||
private static extern int SparklePlugin_IsCheckingForUpdates();
|
||||
|
||||
[DllImport(PluginName)]
|
||||
private static extern int SparklePlugin_GetAutomaticallyChecksForUpdates();
|
||||
|
||||
[DllImport(PluginName)]
|
||||
private static extern void SparklePlugin_SetAutomaticallyChecksForUpdates(int enabled);
|
||||
|
||||
private static bool _initialized = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the Sparkle updater. Should be called once at app startup.
|
||||
/// This starts automatic background update checks based on Info.plist settings.
|
||||
/// </summary>
|
||||
public static void Initialize() {
|
||||
if (_initialized) {
|
||||
Debug.Log("SparkleUpdater: Already initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("SparkleUpdater: Initializing Sparkle");
|
||||
try {
|
||||
SparklePlugin_Initialize();
|
||||
_initialized = true;
|
||||
Debug.Log("SparkleUpdater: Initialization complete");
|
||||
} catch (System.Exception e) {
|
||||
Debug.LogError($"SparkleUpdater: Failed to initialize: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually check for updates. Shows the update UI to the user.
|
||||
/// </summary>
|
||||
public static void CheckForUpdates() {
|
||||
if (!_initialized) {
|
||||
Debug.LogWarning("SparkleUpdater: Not initialized");
|
||||
return;
|
||||
}
|
||||
SparklePlugin_CheckForUpdates();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for updates silently in the background.
|
||||
/// Only shows UI if an update is found.
|
||||
/// </summary>
|
||||
public static void CheckForUpdatesInBackground() {
|
||||
if (!_initialized) {
|
||||
Debug.LogWarning("SparkleUpdater: Not initialized");
|
||||
return;
|
||||
}
|
||||
SparklePlugin_CheckForUpdatesInBackground();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if an update check is currently in progress.
|
||||
/// </summary>
|
||||
public static bool IsCheckingForUpdates {
|
||||
get {
|
||||
if (!_initialized) return false;
|
||||
return SparklePlugin_IsCheckingForUpdates() != 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether automatic update checks are enabled.
|
||||
/// </summary>
|
||||
public static bool AutomaticallyChecksForUpdates {
|
||||
get {
|
||||
if (!_initialized) return false;
|
||||
return SparklePlugin_GetAutomaticallyChecksForUpdates() != 0;
|
||||
}
|
||||
set {
|
||||
if (!_initialized) return;
|
||||
SparklePlugin_SetAutomaticallyChecksForUpdates(value? 1: 0);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
// Stub implementations for non-macOS platforms and Editor
|
||||
|
||||
public static void Initialize() { Debug.Log("SparkleUpdater: Not available on this platform"); }
|
||||
|
||||
public static void CheckForUpdates() {
|
||||
Debug.Log("SparkleUpdater: Not available on this platform");
|
||||
}
|
||||
|
||||
public static void CheckForUpdatesInBackground() {
|
||||
Debug.Log("SparkleUpdater: Not available on this platform");
|
||||
}
|
||||
|
||||
public static bool IsCheckingForUpdates => false;
|
||||
|
||||
public static bool AutomaticallyChecksForUpdates {
|
||||
get => false;
|
||||
set {}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a3b5c7d9e1f2a3b4c5d6e7f8a9b0c1d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,3 +1,6 @@
|
||||
System.Runtime.CompilerServices.Unsafe/**
|
||||
!**/*.psd
|
||||
**/*.dll
|
||||
# Native plugins built at CI time
|
||||
DarwinGodiceBundle.bundle/
|
||||
macOS/SparklePlugin.bundle/
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c5d7e9f1a2b4c6d8e0f2a4b6c8d0e2f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d6e8f0a2b3c5d7e9f1a3b5c7d9e1f3a
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Editor: 1
|
||||
Exclude Linux64: 1
|
||||
Exclude OSXUniversal: 0
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 1
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_bundle")
|
||||
|
||||
# Native Sparkle plugin for Unity
|
||||
# This plugin initializes Sparkle auto-updater at runtime
|
||||
objc_library(
|
||||
name = "sparkle_plugin_lib",
|
||||
srcs = ["SparklePlugin.m"],
|
||||
copts = [
|
||||
"-fmodules",
|
||||
"-fobjc-arc",
|
||||
"-fvisibility=default", # Export symbols for Unity P/Invoke
|
||||
],
|
||||
sdk_frameworks = ["Foundation"],
|
||||
deps = ["@sparkle//:Sparkle"],
|
||||
)
|
||||
|
||||
macos_bundle(
|
||||
name = "SparklePlugin",
|
||||
bundle_id = "net.eagle0.SparklePlugin",
|
||||
bundle_name = "SparklePlugin",
|
||||
infoplists = ["Info.plist"],
|
||||
linkopts = [
|
||||
# Export symbols for Unity P/Invoke
|
||||
"-exported_symbol",
|
||||
"_SparklePlugin_Initialize",
|
||||
"-exported_symbol",
|
||||
"_SparklePlugin_CheckForUpdates",
|
||||
"-exported_symbol",
|
||||
"_SparklePlugin_CheckForUpdatesInBackground",
|
||||
"-exported_symbol",
|
||||
"_SparklePlugin_IsCheckingForUpdates",
|
||||
"-exported_symbol",
|
||||
"_SparklePlugin_GetAutomaticallyChecksForUpdates",
|
||||
"-exported_symbol",
|
||||
"_SparklePlugin_SetAutomaticallyChecksForUpdates",
|
||||
],
|
||||
minimum_os_version = "10.13",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":sparkle_plugin_lib"],
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>SparklePlugin</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>net.eagle0.SparklePlugin</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>SparklePlugin</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// SparklePlugin.m
|
||||
// Native macOS plugin for Unity to initialize Sparkle auto-updates
|
||||
//
|
||||
// Copyright 2026 Dan Crosby
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Sparkle/Sparkle.h>
|
||||
|
||||
// The updater controller - retained for the lifetime of the app
|
||||
static SPUStandardUpdaterController *updaterController = nil;
|
||||
|
||||
// Export macro for Unity P/Invoke
|
||||
#define EXPORT __attribute__((visibility("default")))
|
||||
|
||||
// Called from Unity to initialize Sparkle
|
||||
// This should be called once at app startup
|
||||
EXPORT void SparklePlugin_Initialize(void) {
|
||||
if (updaterController != nil) {
|
||||
NSLog(@"SparklePlugin: Already initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"SparklePlugin: Initializing Sparkle auto-updater");
|
||||
|
||||
// Create the updater controller with automatic update checking enabled
|
||||
// The feed URL and public key are read from Info.plist (SUFeedURL, SUPublicEDKey)
|
||||
updaterController = [[SPUStandardUpdaterController alloc]
|
||||
initWithStartingUpdater:YES
|
||||
updaterDelegate:nil
|
||||
userDriverDelegate:nil];
|
||||
|
||||
if (updaterController != nil) {
|
||||
NSLog(@"SparklePlugin: Sparkle initialized successfully");
|
||||
} else {
|
||||
NSLog(@"SparklePlugin: Failed to initialize Sparkle");
|
||||
}
|
||||
}
|
||||
|
||||
// Manually trigger an update check (shows UI)
|
||||
EXPORT void SparklePlugin_CheckForUpdates(void) {
|
||||
if (updaterController == nil) {
|
||||
NSLog(@"SparklePlugin: Not initialized, call Initialize first");
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"SparklePlugin: Checking for updates");
|
||||
[updaterController checkForUpdates:nil];
|
||||
}
|
||||
|
||||
// Check for updates silently in background (no UI unless update found)
|
||||
EXPORT void SparklePlugin_CheckForUpdatesInBackground(void) {
|
||||
if (updaterController == nil) {
|
||||
NSLog(@"SparklePlugin: Not initialized, call Initialize first");
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"SparklePlugin: Checking for updates in background");
|
||||
[[updaterController updater] checkForUpdatesInBackground];
|
||||
}
|
||||
|
||||
// Returns 1 if an update check is in progress, 0 otherwise
|
||||
EXPORT int SparklePlugin_IsCheckingForUpdates(void) {
|
||||
if (updaterController == nil) {
|
||||
return 0;
|
||||
}
|
||||
return [[updaterController updater] sessionInProgress] ? 1 : 0;
|
||||
}
|
||||
|
||||
// Returns 1 if automatic update checks are enabled, 0 otherwise
|
||||
EXPORT int SparklePlugin_GetAutomaticallyChecksForUpdates(void) {
|
||||
if (updaterController == nil) {
|
||||
return 0;
|
||||
}
|
||||
return [[updaterController updater] automaticallyChecksForUpdates] ? 1 : 0;
|
||||
}
|
||||
|
||||
// Enable or disable automatic update checks
|
||||
EXPORT void SparklePlugin_SetAutomaticallyChecksForUpdates(int enabled) {
|
||||
if (updaterController == nil) {
|
||||
NSLog(@"SparklePlugin: Not initialized");
|
||||
return;
|
||||
}
|
||||
[[updaterController updater] setAutomaticallyChecksForUpdates:(enabled != 0)];
|
||||
}
|
||||
Reference in New Issue
Block a user