Simplify the system-update check

No need for a panel, just focus on the main omarchy package
This commit is contained in:
David Heinemeier Hansson
2026-06-23 15:59:27 +02:00
parent aa7350ab60
commit 3fd36802ac
7 changed files with 86 additions and 550 deletions
-2
View File
@@ -84,8 +84,6 @@ run_update_pipeline() {
omarchy-update-analyze-logs
# Re-check after updates so the status bar reflects any remaining updates.
# This clears the state when everything is current, so a separate reset command
# is not needed.
if omarchy-update-available >/dev/null; then
omarchy-shell -q omarchy.system-update refresh
else
+15 -110
View File
@@ -1,119 +1,24 @@
#!/bin/bash
# omarchy:summary=Check whether system or AUR packages have available updates.
# omarchy:summary=Check whether Omarchy package updates are available.
set -euo pipefail
CHECK_TIMEOUT="${OMARCHY_UPDATE_CHECK_TIMEOUT:-60}"
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/omarchy/updates"
TMP_PARENT="${TMPDIR:-/tmp}"
mkdir -p "$STATE_DIR"
tmp_dir=$(mktemp -d "$TMP_PARENT/omarchy-update-available.XXXXXX")
trap 'rm -rf "$tmp_dir"' EXIT
packages_raw="$tmp_dir/packages.raw"
packages_tmp="$tmp_dir/packages"
aur_raw="$tmp_dir/aur.raw"
aur_tmp="$tmp_dir/aur"
errors_tmp="$tmp_dir/errors"
packages_file="$STATE_DIR/packages"
aur_file="$STATE_DIR/aur"
available_file="$STATE_DIR/available"
checked_at_file="$STATE_DIR/checked-at"
error_file="$STATE_DIR/error"
: >"$packages_raw"
: >"$packages_tmp"
: >"$aur_raw"
: >"$aur_tmp"
: >"$errors_tmp"
strip_update_output() {
local source="$1"
local destination="$2"
sed -E 's/\x1B\[[0-9;]*m//g' "$source" | sed '/^[[:space:]]*$/d' >"$destination"
}
check_system_packages() {
local status=0
if command -v checkupdates >/dev/null 2>&1 && command -v fakeroot >/dev/null 2>&1; then
CHECKUPDATES_DB="$tmp_dir/checkupdates-db" timeout "$CHECK_TIMEOUT" checkupdates --nocolor >"$packages_raw" 2>"$tmp_dir/checkupdates.err" || status=$?
case "$status" in
0|2)
;;
124)
echo "System package update check timed out" >>"$errors_tmp"
;;
*)
echo "System package update check failed" >>"$errors_tmp"
sed '/^[[:space:]]*$/d' "$tmp_dir/checkupdates.err" >>"$errors_tmp" || true
;;
esac
elif command -v pacman >/dev/null 2>&1; then
# Fallback for systems that have not picked up pacman-contrib/fakeroot yet.
# This only reports updates already known to the local sync database.
pacman -Qu --color never >"$packages_raw" 2>/dev/null || true
else
echo "pacman is not available" >>"$errors_tmp"
fi
strip_update_output "$packages_raw" "$packages_tmp"
}
check_aur_packages() {
local status=0
command -v pacman >/dev/null 2>&1 || return 0
command -v yay >/dev/null 2>&1 || return 0
pacman -Qem >/dev/null 2>&1 || return 0
timeout "$CHECK_TIMEOUT" yay --color never -Qua >"$aur_raw" 2>"$tmp_dir/yay.err" || status=$?
case "$status" in
0|1)
;;
124)
echo "AUR package update check timed out" >>"$errors_tmp"
;;
*)
echo "AUR package update check failed" >>"$errors_tmp"
sed '/^[[:space:]]*$/d' "$tmp_dir/yay.err" >>"$errors_tmp" || true
;;
esac
strip_update_output "$aur_raw" "$aur_tmp"
grep -vw '\[ignored\]$' "$aur_tmp" >"$aur_tmp.filtered" || true
mv "$aur_tmp.filtered" "$aur_tmp"
}
check_system_packages
check_aur_packages
cp "$packages_tmp" "$packages_file"
cp "$aur_tmp" "$aur_file"
cat "$packages_tmp" "$aur_tmp" >"$available_file"
date --iso-8601=seconds >"$checked_at_file"
if [[ -s $errors_tmp ]]; then
cp "$errors_tmp" "$error_file"
else
rm -f "$error_file"
package=""
if pacman -Qq omarchy-dev >/dev/null 2>&1; then
package=omarchy-dev
elif pacman -Qq omarchy >/dev/null 2>&1; then
package=omarchy
fi
if [[ -s $available_file ]]; then
cat "$available_file"
if [[ -n $package ]]; then
update=$(checkupdates --nocolor 2>/dev/null | awk -v package="$package" '$1 == package { print; exit }' || true)
fi
if [[ -n ${update:-} ]]; then
printf '%s\n' "$update"
exit 0
else
echo "Omarchy is up to date"
exit 1
fi
if [[ -s $error_file ]]; then
cat "$error_file" >&2
fi
echo "System is up to date"
exit 1
+2 -7
View File
@@ -1113,9 +1113,7 @@ run_final_system_package_upgrade() {
}
run_post_upgrade_update_steps() {
local update_error_file update_output update_status
update_error_file="$target_home/.local/state/omarchy/updates/error"
local update_output update_status
if PATH="$package_path" command -v omarchy-update-aur-pkgs >/dev/null 2>&1; then
if ! run_as_user env \
@@ -1125,7 +1123,7 @@ run_post_upgrade_update_steps() {
OMARCHY_PATH=/usr/share/omarchy \
PATH="$package_path" \
omarchy-update-aur-pkgs; then
warn "Could not update AUR packages; the update indicator may still show AUR updates after reboot."
warn "Could not update AUR packages; running omarchy-update after reboot may still update AUR packages."
fi
fi
@@ -1155,9 +1153,6 @@ run_post_upgrade_update_steps() {
if (( update_status == 0 )); then
warn "Updates are still available after the upgrade; first-run will still offer to run omarchy-update:"
printf '%s\n' "$update_output" >&2
elif [[ -s $update_error_file ]]; then
warn "Could not confirm update state after upgrade; first-run will still offer to run omarchy-update:"
printf '%s\n' "$update_output" >&2
else
# The live upgrade has already completed the package update. Let first-run
# finish session-only setup, but skip its generic fresh-install update
+8 -18
View File
@@ -23,7 +23,6 @@ The design goal is:
| --- | --- | --- |
| `${XDG_RUNTIME_DIR:-/tmp}/omarchy-update.lock` | user | Prevent overlapping update runs. Owned by `omarchy-update`; compatibility wrappers inherit/respect it. |
| `/tmp/omarchy-update.log` | user | Transcript of `omarchy update`, used by `omarchy-update-analyze-logs`. |
| `~/.local/state/omarchy/updates/` | user | Update-check state for the shell widget (`packages`, `aur`, `available`, `checked-at`, `error`). |
| `~/.local/state/omarchy/current/` | user | Generated active theme, selected theme name, and current background symlink. |
| `~/.local/state/omarchy/migrations/` | user | Per-user migration markers. |
| `~/.local/state/omarchy/reboot-required` | user | Optional reboot marker checked by `omarchy-update-restart`. |
@@ -173,27 +172,18 @@ The bar widget `omarchy.system-update` runs:
omarchy-update-available
```
`omarchy-update-available` uses `checkupdates` with a temporary database when
available, plus `yay -Qua` for AUR updates when foreign packages are installed.
It stores the result in:
`omarchy-update-available` checks the installed Omarchy package for updates:
```text
~/.local/state/omarchy/updates/packages
~/.local/state/omarchy/updates/aur
~/.local/state/omarchy/updates/available
~/.local/state/omarchy/updates/checked-at
~/.local/state/omarchy/updates/error
```
- `omarchy-dev`, when installed
- otherwise `omarchy`, when installed
Exit codes:
- `0` — updates are available; stdout is the update list.
- non-zero — no updates are available; stdout says the system is up to date.
- `0` Omarchy updates are available; stdout is the update list.
- non-zero — no Omarchy updates are available; stdout says Omarchy is up to date.
The widget uses the line count to show/hide itself. It also watches the
`available` state file, so manually running `omarchy-update-available` updates
the widget as soon as the file changes. Hovering the update icon opens a panel
that lists Omarchy packages first, then all other package updates.
The widget runs this check on shell startup and every six hours. Clicking the
update icon launches `omarchy-update` in a floating terminal.
## Update-related binaries
@@ -212,7 +202,7 @@ scripts.
| `omarchy-update-pacman-guard` | ALPM pre-transaction guard that aborts direct `pacman -Syu` style upgrades unless Omarchy set `OMARCHY_UPDATE_PACMAN=1` or the user explicitly set `OMARCHY_ALLOW_DIRECT_PACMAN=1`. | **Keep internal/hidden.** This is what nudges users back to `omarchy update`. |
| `omarchy-migrate-notify` | Internal notification helper for direct pacman updates. Uses `omarchy-migrate --pending` and shows notification only when this user has pending migrations. | **Keep internal/hidden.** Clear name now that the public command is `omarchy-migrate`. |
| `omarchy-update-user-notify` | Hidden compatibility wrapper for `omarchy-migrate-notify`. | **Temporary.** Keep only for old callers. |
| `omarchy-update-available` | Update checker for shell widget and post-update refresh. Writes update state. | **Keep.** Could eventually be renamed `omarchy-update-check`, but current name matches widget semantics. |
| `omarchy-update-available` | Update checker for shell widget and post-update refresh. | **Keep.** Could eventually be renamed `omarchy-update-check`, but current name matches widget semantics. |
| `omarchy-update-aur-pkgs` | Updates AUR packages with `yay -Sua` if foreign packages exist and AUR is reachable. | **Question.** Omarchy is package-backed now, but users may still install AUR packages. Keep for now. |
| `omarchy-update-mise` | Runs `mise up` for mise-managed tools. | **Keep.** Mise-managed tools are intentionally part of the blessed update path. |
| `omarchy-update-orphan-pkgs` | Lists orphans and prompts before removal; noninteractive mode never removes. | **Keep for now.** Safe because it is prompt-only. |
@@ -1,10 +1,10 @@
{
"schemaVersion": 1,
"id": "omarchy.system-update",
"name": "System update",
"name": "Omarchy update",
"version": "1.0.0",
"author": "Omarchy",
"description": "Indicates available system updates",
"description": "Indicates available Omarchy updates",
"kinds": [
"bar-widget"
],
@@ -12,8 +12,8 @@
"barWidget": "SystemUpdate.qml"
},
"barWidget": {
"displayName": "System update",
"description": "Indicates available system updates",
"displayName": "Omarchy update",
"description": "Indicates available Omarchy updates",
"category": "System",
"allowMultiple": false
}
+4 -362
View File
@@ -1,6 +1,4 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Commons
@@ -11,117 +9,17 @@ BarWidget {
moduleName: "omarchy.system-update"
property bool updateAvailable: false
property int updateCount: 0
property string updateOutput: ""
property var updateLines: []
property var omarchyUpdateLines: []
property var otherUpdateLines: []
property bool popupOpen: false
property bool buttonHovered: false
property bool popupHovered: popup.containsMouse
readonly property string stateHome: Quickshell.env("XDG_STATE_HOME") || (Quickshell.env("HOME") + "/.local/state")
readonly property string availableStatePath: stateHome + "/omarchy/updates/available"
readonly property color foreground: root.bar ? root.bar.foreground : Color.foreground
readonly property color urgent: root.bar ? root.bar.urgent : Color.urgent
readonly property color dim: Qt.darker(foreground, 1.55)
readonly property string fontFamily: root.bar ? root.bar.fontFamily : Style.font.family
readonly property string changelogUrl: "https://github.com/basecamp/omarchy/releases/latest"
function close() { popupOpen = false }
function refresh() {
updateOutput = ""
if (!updateProc.running) updateProc.running = true
}
function clear() {
updateOutput = ""
updateLines = []
omarchyUpdateLines = []
otherUpdateLines = []
updateCount = 0
updateAvailable = false
popupOpen = false
}
function clear() { updateAvailable = false }
function runUpdate() {
if (root.bar) root.bar.run("omarchy-launch-floating-terminal-with-presentation omarchy-update")
}
function openChangelog() {
Qt.openUrlExternally(root.changelogUrl)
}
function packageName(line) {
return String(line || "").trim().split(/\s+/)[0] || ""
}
function versionMatch(line) {
return String(line || "").trim().match(/^\S+\s+(.+?)\s+->\s+(.+)$/)
}
function versionFrom(line) {
var match = versionMatch(line)
return match ? match[1] : ""
}
function versionTo(line) {
var match = versionMatch(line)
return match ? match[2] : ""
}
function isOmarchyPackage(line) {
var pkg = packageName(line)
return pkg === "omarchy" || pkg.indexOf("omarchy-") === 0
}
function countLabel(count) {
return count === 1 ? "1 update" : count + " updates"
}
function pendingPackageLabel(count) {
return count === 1 ? "1 package pending update" : count + " packages pending update"
}
function parseUpdateText(text) {
var lines = String(text || "").split(/\r?\n/).filter(function(line) {
return line.trim().length > 0
})
var omarchyLines = []
var otherLines = []
for (var i = 0; i < lines.length; i++) {
if (isOmarchyPackage(lines[i])) omarchyLines.push(lines[i])
else otherLines.push(lines[i])
}
omarchyUpdateLines = omarchyLines
otherUpdateLines = otherLines
updateLines = omarchyLines.concat(otherLines)
updateCount = updateLines.length
updateAvailable = updateCount > 0
if (!updateAvailable) popupOpen = false
}
function applyUpdateOutput(exitCode) {
var output = String(updateStdout.text || updateOutput || "")
parseUpdateText(exitCode === 0 ? output : "")
}
function showPopup() {
hideTimer.stop()
if (updateAvailable) popupOpen = true
}
function scheduleHide() {
hideTimer.restart()
}
onButtonHoveredChanged: buttonHovered ? showPopup() : scheduleHide()
onPopupHoveredChanged: popupHovered ? hideTimer.stop() : scheduleHide()
onUpdateAvailableChanged: if (!updateAvailable) popupOpen = false
visible: updateAvailable
implicitWidth: button.implicitWidth
implicitHeight: button.implicitHeight
@@ -140,23 +38,12 @@ BarWidget {
Process {
id: updateProc
command: ["bash", "-lc", "omarchy-update-available"]
stdout: StdioCollector { id: updateStdout; waitForEnd: true; onStreamFinished: root.updateOutput = text }
command: ["omarchy-update-available"]
onExited: function(exitCode) {
root.applyUpdateOutput(exitCode)
root.updateAvailable = exitCode === 0
}
}
FileView {
id: availableState
path: root.availableStatePath
watchChanges: true
printErrors: false
onLoaded: root.parseUpdateText(text())
onLoadFailed: root.clear()
onFileChanged: reload()
}
Timer {
interval: 21600000
running: true
@@ -165,258 +52,13 @@ BarWidget {
onTriggered: root.refresh()
}
Timer {
id: hideTimer
interval: 220
onTriggered: {
if (!root.buttonHovered && !root.popupHovered) root.popupOpen = false
}
}
WidgetButton {
id: button
anchors.fill: parent
bar: root.bar
text: root.updateAvailable ? "\uf021" : ""
text: "\uf021"
fontSize: Style.font.caption
tooltipText: ""
onPressed: root.runUpdate()
}
HoverHandler {
id: hoverHandler
target: button
onHoveredChanged: root.buttonHovered = hovered
}
PopupCard {
id: popup
anchorItem: button
owner: root
bar: root.bar
open: root.popupOpen && root.updateAvailable
triggerMode: "hover"
contentWidth: popup.fittedContentWidth(Style.space(420))
contentHeight: popup.fittedContentHeight(panelColumn.implicitHeight + Style.space(4), Style.space(640))
Flickable {
id: updateFlick
anchors.fill: parent
contentWidth: width
contentHeight: panelColumn.implicitHeight
clip: true
boundsBehavior: Flickable.StopAtBounds
flickableDirection: Flickable.VerticalFlick
interactive: contentHeight > height
ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded }
Column {
id: panelColumn
width: updateFlick.width
spacing: Style.space(12)
PanelHero {
width: parent.width
title: "System update"
meta: countLabel(root.updateCount) + " available"
foreground: root.foreground
fontFamily: root.fontFamily
iconComponent: Component {
Text {
text: "\uf021"
color: root.foreground
font.family: root.fontFamily
font.pixelSize: Style.font.display
}
}
}
Button {
width: parent.width
text: "Run omarchy update"
iconText: "\uf021"
foreground: root.foreground
accent: root.urgent
fontFamily: root.fontFamily
fontSize: Style.font.bodySmall
iconSize: Style.font.body
bordered: true
onClicked: root.runUpdate()
}
PanelSeparator {
visible: root.updateLines.length > 0
foreground: root.foreground
}
UpdateSection {
title: "Omarchy"
lines: root.omarchyUpdateLines
important: true
showChangelog: true
width: parent.width
}
PanelSeparator {
visible: root.omarchyUpdateLines.length > 0 && root.otherUpdateLines.length > 0
foreground: root.foreground
}
OtherPackagesSummary {
count: root.otherUpdateLines.length
width: parent.width
}
}
}
}
component UpdateSection: Column {
id: section
property string title: ""
property var lines: []
property bool important: false
property bool showChangelog: false
visible: lines.length > 0
spacing: Style.space(8)
PanelSectionHeader {
width: section.width
text: section.title.toUpperCase()
foreground: root.foreground
fontFamily: root.fontFamily
}
Column {
width: section.width
spacing: Style.space(6)
Repeater {
model: section.lines
delegate: UpdateRow {
width: parent.width
line: modelData
important: section.important
}
}
}
Button {
visible: section.showChangelog
width: section.width
text: "View latest release notes"
iconText: "\uf08e"
foreground: root.foreground
accent: root.urgent
fontFamily: root.fontFamily
fontSize: Style.font.bodySmall
iconSize: Style.font.body
bordered: true
onClicked: root.openChangelog()
}
}
component OtherPackagesSummary: Column {
id: summary
property int count: 0
visible: count > 0
spacing: Style.space(8)
PanelSectionHeader {
width: summary.width
text: "OTHER PACKAGES"
foreground: root.foreground
fontFamily: root.fontFamily
}
Text {
width: summary.width
text: root.pendingPackageLabel(summary.count)
color: root.foreground
font.family: root.fontFamily
font.pixelSize: Style.font.body
font.bold: true
elide: Text.ElideRight
}
}
component UpdateRow: CursorSurface {
id: row
property string line: ""
property bool important: false
readonly property string packageTitle: root.packageName(line)
readonly property string fromVersion: root.versionFrom(line)
readonly property string toVersion: root.versionTo(line)
readonly property bool hasVersions: fromVersion !== "" && toVersion !== ""
foreground: root.foreground
accent: root.urgent
implicitHeight: Math.max(Style.spacing.popupRowHeight, rowContent.implicitHeight + Style.spacing.rowPaddingX)
ColumnLayout {
id: rowContent
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Style.spacing.rowPaddingX
anchors.rightMargin: Style.spacing.rowPaddingX
spacing: Style.space(1)
Text {
Layout.fillWidth: true
text: row.packageTitle
color: root.foreground
font.family: root.fontFamily
font.pixelSize: Style.font.body
font.bold: true
elide: Text.ElideRight
}
Row {
visible: row.hasVersions
Layout.fillWidth: true
spacing: Style.space(7)
Text {
text: row.fromVersion
color: root.dim
font.family: root.fontFamily
font.pixelSize: Style.font.caption
elide: Text.ElideRight
}
Text {
text: "\u2192"
color: row.important ? root.urgent : root.dim
font.family: root.fontFamily
font.pixelSize: Style.font.bodySmall
font.bold: true
}
Text {
text: row.toVersion
color: row.important ? root.urgent : root.foreground
font.family: root.fontFamily
font.pixelSize: Style.font.caption
font.bold: true
elide: Text.ElideRight
}
}
Text {
visible: !row.hasVersions
Layout.fillWidth: true
text: row.line
color: root.dim
font.family: root.fontFamily
font.pixelSize: Style.font.caption
wrapMode: Text.WrapAnywhere
}
}
}
}
+53 -47
View File
@@ -8,21 +8,13 @@ test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT
stub_bin="$test_tmp/bin"
test_home="$test_tmp/home"
state_home="$test_tmp/state"
mkdir -p "$stub_bin" "$test_home" "$state_home"
cat >"$stub_bin/fakeroot" <<'SH'
#!/bin/bash
exit 0
SH
chmod +x "$stub_bin/fakeroot"
mkdir -p "$stub_bin"
cat >"$stub_bin/checkupdates" <<'SH'
#!/bin/bash
case "${TEST_CHECKUPDATES:-updates}" in
updates)
printf 'linux 6.1-1 -> 6.1-2\nomarchy 4.0.0-1 -> 4.0.1-1\n'
printf 'linux 6.1-1 -> 6.1-2\nomarchy 4.0.0-1 -> 4.0.1-1\nomarchy-settings 4.0.0-1 -> 4.0.1-1\nomarchy-dev 4.1.0-1 -> 4.1.1-1\nomarchy-settings-dev 4.1.0-1 -> 4.1.1-1\n'
exit 0
;;
none)
@@ -39,35 +31,29 @@ chmod +x "$stub_bin/checkupdates"
cat >"$stub_bin/pacman" <<'SH'
#!/bin/bash
case "$1" in
-Qem)
[[ ${TEST_AUR_INSTALLED:-0} == "1" ]] && exit 0 || exit 1
;;
-Qu)
if [[ ${TEST_PACMAN_FALLBACK_UPDATES:-0} == "1" ]]; then
echo "fallback-pkg 1-1 -> 1-2"
fi
exit 0
-Qq)
case "${TEST_INSTALLED_PACKAGE:-omarchy}" in
omarchy)
[[ $2 == "omarchy" ]]; exit $?
;;
omarchy-dev)
[[ $2 == "omarchy-dev" ]]; exit $?
;;
both)
[[ $2 == "omarchy" || $2 == "omarchy-dev" ]]; exit $?
;;
none)
exit 1
;;
esac
;;
esac
exit 0
SH
chmod +x "$stub_bin/pacman"
cat >"$stub_bin/yay" <<'SH'
#!/bin/bash
if [[ ${TEST_YAY_UPDATES:-0} == "1" ]]; then
echo "aur-helper 1-1 -> 1-2"
fi
exit 0
SH
chmod +x "$stub_bin/yay"
run_checker() {
HOME="$test_home" \
XDG_STATE_HOME="$state_home" \
PATH="$stub_bin:$PATH" \
OMARCHY_UPDATE_CHECK_TIMEOUT=5 \
"$ROOT/bin/omarchy-update-available"
PATH="$stub_bin:$PATH" "$ROOT/bin/omarchy-update-available"
}
capture_checker() {
@@ -88,33 +74,53 @@ capture_checker() {
stdout="$test_tmp/stdout"
stderr="$test_tmp/stderr"
if capture_checker "$stdout" "$stderr" TEST_CHECKUPDATES=updates TEST_AUR_INSTALLED=0; then
if capture_checker "$stdout" "$stderr" TEST_CHECKUPDATES=updates TEST_INSTALLED_PACKAGE=omarchy; then
status=0
else
status=$?
fi
[[ $status -eq 0 ]] || fail "update checker exits successfully when system updates are available"
grep -q '^linux ' "$stdout" || fail "update checker prints system package updates"
grep -q '^omarchy ' "$state_home/omarchy/updates/packages" || fail "update checker stores system package updates"
[[ $(wc -l <"$state_home/omarchy/updates/available") -eq 2 ]] || fail "update checker stores combined update list"
pass "update checker detects system package updates"
[[ $status -eq 0 ]] || fail "update checker exits successfully when omarchy update is available"
grep -q '^omarchy ' "$stdout" || fail "update checker prints omarchy updates"
! grep -q '^omarchy-settings ' "$stdout" || fail "update checker ignores omarchy-settings updates"
! grep -q '^linux ' "$stdout" || fail "update checker ignores non-Omarchy package updates"
! grep -q '^omarchy-dev ' "$stdout" || fail "update checker ignores omarchy-dev when omarchy is installed"
pass "update checker detects installed omarchy package updates"
if capture_checker "$stdout" "$stderr" TEST_CHECKUPDATES=none TEST_AUR_INSTALLED=1 TEST_YAY_UPDATES=1; then
if capture_checker "$stdout" "$stderr" TEST_CHECKUPDATES=updates TEST_INSTALLED_PACKAGE=omarchy-dev; then
status=0
else
status=$?
fi
[[ $status -eq 0 ]] || fail "update checker exits successfully when AUR updates are available"
grep -q '^aur-helper ' "$stdout" || fail "update checker prints AUR package updates"
grep -q '^aur-helper ' "$state_home/omarchy/updates/aur" || fail "update checker stores AUR package updates"
pass "update checker detects AUR package updates"
[[ $status -eq 0 ]] || fail "update checker exits successfully when omarchy-dev update is available"
grep -q '^omarchy-dev ' "$stdout" || fail "update checker prints omarchy-dev updates"
! grep -q '^omarchy-settings-dev ' "$stdout" || fail "update checker ignores omarchy-settings-dev updates"
! grep -q '^omarchy ' "$stdout" || fail "update checker ignores omarchy when omarchy-dev is installed"
pass "update checker detects installed omarchy-dev package updates"
if capture_checker "$stdout" "$stderr" TEST_CHECKUPDATES=none TEST_AUR_INSTALLED=0; then
if capture_checker "$stdout" "$stderr" TEST_CHECKUPDATES=updates TEST_INSTALLED_PACKAGE=both; then
status=0
else
status=$?
fi
[[ $status -eq 0 ]] || fail "update checker prefers omarchy-dev when both packages are installed"
grep -q '^omarchy-dev ' "$stdout" || fail "update checker prints omarchy-dev when both packages are installed"
! grep -q '^omarchy ' "$stdout" || fail "update checker ignores omarchy when omarchy-dev is installed"
pass "update checker prefers omarchy-dev over omarchy"
if capture_checker "$stdout" "$stderr" TEST_CHECKUPDATES=updates TEST_INSTALLED_PACKAGE=none; then
status=0
else
status=$?
fi
[[ $status -eq 1 ]] || fail "update checker exits non-zero when no Omarchy package is installed"
[[ ! -s $stderr ]] || fail "update checker is quiet when no Omarchy package is installed"
pass "update checker ignores systems without omarchy or omarchy-dev installed"
if capture_checker "$stdout" "$stderr" TEST_CHECKUPDATES=none TEST_INSTALLED_PACKAGE=omarchy; then
status=0
else
status=$?
fi
[[ $status -eq 1 ]] || fail "update checker exits non-zero when no updates are available"
grep -q '^System is up to date$' "$stdout" || fail "update checker prints up-to-date message"
[[ ! -s "$state_home/omarchy/updates/available" ]] || fail "update checker clears combined update list when up to date"
pass "update checker reports up-to-date systems"
grep -q '^Omarchy is up to date$' "$stdout" || fail "update checker prints up-to-date message"
pass "update checker reports up-to-date Omarchy packages"