Fix ArchUpdater to be able to use ghostty

ArchUpdaterService: add separate ghostty command
ArchUpdater: Change color of symbol if no terminal/aur helper is found,
edited tooltip
ArchUpdaterPanel: Add proper error message if TERMINAL or aur helper
was not found
This commit is contained in:
Ly-sec 2025-08-31 16:41:52 +02:00
parent 3f0374e1f2
commit 1ecc3d9744
3 changed files with 139 additions and 131 deletions

View file

@ -66,11 +66,12 @@ NPanel {
Layout.fillWidth: true Layout.fillWidth: true
} }
// Update summary (only show when packages are available) // Update summary (only show when packages are available and terminal is configured)
NText { NText {
visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed
&& !ArchUpdaterService.checkFailed && !ArchUpdaterService.aurBusy && !ArchUpdaterService.checkFailed && !ArchUpdaterService.aurBusy
&& ArchUpdaterService.totalUpdates > 0 && ArchUpdaterService.totalUpdates > 0 && ArchUpdaterService.terminalAvailable
&& ArchUpdaterService.aurHelperAvailable
text: ArchUpdaterService.totalUpdates + " package" + (ArchUpdaterService.totalUpdates !== 1 ? "s" : "") + " can be updated" text: ArchUpdaterService.totalUpdates + " package" + (ArchUpdaterService.totalUpdates !== 1 ? "s" : "") + " can be updated"
font.pointSize: Style.fontSizeL * scaling font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightMedium font.weight: Style.fontWeightMedium
@ -78,11 +79,12 @@ NPanel {
Layout.fillWidth: true Layout.fillWidth: true
} }
// Package selection info (only show when not updating and have packages) // Package selection info (only show when not updating and have packages and terminal is configured)
NText { NText {
visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed
&& !ArchUpdaterService.checkFailed && !ArchUpdaterService.aurBusy && !ArchUpdaterService.checkFailed && !ArchUpdaterService.aurBusy
&& ArchUpdaterService.totalUpdates > 0 && ArchUpdaterService.totalUpdates > 0 && ArchUpdaterService.terminalAvailable
&& ArchUpdaterService.aurHelperAvailable
text: ArchUpdaterService.selectedPackagesCount + " of " + ArchUpdaterService.totalUpdates + " packages selected" text: ArchUpdaterService.selectedPackagesCount + " of " + ArchUpdaterService.totalUpdates + " packages selected"
font.pointSize: Style.fontSizeS * scaling font.pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant color: Color.mOnSurfaceVariant
@ -129,12 +131,88 @@ NPanel {
} // Spacer } // Spacer
} }
// Terminal not available state
Item {
Layout.fillWidth: true
Layout.fillHeight: true
visible: !ArchUpdaterService.terminalAvailable && !ArchUpdaterService.updateInProgress
&& !ArchUpdaterService.updateFailed
ColumnLayout {
anchors.centerIn: parent
spacing: Style.marginM * scaling
NIcon {
text: "terminal"
font.pointSize: Style.fontSizeXXXL * scaling
color: Color.mError
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "Terminal not configured"
font.pointSize: Style.fontSizeL * scaling
color: Color.mOnSurface
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "The TERMINAL environment variable is not set. Please set it to your preferred terminal (e.g., kitty, alacritty, foot) in your shell configuration."
font.pointSize: Style.fontSizeNormal * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
Layout.maximumWidth: 280 * scaling
}
}
}
// AUR helper not available state
Item {
Layout.fillWidth: true
Layout.fillHeight: true
visible: ArchUpdaterService.terminalAvailable && !ArchUpdaterService.aurHelperAvailable
&& !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed
&& !ArchUpdaterService.checkFailed && !ArchUpdaterService.aurBusy
ColumnLayout {
anchors.centerIn: parent
spacing: Style.marginM * scaling
NIcon {
text: "package"
font.pointSize: Style.fontSizeXXXL * scaling
color: Color.mError
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "AUR helper not found"
font.pointSize: Style.fontSizeL * scaling
color: Color.mOnSurface
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "No AUR helper (yay or paru) is installed. Please install either yay or paru to manage AUR packages. yay is recommended."
font.pointSize: Style.fontSizeNormal * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
Layout.maximumWidth: 280 * scaling
}
}
}
// Check failed state (AUR down, network issues, etc.) // Check failed state (AUR down, network issues, etc.)
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
visible: ArchUpdaterService.checkFailed && !ArchUpdaterService.updateInProgress visible: ArchUpdaterService.checkFailed && !ArchUpdaterService.updateInProgress
&& !ArchUpdaterService.updateFailed && !ArchUpdaterService.updateFailed && ArchUpdaterService.terminalAvailable
&& ArchUpdaterService.aurHelperAvailable
ColumnLayout { ColumnLayout {
anchors.centerIn: parent anchors.centerIn: parent
@ -237,7 +315,8 @@ NPanel {
Layout.fillHeight: true Layout.fillHeight: true
visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed
&& !ArchUpdaterService.checkFailed && !ArchUpdaterService.aurBusy && !ArchUpdaterService.checkFailed && !ArchUpdaterService.aurBusy
&& ArchUpdaterService.totalUpdates === 0 && ArchUpdaterService.totalUpdates === 0 && ArchUpdaterService.terminalAvailable
&& ArchUpdaterService.aurHelperAvailable
ColumnLayout { ColumnLayout {
anchors.centerIn: parent anchors.centerIn: parent
@ -274,6 +353,7 @@ NPanel {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
visible: ArchUpdaterService.aurBusy && !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed visible: ArchUpdaterService.aurBusy && !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed
&& ArchUpdaterService.terminalAvailable && ArchUpdaterService.aurHelperAvailable
ColumnLayout { ColumnLayout {
anchors.centerIn: parent anchors.centerIn: parent
@ -308,7 +388,8 @@ NPanel {
NBox { NBox {
visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed
&& !ArchUpdaterService.checkFailed && !ArchUpdaterService.aurBusy && !ArchUpdaterService.checkFailed && !ArchUpdaterService.aurBusy
&& ArchUpdaterService.totalUpdates > 0 && ArchUpdaterService.totalUpdates > 0 && ArchUpdaterService.terminalAvailable
&& ArchUpdaterService.aurHelperAvailable
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
@ -396,7 +477,8 @@ NPanel {
// Action buttons (only show when not updating) // Action buttons (only show when not updating)
RowLayout { RowLayout {
visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed visible: !ArchUpdaterService.updateInProgress && !ArchUpdaterService.updateFailed
&& !ArchUpdaterService.checkFailed && !ArchUpdaterService.checkFailed && ArchUpdaterService.terminalAvailable
&& ArchUpdaterService.aurHelperAvailable
Layout.fillWidth: true Layout.fillWidth: true
spacing: Style.marginL * scaling spacing: Style.marginL * scaling

View file

@ -16,10 +16,21 @@ NIconButton {
colorBg: Color.mSurfaceVariant colorBg: Color.mSurfaceVariant
colorBorder: Color.transparent colorBorder: Color.transparent
colorBorderHover: Color.transparent colorBorderHover: Color.transparent
colorFg: (ArchUpdaterService.totalUpdates === 0) ? Color.mOnSurface : Color.mPrimary colorFg: {
if (!ArchUpdaterService.terminalAvailable || !ArchUpdaterService.aurHelperAvailable) {
return Color.mError
}
return (ArchUpdaterService.totalUpdates === 0) ? Color.mOnSurface : Color.mPrimary
}
// Icon states // Icon states
icon: { icon: {
if (!ArchUpdaterService.terminalAvailable) {
return "terminal"
}
if (!ArchUpdaterService.aurHelperAvailable) {
return "package"
}
if (ArchUpdaterService.aurBusy) { if (ArchUpdaterService.aurBusy) {
return "sync" return "sync"
} }
@ -31,6 +42,12 @@ NIconButton {
// Tooltip with repo vs AUR breakdown and sample lists // Tooltip with repo vs AUR breakdown and sample lists
tooltipText: { tooltipText: {
if (!ArchUpdaterService.terminalAvailable) {
return "Terminal not configured\nSet TERMINAL environment variable"
}
if (!ArchUpdaterService.aurHelperAvailable) {
return "AUR helper not found\nInstall yay or paru"
}
if (ArchUpdaterService.aurBusy) { if (ArchUpdaterService.aurBusy) {
return "Checking for updates…" return "Checking for updates…"
} }

View file

@ -32,6 +32,14 @@ Singleton {
readonly property int aurUpdates: aurPackages.length readonly property int aurUpdates: aurPackages.length
readonly property int totalUpdates: updates + aurUpdates 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) // Polling cooldown (prevent excessive polling)
property int lastPollTime: 0 property int lastPollTime: 0
readonly property int pollCooldownMs: 5 * 60 * 1000 // 5 minutes readonly property int pollCooldownMs: 5 * 60 * 1000 // 5 minutes
@ -360,16 +368,9 @@ Singleton {
function doPoll() { function doPoll() {
// Prevent excessive polling // Prevent excessive polling
if (aurBusy || !canPoll) { if (aurBusy || !canPoll) {
if (aurBusy) {
Logger.log("ArchUpdater", "Poll skipped - already checking for updates")
} else {
Logger.log("ArchUpdater", "Poll skipped - cooldown period active")
}
return return
} }
Logger.log("ArchUpdater", "Automatic poll triggered")
// Check if we have a cached AUR helper // Check if we have a cached AUR helper
if (cachedAurHelper !== "") { if (cachedAurHelper !== "") {
// Clear error state when helper is available // Clear error state when helper is available
@ -381,9 +382,6 @@ Singleton {
checkAurUpdatesProcess.command = [cachedAurHelper, "-Qu"] checkAurUpdatesProcess.command = [cachedAurHelper, "-Qu"]
checkAurOnlyProcess.command = [cachedAurHelper, getAurOnlyFlag()] checkAurOnlyProcess.command = [cachedAurHelper, getAurOnlyFlag()]
Logger.log("ArchUpdater", "Poll commands:", cachedAurHelper + " -Qu", "and",
cachedAurHelper + " " + getAurOnlyFlag())
// Start AUR updates check (includes both repo and AUR packages) // Start AUR updates check (includes both repo and AUR packages)
checkAurUpdatesProcess.running = true checkAurUpdatesProcess.running = true
lastPollTime = Date.now() lastPollTime = Date.now()
@ -391,7 +389,6 @@ Singleton {
// AUR helper detection is still in progress or failed // AUR helper detection is still in progress or failed
// Try to detect again if not already in progress // Try to detect again if not already in progress
if (!yayCheckProcess.running && !paruCheckProcess.running) { if (!yayCheckProcess.running && !paruCheckProcess.running) {
Logger.log("ArchUpdater", "No AUR helper cached, starting detection...")
getAurHelper() getAurHelper()
} }
Logger.warn("ArchUpdater", "AUR helper detection in progress or failed") Logger.warn("ArchUpdater", "AUR helper detection in progress or failed")
@ -410,7 +407,6 @@ Singleton {
// Update all packages (repo + AUR) // Update all packages (repo + AUR)
function runUpdate() { function runUpdate() {
if (totalUpdates === 0) { if (totalUpdates === 0) {
Logger.log("ArchUpdater", "No updates available, polling for updates...")
doPoll() doPoll()
return return
} }
@ -419,16 +415,20 @@ Singleton {
updateFailed = false updateFailed = false
lastUpdateError = "" lastUpdateError = ""
updateInProgress = true updateInProgress = true
Logger.log("ArchUpdater", "Starting full system update...")
const terminal = Quickshell.env("TERMINAL") || "xterm" 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 // Check if we have an AUR helper for full system update
if (cachedAurHelper !== "" && (aurUpdates > 0 || updates > 0)) { if (cachedAurHelper !== "" && (aurUpdates > 0 || updates > 0)) {
// Use AUR helper for full system update (handles both repo and AUR) // Use AUR helper for full system update (handles both repo and AUR)
const command = generateUpdateCommand(cachedAurHelper + " -Syu") const command = generateUpdateCommand(cachedAurHelper + " -Syu")
Logger.log("ArchUpdater", "Full update command:", cachedAurHelper + " -Syu")
Logger.log("ArchUpdater", "Executing in terminal:", terminal)
Quickshell.execDetached([terminal, "-e", "bash", "-c", command]) Quickshell.execDetached([terminal, "-e", "bash", "-c", command])
} else if (cachedAurHelper === "") { } else if (cachedAurHelper === "") {
// No AUR helper found // No AUR helper found
@ -453,60 +453,29 @@ Singleton {
updateFailed = false updateFailed = false
lastUpdateError = "" lastUpdateError = ""
updateInProgress = true updateInProgress = true
Logger.log("ArchUpdater", "Starting selective update for", selectedPackages.length, "packages")
const terminal = Quickshell.env("TERMINAL") || "xterm" const terminal = Quickshell.env("TERMINAL")
if (!terminal) {
// Split selected packages by source updateInProgress = false
const repoPkgs = [] updateFailed = true
const aurPkgs = [] lastUpdateError = "TERMINAL environment variable not set"
ToastService.showWarning("ArchUpdater", "TERMINAL environment variable not set")
for (const pkgName of selectedPackages) { return
const repoPkg = repoPackages.find(p => p.name === pkgName)
if (repoPkg && repoPkg.source === "repo") {
repoPkgs.push(pkgName)
}
const aurPkg = aurPackages.find(p => p.name === pkgName)
if (aurPkg && aurPkg.source === "aur") {
aurPkgs.push(pkgName)
}
} }
Logger.log("ArchUpdater", "Package source analysis:")
Logger.log("ArchUpdater", " - Repo packages:", repoPkgs)
Logger.log("ArchUpdater", " - AUR packages:", aurPkgs)
Logger.log("ArchUpdater", " - Total repo packages available:", repoPackages.length)
Logger.log("ArchUpdater", " - Total AUR packages available:", aurPackages.length)
// Update all packages with AUR helper (handles both repo and AUR) // Update all packages with AUR helper (handles both repo and AUR)
if (selectedPackages.length > 0) { if (selectedPackages.length > 0) {
if (cachedAurHelper !== "") { if (cachedAurHelper !== "") {
const packageList = selectedPackages.join(" ") const packageList = selectedPackages.join(" ")
const command = generateUpdateCommand(cachedAurHelper + " -S " + packageList)
Logger.log("ArchUpdater", "Selective update command:", cachedAurHelper + " -S " + packageList)
Logger.log("ArchUpdater", "Selected packages:", selectedPackages)
Logger.log("ArchUpdater", "Terminal:", terminal)
Logger.log("ArchUpdater", "AUR helper version check - running:", cachedAurHelper + " --version")
// Log system info for debugging // Handle ghostty terminal differently due to command parsing issues
Logger.log("ArchUpdater", "System info for debugging:") if (terminal.includes("ghostty")) {
Logger.log("ArchUpdater", " - AUR helper:", cachedAurHelper) const simpleCommand = cachedAurHelper + " -S " + packageList
Logger.log("ArchUpdater", " - Terminal:", terminal) Quickshell.execDetached([terminal, "-e", simpleCommand])
Logger.log("ArchUpdater", " - Total updates available:", totalUpdates) } else {
Logger.log("ArchUpdater", " - Package source breakdown:") const command = generateUpdateCommand(cachedAurHelper + " -S " + packageList)
Logger.log("ArchUpdater", " * Repo packages:", repoPkgs.length, ":", repoPkgs) Quickshell.execDetached([terminal, "-e", "bash", "-c", command])
Logger.log("ArchUpdater", " * AUR packages:", aurPkgs.length, ":", aurPkgs) }
Logger.log("ArchUpdater", " - Base command:", cachedAurHelper + " -S " + packageList)
Logger.log("ArchUpdater", " - Full wrapped command:", command)
Logger.log("ArchUpdater", " - Terminal command array:", [terminal, "-e", "bash", "-c", command])
// Test: Try without the wrapper first to see if that's the issue
Logger.log("ArchUpdater", "TESTING: Executing command without wrapper to isolate issue")
Quickshell.execDetached([terminal, "-e", "bash", "-c", cachedAurHelper + " -S " + packageList])
// Original wrapped command (commented out for testing)
// Quickshell.execDetached([terminal, "-e", "bash", "-c", command])
} else { } else {
updateInProgress = false updateInProgress = false
updateFailed = true updateFailed = true
@ -523,8 +492,6 @@ Singleton {
// Reset update state (useful for manual recovery) // Reset update state (useful for manual recovery)
function resetUpdateState() { function resetUpdateState() {
Logger.log("ArchUpdater", "Reset update state triggered")
// Clear all update states // Clear all update states
updateInProgress = false updateInProgress = false
updateFailed = false updateFailed = false
@ -545,12 +512,9 @@ Singleton {
function forceRefresh() { function forceRefresh() {
// Prevent multiple simultaneous refreshes // Prevent multiple simultaneous refreshes
if (aurBusy) { if (aurBusy) {
Logger.log("ArchUpdater", "Refresh skipped - already checking for updates")
return return
} }
Logger.log("ArchUpdater", "Manual refresh triggered")
// Clear error states when refreshing // Clear error states when refreshing
updateFailed = false updateFailed = false
lastUpdateError = "" lastUpdateError = ""
@ -568,9 +532,6 @@ Singleton {
checkAurUpdatesProcess.command = [cachedAurHelper, "-Qu"] checkAurUpdatesProcess.command = [cachedAurHelper, "-Qu"]
checkAurOnlyProcess.command = [cachedAurHelper, getAurOnlyFlag()] checkAurOnlyProcess.command = [cachedAurHelper, getAurOnlyFlag()]
Logger.log("ArchUpdater", "Refresh commands:", cachedAurHelper + " -Qu", "and",
cachedAurHelper + " " + getAurOnlyFlag())
// Force refresh by bypassing cooldown // Force refresh by bypassing cooldown
checkAurUpdatesProcess.running = true checkAurUpdatesProcess.running = true
lastPollTime = Date.now() lastPollTime = Date.now()
@ -578,7 +539,6 @@ Singleton {
// AUR helper detection is still in progress or failed // AUR helper detection is still in progress or failed
// Try to detect again if not already in progress // Try to detect again if not already in progress
if (!yayCheckProcess.running && !paruCheckProcess.running) { if (!yayCheckProcess.running && !paruCheckProcess.running) {
Logger.log("ArchUpdater", "No AUR helper cached, starting detection...")
getAurHelper() getAurHelper()
} }
Logger.warn("ArchUpdater", "AUR helper detection in progress or failed") Logger.warn("ArchUpdater", "AUR helper detection in progress or failed")
@ -602,8 +562,6 @@ Singleton {
checkFailed = false checkFailed = false
lastCheckError = "" lastCheckError = ""
} }
// Log diagnostics
logAurHelperDiagnostics()
// Trigger initial check when helper is found // Trigger initial check when helper is found
triggerInitialCheck() triggerInitialCheck()
} }
@ -625,8 +583,6 @@ Singleton {
checkFailed = false checkFailed = false
lastCheckError = "" lastCheckError = ""
} }
// Log diagnostics
logAurHelperDiagnostics()
// Trigger initial check when helper is found // Trigger initial check when helper is found
triggerInitialCheck() triggerInitialCheck()
} else { } else {
@ -636,37 +592,6 @@ Singleton {
} }
} }
// Process for checking AUR helper version
Process {
id: versionCheckProcess
command: []
onExited: function (exitCode) {
if (exitCode === 0) {
Logger.log("ArchUpdater", " - Version check successful")
} else {
Logger.log("ArchUpdater", " - Version check failed")
}
}
}
// Process for checking AUR helper location
Process {
id: locationCheckProcess
command: []
onExited: function (exitCode) {
if (exitCode === 0) {
Logger.log("ArchUpdater", " - Location check successful")
} else {
Logger.log("ArchUpdater", " - Location check failed")
}
}
stdout: StdioCollector {
onStreamFinished: {
Logger.log("ArchUpdater", " - Location:", text.trim())
}
}
}
// Cached AUR helper detection // Cached AUR helper detection
property string cachedAurHelper: "" property string cachedAurHelper: ""
@ -699,22 +624,6 @@ Singleton {
return "-Qua" // fallback return "-Qua" // fallback
} }
// Helper function to log AUR helper diagnostic info
function logAurHelperDiagnostics() {
if (cachedAurHelper !== "") {
Logger.log("ArchUpdater", "AUR Helper Diagnostics:")
Logger.log("ArchUpdater", " - Helper:", cachedAurHelper)
// Check version using existing process
versionCheckProcess.command = [cachedAurHelper, "--version"]
versionCheckProcess.running = true
// Check location using existing process
locationCheckProcess.command = ["which", cachedAurHelper]
locationCheckProcess.running = true
}
}
// Helper function to trigger the initial package check // Helper function to trigger the initial package check
function triggerInitialCheck() { function triggerInitialCheck() {
// Only trigger if this is the first time (no packages have been checked yet) // Only trigger if this is the first time (no packages have been checked yet)