From 3a6379b178503cfb2336dfce412dabb8b871d921 Mon Sep 17 00:00:00 2001 From: Autli Date: Tue, 6 Oct 2026 12:17:30 +0500 Subject: [PATCH] Add 1-click GitHub in-app updater and update.sh script --- .gitignore | 2 + Sources/blts_tracker/BLTSTrackerApp.swift | 7 ++ .../Services/UpdaterService.swift | 116 ++++++++++++++++++ build_app.sh | 2 + update.sh | 34 +++++ 5 files changed, 161 insertions(+) create mode 100644 Sources/blts_tracker/Services/UpdaterService.swift create mode 100755 update.sh diff --git a/.gitignore b/.gitignore index 0023a53..9f9d8c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .DS_Store /.build +.module-cache +*.app/ /Packages xcuserdata/ DerivedData/ diff --git a/Sources/blts_tracker/BLTSTrackerApp.swift b/Sources/blts_tracker/BLTSTrackerApp.swift index c9199aa..e53f84e 100644 --- a/Sources/blts_tracker/BLTSTrackerApp.swift +++ b/Sources/blts_tracker/BLTSTrackerApp.swift @@ -6,6 +6,7 @@ import CoreAudio @main struct BLTSTrackerApp: App { @ObservedObject var tracker = BluetoothTracker.shared + @ObservedObject var updater = UpdaterService.shared var body: some Scene { MenuBarExtra { @@ -57,6 +58,12 @@ struct BLTSTrackerApp: App { } .keyboardShortcut("r") + Button(updater.isUpdating ? "Обновление..." : "🔄 Обновить из GitHub") { + updater.checkForUpdatesAndApply() + } + .disabled(updater.isUpdating) + .keyboardShortcut("u") + Divider() Button("Завершить") { diff --git a/Sources/blts_tracker/Services/UpdaterService.swift b/Sources/blts_tracker/Services/UpdaterService.swift new file mode 100644 index 0000000..9c4d4f3 --- /dev/null +++ b/Sources/blts_tracker/Services/UpdaterService.swift @@ -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) + } +} diff --git a/build_app.sh b/build_app.sh index 08bf3e0..4bbd995 100755 --- a/build_app.sh +++ b/build_app.sh @@ -21,8 +21,10 @@ swiftc -parse-as-library \ -framework IOKit \ -framework CoreAudio \ -framework AudioToolbox \ + -framework UserNotifications \ "$DIR/Sources/blts_tracker/Models/BluetoothDeviceModel.swift" \ "$DIR/Sources/blts_tracker/Services/BluetoothTracker.swift" \ + "$DIR/Sources/blts_tracker/Services/UpdaterService.swift" \ "$DIR/Sources/blts_tracker/BLTSTrackerApp.swift" \ -o "$MACOS_DIR/blts_tracker" diff --git a/update.sh b/update.sh new file mode 100755 index 0000000..8d9935b --- /dev/null +++ b/update.sh @@ -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!"