From 5079fc78d30673e5147fbe453d77b81e898db353 Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Fri, 12 Sep 2025 21:16:50 -0400 Subject: [PATCH] Removed ArchUpdateService --- Services/ArchUpdaterService.qml | 704 -------------------------------- 1 file changed, 704 deletions(-) delete mode 100644 Services/ArchUpdaterService.qml diff --git a/Services/ArchUpdaterService.qml b/Services/ArchUpdaterService.qml deleted file mode 100644 index ea1d411..0000000 --- a/Services/ArchUpdaterService.qml +++ /dev/null @@ -1,704 +0,0 @@ -pragma Singleton - -import QtQuick -import Quickshell -import Quickshell.Io -import qs.Commons - -Singleton { - id: updateService - - // ============================================================================ - // CORE PROPERTIES - // ============================================================================ - - // Package data - property var repoPackages: [] - property var aurPackages: [] - property var selectedPackages: [] - property int selectedPackagesCount: 0 - property string allUpdatesOutput: "" - - // Update state - property bool updateInProgress: false - property bool updateFailed: false - property string lastUpdateError: "" - property bool checkFailed: false - property string lastCheckError: "" - - // Monitoring state - property string capturedErrorText: "" - property string capturedSuccessText: "" - - // Computed properties - readonly property bool aurBusy: checkAurUpdatesProcess.running || checkAurOnlyProcess.running - readonly property int updates: repoPackages.length - readonly property int aurUpdates: aurPackages.length - readonly property int totalUpdates: updates + aurUpdates - - // Terminal validation - readonly property bool terminalAvailable: Quickshell.env("TERMINAL") !== "" - readonly property string terminalError: "TERMINAL environment variable not set" - - // AUR helper validation - readonly property bool aurHelperAvailable: cachedAurHelper !== "" - readonly property string aurHelperError: "No AUR helper found (yay or paru not installed)" - - // Polling cooldown (prevent excessive polling) - property int lastPollTime: 0 - readonly property int pollCooldownMs: 5 * 60 * 1000 // 5 minutes - readonly property bool canPoll: (Date.now() - lastPollTime) > pollCooldownMs - - // ============================================================================ - // TIMERS - // ============================================================================ - - // Refresh timer for post-update polling - Timer { - id: refreshTimer - interval: 5000 - repeat: false - onTriggered: { - doPoll() - } - } - - // Timer to mark update as complete - with error handling - Timer { - id: updateCompleteTimer - interval: 30000 // Increased to 30 seconds to allow more time - repeat: false - onTriggered: { - checkForUpdateFailures() - } - } - - // Timer to check if update processes are still running - Timer { - id: updateMonitorTimer - interval: 2000 - repeat: true - running: updateInProgress - onTriggered: { - // Check if any update-related processes might still be running - checkUpdateStatus() - } - } - - // ============================================================================ - // MONITORING PROCESSES - // ============================================================================ - - // Process to monitor update completion - Process { - id: updateStatusProcess - command: ["pgrep", "-f", "(yay|paru).*(-S|-Syu)"] - onExited: function (exitCode) { - if (exitCode !== 0 && updateInProgress) { - // No update processes found, update likely completed - updateInProgress = false - updateMonitorTimer.stop() - errorCheckTimer.stop() - successCheckTimer.stop() - - // Don't stop the complete timer - let it handle failures - // If the update actually failed, the timer will trigger and set updateFailed = true - - // Refresh package lists after a short delay - Qt.callLater(() => { - doPoll() - }, 2000) - } - } - } - - // Process to check for errors in log file (only when update is in progress) - Process { - id: errorCheckProcess - command: ["sh", "-c", "if [ -f /tmp/archupdater_output.log ]; then grep -i 'failed to build\\|could not resolve\\|unable to satisfy\\|failed to install\\|failed to upgrade\\|error:' /tmp/archupdater_output.log | grep -v 'ERROR_DETECTED' | tail -1; fi"] - onExited: function (exitCode) { - if (exitCode === 0 && updateInProgress && capturedErrorText.trim() !== "") { - // Error found in log - updateInProgress = false - updateFailed = true - updateCompleteTimer.stop() - updateMonitorTimer.stop() - errorCheckTimer.stop() - successCheckTimer.stop() - lastUpdateError = "Build or update error detected" - - // Refresh to check actual state - Qt.callLater(() => { - doPoll() - }, 1000) - } - } - stdout: StdioCollector { - onStreamFinished: { - capturedErrorText = text || "" - } - } - } - - // Process to check for successful completion - Process { - id: successCheckProcess - command: ["sh", "-c", "if [ -f /tmp/archupdater_output.log ]; then grep -i 'Update complete!\\|:: Running post-transaction hooks\\|:: Processing package changes\\|upgrading.*\\.\\.\\.\\|installing.*\\.\\.\\.\\|removing.*\\.\\.\\.' /tmp/archupdater_output.log | tail -1; fi"] - onExited: function (exitCode) { - if (exitCode === 0 && updateInProgress && capturedSuccessText.trim() !== "") { - // Success indicators found - updateInProgress = false - updateFailed = false - updateCompleteTimer.stop() - updateMonitorTimer.stop() - errorCheckTimer.stop() - successCheckTimer.stop() - lastUpdateError = "" - - // Refresh to check actual state - Qt.callLater(() => { - doPoll() - }, 1000) - } - } - stdout: StdioCollector { - onStreamFinished: { - capturedSuccessText = text || "" - } - } - } - - // Timer to check for success more frequently when update is in progress - Timer { - id: successCheckTimer - interval: 5000 // Check every 5 seconds - repeat: true - running: updateInProgress - onTriggered: { - if (updateInProgress && !successCheckProcess.running) { - successCheckProcess.running = true - } - } - } - - // Timer to check for errors more frequently when update is in progress - Timer { - id: errorCheckTimer - interval: 5000 // Check every 5 seconds - repeat: true - running: updateInProgress - onTriggered: { - if (updateInProgress && !errorCheckProcess.running) { - errorCheckProcess.running = true - } - } - } - - // ============================================================================ - // MONITORING FUNCTIONS - // ============================================================================ - function checkUpdateStatus() { - if (updateInProgress && !updateStatusProcess.running) { - updateStatusProcess.running = true - } - } - - function checkForUpdateFailures() { - updateInProgress = false - updateFailed = true - updateCompleteTimer.stop() - updateMonitorTimer.stop() - - // Refresh to check actual state after a delay - Qt.callLater(() => { - doPoll() - }, 2000) - } - - // Initial check - Component.onCompleted: { - // Start AUR helper detection - getAurHelper() - - // Set up a fallback timer in case detection takes too long - Qt.callLater(() => { - if (cachedAurHelper === "" && !yayCheckProcess.running && !paruCheckProcess.running) { - // No AUR helper found after reasonable time and processes have finished - checkFailed = true - lastCheckError = "No AUR helper found (yay or paru not installed)" - Logger.warn("ArchUpdater", "No AUR helper found (yay or paru)") - } - }, 5000) // 5 second fallback - } - - // ============================================================================ - // PACKAGE CHECKING PROCESSES - // ============================================================================ - - // Process for checking all updates with AUR helper (repo + AUR) - Process { - id: checkAurUpdatesProcess - command: [] - onExited: function (exitCode) { - // For both yay and paru: exit code 0 = updates available, exit code 1 = no updates - if (exitCode !== 0 && exitCode !== 1) { - Logger.warn("ArchUpdater", "AUR helper check failed (code:", exitCode, ")") - checkFailed = true - lastCheckError = "Failed to check for updates (exit code: " + exitCode + ")" - aurPackages = [] - repoPackages = [] - } - // Don't clear checkFailed here - wait for the second process to complete - } - stdout: StdioCollector { - onStreamFinished: { - allUpdatesOutput = text - // Now get AUR-only updates to compare - checkAurOnlyProcess.running = true - } - } - } - - // Process for checking AUR-only updates (to separate from repo updates) - Process { - id: checkAurOnlyProcess - command: [] - onExited: function (exitCode) { - // For both yay and paru: exit code 0 = updates available, exit code 1 = no updates - if (exitCode !== 0 && exitCode !== 1) { - Logger.warn("ArchUpdater", "AUR helper AUR-only check failed (code:", exitCode, ")") - checkFailed = true - lastCheckError = "Failed to check AUR updates (exit code: " + exitCode + ")" - aurPackages = [] - repoPackages = [] - } else { - // Only clear checkFailed if both processes succeeded - // Check if the first process also succeeded (no error was set) - if (!checkFailed) { - checkFailed = false - lastCheckError = "" - } - } - } - stdout: StdioCollector { - onStreamFinished: { - parseAllUpdatesOutput(allUpdatesOutput, text) - Logger.log("ArchUpdater", "found", repoPackages.length, "repo package(s) and", aurPackages.length, "AUR package(s) to upgrade") - } - } - } - - // ============================================================================ - // PARSING FUNCTIONS - // ============================================================================ - - // Generic package parsing function - function parsePackageOutput(output, source) { - const lines = output.trim().split('\n').filter(line => line.trim()) - const packages = [] - - for (const line of lines) { - const m = line.match(/^(\S+)\s+([^\s]+)\s+->\s+([^\s]+)$/) - if (m) { - packages.push({ - "name": m[1], - "oldVersion": m[2], - "newVersion": m[3], - "description": `${m[1]} ${m[2]} -> ${m[3]}`, - "source": source - }) - } - } - - // Only update if we have new data or if this is a fresh check - if (packages.length > 0 || output.trim() === "") { - if (source === "repo") { - repoPackages = packages - } else { - aurPackages = packages - } - } - } - - // Parse all updates output (repo + AUR packages) - function parseAllUpdatesOutput(allOutput, aurOnlyOutput) { - const allLines = allOutput.trim().split('\n').filter(line => line.trim()) - const aurOnlyLines = aurOnlyOutput.trim().split('\n').filter(line => line.trim()) - - // Create a set of AUR package names for quick lookup - const aurPackageNames = new Set() - for (const line of aurOnlyLines) { - const m = line.match(/^(\S+)\s+([^\s]+)\s+->\s+([^\s]+)$/) - if (m) { - aurPackageNames.add(m[1]) - } - } - - const repoPackages = [] - const aurPackages = [] - - for (const line of allLines) { - const m = line.match(/^(\S+)\s+([^\s]+)\s+->\s+([^\s]+)$/) - if (m) { - const packageInfo = { - "name": m[1], - "oldVersion": m[2], - "newVersion": m[3], - "description": `${m[1]} ${m[2]} -> ${m[3]}` - } - - // Check if this package is in the AUR-only list - if (aurPackageNames.has(m[1])) { - packageInfo.source = "aur" - aurPackages.push(packageInfo) - } else { - packageInfo.source = "repo" - repoPackages.push(packageInfo) - } - } - } - - // Update the package lists - if (repoPackages.length > 0 || aurPackages.length > 0 || allOutput.trim() === "") { - updateService.repoPackages = repoPackages - updateService.aurPackages = aurPackages - } - } - - function doPoll() { - // Prevent excessive polling - if (aurBusy || !canPoll) { - return - } - - // Check if we have a cached AUR helper - if (cachedAurHelper !== "") { - // Clear error state when helper is available - if (checkFailed && lastCheckError.includes("No AUR helper found")) { - checkFailed = false - lastCheckError = "" - } - - checkAurUpdatesProcess.command = [cachedAurHelper, "-Qu"] - checkAurOnlyProcess.command = [cachedAurHelper, getAurOnlyFlag()] - - // Start AUR updates check (includes both repo and AUR packages) - checkAurUpdatesProcess.running = true - lastPollTime = Date.now() - } else { - // AUR helper detection is still in progress or failed - // Try to detect again if not already in progress - if (!yayCheckProcess.running && !paruCheckProcess.running) { - getAurHelper() - } - Logger.warn("ArchUpdater", "AUR helper detection in progress or failed") - } - } - - // ============================================================================ - // UPDATE FUNCTIONS - // ============================================================================ - - // Helper function to generate update command with error detection - function generateUpdateCommand(baseCommand) { - return baseCommand + " 2>&1 | tee /tmp/archupdater_output.log; if [ $? -ne 0 ]; then echo 'ERROR_DETECTED'; fi; echo 'Update complete! Press Enter to close...'; read -p 'Press Enter to continue...'" - } - - // Update all packages (repo + AUR) - function runUpdate() { - if (totalUpdates === 0) { - doPoll() - return - } - - // Reset any previous error states - updateFailed = false - lastUpdateError = "" - updateInProgress = true - capturedErrorText = "" - capturedSuccessText = "" - - const terminal = Quickshell.env("TERMINAL") - if (!terminal) { - updateInProgress = false - updateFailed = true - lastUpdateError = "TERMINAL environment variable not set" - ToastService.showWarning("ArchUpdater", "TERMINAL environment variable not set") - return - } - - // Check if we have an AUR helper for full system update - if (cachedAurHelper !== "" && (aurUpdates > 0 || updates > 0)) { - // Use AUR helper for full system update (handles both repo and AUR) - const command = generateUpdateCommand(cachedAurHelper + " -Syu") - Quickshell.execDetached([terminal, "-e", "bash", "-c", command]) - } else if (cachedAurHelper === "") { - // No AUR helper found - updateInProgress = false - updateFailed = true - lastUpdateError = "No AUR helper found (yay or paru not installed)" - Logger.warn("ArchUpdater", "No AUR helper found for update") - } - - // Start monitoring and timeout timers - refreshTimer.start() - updateCompleteTimer.start() - updateMonitorTimer.start() - } - - // Update selected packages - function runSelectiveUpdate() { - if (selectedPackages.length === 0) - return - - // Reset any previous error states - updateFailed = false - lastUpdateError = "" - updateInProgress = true - capturedErrorText = "" - capturedSuccessText = "" - - const terminal = Quickshell.env("TERMINAL") - if (!terminal) { - updateInProgress = false - updateFailed = true - lastUpdateError = "TERMINAL environment variable not set" - ToastService.showWarning("ArchUpdater", "TERMINAL environment variable not set") - return - } - - // Update all packages with AUR helper (handles both repo and AUR) - if (selectedPackages.length > 0) { - if (cachedAurHelper !== "") { - const packageList = selectedPackages.join(" ") - - // Handle ghostty terminal differently due to command parsing issues - if (terminal.includes("ghostty")) { - const simpleCommand = cachedAurHelper + " -S " + packageList - Quickshell.execDetached([terminal, "-e", simpleCommand]) - } else { - const command = generateUpdateCommand(cachedAurHelper + " -S " + packageList) - Quickshell.execDetached([terminal, "-e", "bash", "-c", command]) - } - } else { - updateInProgress = false - updateFailed = true - lastUpdateError = "No AUR helper found (yay or paru not installed)" - Logger.warn("ArchUpdater", "No AUR helper found for packages:", selectedPackages.join(", ")) - } - } - - // Start monitoring and timeout timers - refreshTimer.start() - updateCompleteTimer.start() - updateMonitorTimer.start() - } - - // Reset update state (useful for manual recovery) - function resetUpdateState() { - // Clear all update states - updateInProgress = false - updateFailed = false - lastUpdateError = "" - checkFailed = false - lastCheckError = "" - updateCompleteTimer.stop() - updateMonitorTimer.stop() - refreshTimer.stop() - errorCheckTimer.stop() - successCheckTimer.stop() - - // Refresh to get current state - doPoll() - } - - // Manual refresh function (bypasses cooldown) - function forceRefresh() { - // Prevent multiple simultaneous refreshes - if (aurBusy) { - return - } - - // Clear error states when refreshing - updateFailed = false - lastUpdateError = "" - checkFailed = false - lastCheckError = "" - - // Check if we have a cached AUR helper - if (cachedAurHelper !== "") { - // Clear error state when helper is available - if (checkFailed && lastCheckError.includes("No AUR helper found")) { - checkFailed = false - lastCheckError = "" - } - - checkAurUpdatesProcess.command = [cachedAurHelper, "-Qu"] - checkAurOnlyProcess.command = [cachedAurHelper, getAurOnlyFlag()] - - // Force refresh by bypassing cooldown - checkAurUpdatesProcess.running = true - lastPollTime = Date.now() - } else { - // AUR helper detection is still in progress or failed - // Try to detect again if not already in progress - if (!yayCheckProcess.running && !paruCheckProcess.running) { - getAurHelper() - } - Logger.warn("ArchUpdater", "AUR helper detection in progress or failed") - } - } - - // ============================================================================ - // UTILITY PROCESSES - // ============================================================================ - - // Process for checking yay availability - Process { - id: yayCheckProcess - command: ["which", "yay"] - onExited: function (exitCode) { - if (exitCode === 0) { - cachedAurHelper = "yay" - Logger.log("ArchUpdater", "Found yay AUR helper (preferred)") - // Clear error state when helper is found - if (checkFailed && lastCheckError.includes("No AUR helper found")) { - checkFailed = false - lastCheckError = "" - } - // Trigger initial check when helper is found - triggerInitialCheck() - } - } - } - - // Process for checking paru availability - Process { - id: paruCheckProcess - command: ["which", "paru"] - onExited: function (exitCode) { - if (exitCode === 0) { - // Only use paru if yay wasn't found (yay is preferred) - if (cachedAurHelper === "") { - cachedAurHelper = "paru" - Logger.log("ArchUpdater", "Found paru AUR helper") - // Clear error state when helper is found - if (checkFailed && lastCheckError.includes("No AUR helper found")) { - checkFailed = false - lastCheckError = "" - } - // Trigger initial check when helper is found - triggerInitialCheck() - } else { - Logger.log("ArchUpdater", "Found paru but using", cachedAurHelper, "(preferred)") - } - } - } - } - - // Cached AUR helper detection - property string cachedAurHelper: "" - - // Helper function to detect AUR helper - function getAurHelper() { - // Return cached result if available - if (cachedAurHelper !== "") { - return cachedAurHelper - } - - // Check for AUR helpers using Process objects - Logger.log("ArchUpdater", "Detecting AUR helper...") - - // Start the detection processes - yayCheckProcess.running = true - paruCheckProcess.running = true - - // Return empty string to indicate no helper found yet - // The processes will update cachedAurHelper when they complete - return "" - } - - // Helper function to get the correct AUR-only flag for the detected helper - function getAurOnlyFlag() { - if (cachedAurHelper === "yay") { - return "-Qua" - } else if (cachedAurHelper === "paru") { - return "-Qua" // paru uses the same flag but different exit code behavior - } - return "-Qua" // fallback - } - - // Helper function to trigger the initial package check - function triggerInitialCheck() { - // Only trigger if this is the first time (no packages have been checked yet) - if (repoPackages.length === 0 && aurPackages.length === 0 && !aurBusy) { - // Clear any previous error state - checkFailed = false - lastCheckError = "" - - // Wait a bit for the system to be ready before the first check - Qt.callLater(() => { - checkAurUpdatesProcess.command = [cachedAurHelper, "-Qu"] - checkAurOnlyProcess.command = [cachedAurHelper, getAurOnlyFlag()] - checkAurUpdatesProcess.running = true - lastPollTime = Date.now() - }, 1000) - } - } - - // ============================================================================ - // PACKAGE SELECTION FUNCTIONS - // ============================================================================ - function togglePackageSelection(packageName) { - const index = selectedPackages.indexOf(packageName) - if (index > -1) { - selectedPackages.splice(index, 1) - } else { - selectedPackages.push(packageName) - } - selectedPackagesCount = selectedPackages.length - } - - function selectAllPackages() { - selectedPackages = [...repoPackages.map(pkg => pkg.name), ...aurPackages.map(pkg => pkg.name)] - selectedPackagesCount = selectedPackages.length - } - - function deselectAllPackages() { - selectedPackages = [] - selectedPackagesCount = 0 - } - - function isPackageSelected(packageName) { - return selectedPackages.indexOf(packageName) > -1 - } - - // ============================================================================ - // REFRESH FUNCTIONS - // ============================================================================ - - // ============================================================================ - // UTILITY FUNCTIONS - // ============================================================================ - - // Notification helper - function notify(title, body) { - Quickshell.execDetached(["notify-send", "-a", "UpdateService", "-i", "system-software-update", title, body]) - } - - // ============================================================================ - // AUTO-POLL TIMER - // ============================================================================ - - // Auto-poll every 15 minutes (respects cooldown) - Timer { - interval: 15 * 60 * 1000 // 15 minutes - repeat: true - running: true - onTriggered: { - if (!updateInProgress && canPoll) { - doPoll() - } - } - } -}