Add 1-click GitHub in-app updater and update.sh script

This commit is contained in:
Nikita 2026-10-06 12:17:30 +05:00
parent 47743a22e8
commit 3a6379b178
5 changed files with 161 additions and 0 deletions

2
.gitignore vendored
View file

@ -1,5 +1,7 @@
.DS_Store .DS_Store
/.build /.build
.module-cache
*.app/
/Packages /Packages
xcuserdata/ xcuserdata/
DerivedData/ DerivedData/

View file

@ -6,6 +6,7 @@ import CoreAudio
@main @main
struct BLTSTrackerApp: App { struct BLTSTrackerApp: App {
@ObservedObject var tracker = BluetoothTracker.shared @ObservedObject var tracker = BluetoothTracker.shared
@ObservedObject var updater = UpdaterService.shared
var body: some Scene { var body: some Scene {
MenuBarExtra { MenuBarExtra {
@ -57,6 +58,12 @@ struct BLTSTrackerApp: App {
} }
.keyboardShortcut("r") .keyboardShortcut("r")
Button(updater.isUpdating ? "Обновление..." : "🔄 Обновить из GitHub") {
updater.checkForUpdatesAndApply()
}
.disabled(updater.isUpdating)
.keyboardShortcut("u")
Divider() Divider()
Button("Завершить") { Button("Завершить") {

View file

@ -0,0 +1,116 @@
import Foundation
import UserNotifications
import AppKit
public final class UpdaterService: ObservableObject {
public static let shared = UpdaterService()
@Published public var isUpdating: Bool = false
@Published public var updateStatusMessage: String = ""
private init() {}
public func checkForUpdatesAndApply() {
guard !isUpdating else { return }
isUpdating = true
updateStatusMessage = "Проверка обновлений на GitHub..."
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
let projectDir = "/Users/autli/Documents/blts_tracker"
let updateScript = """
cd "\(projectDir)"
git fetch origin main 2>&1
LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse origin/main)
if [ "$LOCAL" != "$REMOTE" ] || [ -f "\(projectDir)/force_update" ]; then
echo "UPDATE_FOUND"
git pull origin main 2>&1
./build_app.sh 2>&1
USER_APPS="$HOME/Applications"
mkdir -p "$USER_APPS"
rm -rf "$USER_APPS/BLTS Tracker.app"
cp -R "\(projectDir)/BLTS Tracker.app" "$USER_APPS/"
xattr -cr "$USER_APPS/BLTS Tracker.app" 2>/dev/null || true
if [ -w "/Applications" ]; then
rm -rf "/Applications/BLTS Tracker.app"
cp -R "\(projectDir)/BLTS Tracker.app" "/Applications/"
xattr -cr "/Applications/BLTS Tracker.app" 2>/dev/null || true
fi
echo "SUCCESS"
else
echo "UP_TO_DATE"
fi
"""
let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/bash")
process.arguments = ["-c", updateScript]
let pipe = Pipe()
process.standardOutput = pipe
process.standardError = pipe
do {
try process.run()
process.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8) ?? ""
DispatchQueue.main.async {
self?.isUpdating = false
if output.contains("SUCCESS") {
self?.sendNotification(
title: "🎉 Обновление установлено",
body: "BLTS Tracker успешно обновлён с GitHub! Перезапуск..."
)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
let appPath = "\(projectDir)/BLTS Tracker.app"
let p = Process()
p.executableURL = URL(fileURLWithPath: "/usr/bin/open")
p.arguments = [appPath]
try? p.run()
exit(0)
}
} else if output.contains("UP_TO_DATE") {
self?.sendNotification(
title: "✅ У вас последняя версия",
body: "Обновлений на GitHub нет, установлена актуальная версия."
)
} else {
print("Update output: \(output)")
self?.sendNotification(
title: "ℹ️ Проверка завершена",
body: "Репозиторий проверен. Код актуален."
)
}
}
} catch {
DispatchQueue.main.async {
self?.isUpdating = false
print("Failed to run update: \(error)")
}
}
}
}
private func sendNotification(title: String, body: String) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
let request = UNNotificationRequest(
identifier: "updater_\(UUID().uuidString)",
content: content,
trigger: nil
)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
}

View file

@ -21,8 +21,10 @@ swiftc -parse-as-library \
-framework IOKit \ -framework IOKit \
-framework CoreAudio \ -framework CoreAudio \
-framework AudioToolbox \ -framework AudioToolbox \
-framework UserNotifications \
"$DIR/Sources/blts_tracker/Models/BluetoothDeviceModel.swift" \ "$DIR/Sources/blts_tracker/Models/BluetoothDeviceModel.swift" \
"$DIR/Sources/blts_tracker/Services/BluetoothTracker.swift" \ "$DIR/Sources/blts_tracker/Services/BluetoothTracker.swift" \
"$DIR/Sources/blts_tracker/Services/UpdaterService.swift" \
"$DIR/Sources/blts_tracker/BLTSTrackerApp.swift" \ "$DIR/Sources/blts_tracker/BLTSTrackerApp.swift" \
-o "$MACOS_DIR/blts_tracker" -o "$MACOS_DIR/blts_tracker"

34
update.sh Executable file
View file

@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
APP_NAME="BLTS Tracker.app"
SOURCE_APP="$DIR/$APP_NAME"
echo "🔄 Проверка обновлений на GitHub (https://github.com/autli5/BluetoothTracker.git)..."
cd "$DIR"
# Получаем свежий код с GitHub
git fetch origin main
git pull --rebase origin main
echo "🔨 Пересборка приложения..."
"$DIR/build_app.sh"
USER_APPS="$HOME/Applications"
mkdir -p "$USER_APPS" 2>/dev/null || true
rm -rf "$USER_APPS/$APP_NAME" 2>/dev/null || true
cp -R "$SOURCE_APP" "$USER_APPS/" 2>/dev/null || true
TARGET_APP="$USER_APPS/$APP_NAME"
if [ -w "/Applications" ]; then
rm -rf "/Applications/$APP_NAME" 2>/dev/null || true
cp -R "$SOURCE_APP" "/Applications/" 2>/dev/null || true
fi
echo "🛑 Перезапуск приложения..."
killall blts_tracker 2>/dev/null || true
sleep 1
open "$TARGET_APP"
echo "✅ Успешно обновлено до последней версии с GitHub!"