Prevent update dialogs on app startup in debug mode

- Set startingUpdater to false in DEBUG builds to prevent automatic initialization
- Keep all automatic update checks and downloads disabled in DEBUG mode
- Only start the updater manually in release builds if needed
- This ensures no update-related dialogs appear during development
This commit is contained in:
Peter Steinberger 2025-06-16 06:11:34 +02:00
parent d4fae5631a
commit 6c0b8cb7b3
3 changed files with 104 additions and 56 deletions

View file

@ -26,11 +26,20 @@ public final class SparkleUpdaterManager: NSObject {
} }
// Initialize Sparkle with standard configuration // Initialize Sparkle with standard configuration
#if DEBUG
// In debug mode, don't start the updater automatically
updaterController = SPUStandardUpdaterController(
startingUpdater: false,
updaterDelegate: nil,
userDriverDelegate: nil
)
#else
updaterController = SPUStandardUpdaterController( updaterController = SPUStandardUpdaterController(
startingUpdater: true, startingUpdater: true,
updaterDelegate: nil, updaterDelegate: nil,
userDriverDelegate: nil userDriverDelegate: nil
) )
#endif
// Configure automatic updates // Configure automatic updates
if let updater = updaterController?.updater { if let updater = updaterController?.updater {
@ -50,6 +59,11 @@ public final class SparkleUpdaterManager: NSObject {
updater.updateCheckInterval = 86_400 updater.updateCheckInterval = 86_400
logger.info("Sparkle updater initialized successfully with automatic downloads enabled") logger.info("Sparkle updater initialized successfully with automatic downloads enabled")
// Start the updater if it wasn't started during initialization
if !updaterController!.startedUpdater {
updaterController!.updater.startUpdater()
}
#endif #endif
} }
} }

View file

@ -768,13 +768,11 @@ public final class TunnelServer {
) async { ) async {
let startTime = Date() let startTime = Date()
var headerSent = false var headerSent = false
var tailProcess: Process? var fileMonitor: DispatchSourceFileSystemObject?
defer { defer {
// Ensure tail process is terminated when function exits // Ensure file monitor is cancelled when function exits
if let process = tailProcess, process.isRunning { fileMonitor?.cancel()
process.terminate()
}
} }
// Send existing content first // Send existing content first
@ -831,12 +829,22 @@ public final class TunnelServer {
} }
// Stream new content by monitoring file changes // Stream new content by monitoring file changes
tailProcess = await monitorFileChanges( fileMonitor = await monitorFileChanges(
streamOutPath: streamOutPath, streamOutPath: streamOutPath,
startTime: startTime, startTime: startTime,
continuation: continuation continuation: continuation
) )
// Wait for cancellation
await withTaskCancellationHandler {
await withCheckedContinuation { continuation in
// This will suspend until cancelled
continuation.resume()
}
} onCancel: { [fileMonitor] in
fileMonitor?.cancel()
}
continuation.finish() continuation.finish()
} }
@ -844,61 +852,87 @@ public final class TunnelServer {
streamOutPath: String, streamOutPath: String,
startTime: Date, startTime: Date,
continuation: AsyncStream<ByteBuffer>.Continuation continuation: AsyncStream<ByteBuffer>.Continuation
) async -> Process? { ) async -> DispatchSourceFileSystemObject? {
// Use tail -f to monitor file changes // Open file for reading
let tailProcess = Process() let fileDescriptor = open(streamOutPath, O_RDONLY)
tailProcess.executableURL = URL(fileURLWithPath: "/usr/bin/tail") guard fileDescriptor >= 0 else {
tailProcess.arguments = ["-f", streamOutPath] logger.error("Failed to open file for monitoring: \(streamOutPath)")
return nil
let outputPipe = Pipe()
tailProcess.standardOutput = outputPipe
do {
try tailProcess.run()
// Process output from tail -f
let outputHandle = outputPipe.fileHandleForReading
var buffer = ""
// Read data asynchronously
for try await data in outputHandle.bytes {
if Task.isCancelled {
break
} }
// Accumulate data into buffer // Get initial file size
if let character = String(data: Data([data]), encoding: .utf8) { var lastReadPosition = lseek(fileDescriptor, 0, SEEK_END)
buffer += character
// Process complete lines // Create dispatch source for monitoring file writes
let lines = buffer.components(separatedBy: .newlines) let source = DispatchSource.makeFileSystemObjectSource(
fileDescriptor: fileDescriptor,
eventMask: [.write, .extend],
queue: DispatchQueue.global(qos: .background)
)
// Store buffer for incomplete lines
var lineBuffer = ""
source.setEventHandler { [weak self] in
guard let self = self else { return }
// Get current file size
let currentPosition = lseek(fileDescriptor, 0, SEEK_END)
// Calculate how much new data to read
let bytesToRead = currentPosition - lastReadPosition
guard bytesToRead > 0 else { return }
// Seek to last read position
lseek(fileDescriptor, lastReadPosition, SEEK_SET)
// Read new data
let buffer = UnsafeMutablePointer<CChar>.allocate(capacity: Int(bytesToRead) + 1)
defer { buffer.deallocate() }
let bytesRead = read(fileDescriptor, buffer, Int(bytesToRead))
guard bytesRead > 0 else { return }
// Convert to string (handle potential UTF-8 boundary issues)
let data = Data(bytes: buffer, count: bytesRead)
guard let contentString = String(data: data, encoding: .utf8) else {
// If UTF-8 decoding fails, it might be due to split multi-byte character
// Store the bytes and try again with next chunk
return
}
// Update last read position
lastReadPosition = currentPosition
// Process new content
lineBuffer += contentString
let lines = lineBuffer.components(separatedBy: .newlines)
// Process all complete lines
if lines.count > 1 { if lines.count > 1 {
// Process all complete lines except the last (incomplete) one
for i in 0..<(lines.count - 1) { for i in 0..<(lines.count - 1) {
let line = lines[i] let line = lines[i]
await processNewLine( Task { @MainActor in
await self.processNewLine(
line: line, line: line,
startTime: startTime, startTime: startTime,
continuation: continuation continuation: continuation
) )
} }
}
// Keep the last incomplete line in buffer // Keep the last incomplete line in buffer
buffer = lines.last ?? "" lineBuffer = lines.last ?? ""
}
} }
} }
} catch { source.setCancelHandler {
logger.error("Error starting tail process: \(error)") close(fileDescriptor)
return nil
} }
// Cleanup when cancelled or finished // Start monitoring
if tailProcess.isRunning { source.resume()
tailProcess.terminate()
}
return tailProcess return source
} }
private func processNewLine( private func processNewLine(