diff --git a/system_files/shared/usr/lib/systemd/system/aurora-deployment-check.service b/system_files/shared/usr/lib/systemd/system/aurora-deployment-check.service new file mode 100644 index 0000000..c810fc1 --- /dev/null +++ b/system_files/shared/usr/lib/systemd/system/aurora-deployment-check.service @@ -0,0 +1,8 @@ +[Unit] +Description=Check for staged Aurora deployments +Documentation=https://docs.getaurora.dev/ +After=uupd.service rpm-ostreed.service + +[Service] +Type=oneshot +ExecStart=/usr/libexec/aurora-deployment-check diff --git a/system_files/shared/usr/lib/systemd/system/aurora-deployment-check.timer b/system_files/shared/usr/lib/systemd/system/aurora-deployment-check.timer new file mode 100644 index 0000000..2b58112 --- /dev/null +++ b/system_files/shared/usr/lib/systemd/system/aurora-deployment-check.timer @@ -0,0 +1,12 @@ +[Unit] +Description=Periodically check for staged Aurora deployments + +[Timer] +OnBootSec=5min +OnUnitActiveSec=1h +AccuracySec=5min +Persistent=false +Unit=aurora-deployment-check.service + +[Install] +WantedBy=timers.target diff --git a/system_files/shared/usr/lib/systemd/system/uupd.service.d/aurora-deployment-check.conf b/system_files/shared/usr/lib/systemd/system/uupd.service.d/aurora-deployment-check.conf new file mode 100644 index 0000000..63131b2 --- /dev/null +++ b/system_files/shared/usr/lib/systemd/system/uupd.service.d/aurora-deployment-check.conf @@ -0,0 +1,2 @@ +[Unit] +OnSuccess=aurora-deployment-check.service diff --git a/system_files/shared/usr/lib/systemd/user/aurora-deployment-notifier.path b/system_files/shared/usr/lib/systemd/user/aurora-deployment-notifier.path new file mode 100644 index 0000000..03df6a0 --- /dev/null +++ b/system_files/shared/usr/lib/systemd/user/aurora-deployment-notifier.path @@ -0,0 +1,10 @@ +[Unit] +Description=Watch for staged Aurora deployment status +Documentation=https://docs.getaurora.dev/ + +[Path] +PathExists=/run/aurora/staged-ready +Unit=aurora-deployment-notifier.service + +[Install] +WantedBy=default.target diff --git a/system_files/shared/usr/lib/systemd/user/aurora-deployment-notifier.service b/system_files/shared/usr/lib/systemd/user/aurora-deployment-notifier.service new file mode 100644 index 0000000..903b9f8 --- /dev/null +++ b/system_files/shared/usr/lib/systemd/user/aurora-deployment-notifier.service @@ -0,0 +1,11 @@ +[Unit] +Description=Notify when an Aurora deployment is ready to boot +Documentation=https://docs.getaurora.dev/ + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/libexec/aurora-deployment-notifier + +[Install] +WantedBy=default.target diff --git a/system_files/shared/usr/lib/tmpfiles.d/aurora-deployment-notifier.conf b/system_files/shared/usr/lib/tmpfiles.d/aurora-deployment-notifier.conf new file mode 100644 index 0000000..5fb9852 --- /dev/null +++ b/system_files/shared/usr/lib/tmpfiles.d/aurora-deployment-notifier.conf @@ -0,0 +1 @@ +d /run/aurora 0755 root root - diff --git a/system_files/shared/usr/libexec/aurora-deployment-check b/system_files/shared/usr/libexec/aurora-deployment-check new file mode 100755 index 0000000..53d7105 --- /dev/null +++ b/system_files/shared/usr/libexec/aurora-deployment-check @@ -0,0 +1,113 @@ +#!/usr/bin/env bash + +set -euo pipefail + +STATUS_DIR="${AURORA_DEPLOYMENT_STATUS_DIR:-/run/aurora}" +FLAG_FILE="${STATUS_DIR}/staged-ready" +STATUS_FILE="${STATUS_DIR}/deployment-status.json" + +SOURCE="" +STAGED_ID="" +BOOTED_ID="" + +write_status() { + local tmpfile + tmpfile="$(mktemp "${STATUS_DIR}/deployment-status.XXXXXX")" + + jq -n \ + --arg source "${SOURCE}" \ + --arg staged_id "${STAGED_ID}" \ + --arg booted_id "${BOOTED_ID}" \ + --arg created_at "$(date --iso-8601=seconds)" \ + '{ + ready: true, + source: $source, + staged_id: $staged_id, + booted_id: $booted_id, + created_at: $created_at + }' > "${tmpfile}" + + chmod 0644 "${tmpfile}" + mv -f "${tmpfile}" "${STATUS_FILE}" + printf 'ready\n' > "${FLAG_FILE}" + chmod 0644 "${FLAG_FILE}" +} + +clear_status() { + rm -f "${FLAG_FILE}" "${STATUS_FILE}" +} + +check_bootc() { + local bootc_json + local staged_id + local booted_id + + command -v bootc >/dev/null 2>&1 || return 1 + bootc_json="$(bootc status --json 2>/dev/null)" || return 1 + + staged_id="$(jq -er ' + .status as $status + | select($status.staged != null and $status.staged != {}) + | ( + $status.staged.image.imageDigest + // $status.staged.image.digest + // $status.staged.ostree.checksum + // $status.staged.checksum + // ($status.staged | tostring) + ) + ' <<< "${bootc_json}" 2>/dev/null)" || return 1 + + booted_id="$(jq -r ' + .status.booted.image.imageDigest + // .status.booted.image.digest + // .status.booted.ostree.checksum + // .status.booted.checksum + // empty + ' <<< "${bootc_json}" 2>/dev/null)" + + if [[ -n "${staged_id}" && "${staged_id}" != "${booted_id}" ]]; then + SOURCE="bootc" + STAGED_ID="${staged_id}" + BOOTED_ID="${booted_id}" + return 0 + fi + + return 1 +} + +check_rpm_ostree() { + local ostree_json + local staged_json + local staged_id + local booted_id + + command -v rpm-ostree >/dev/null 2>&1 || return 1 + ostree_json="$(rpm-ostree status --json 2>/dev/null)" || return 1 + + staged_json="$(jq -ec '[.deployments[]? | select(.staged == true)] | first // empty' <<< "${ostree_json}" 2>/dev/null)" || return 1 + [[ -n "${staged_json}" ]] || return 1 + + staged_id="$(jq -r '.checksum // .version // .origin // tostring' <<< "${staged_json}" 2>/dev/null)" + booted_id="$(jq -r '[.deployments[]? | select(.booted == true)] | first | .checksum // .version // .origin // empty' <<< "${ostree_json}" 2>/dev/null)" + + if [[ -n "${staged_id}" && "${staged_id}" != "${booted_id}" ]]; then + SOURCE="rpm-ostree" + STAGED_ID="${staged_id}" + BOOTED_ID="${booted_id}" + return 0 + fi + + return 1 +} + +main() { + install -d -m 0755 -o root -g root "${STATUS_DIR}" + + if check_bootc || check_rpm_ostree; then + write_status + else + clear_status + fi +} + +main "$@" diff --git a/system_files/shared/usr/libexec/aurora-deployment-notifier b/system_files/shared/usr/libexec/aurora-deployment-notifier new file mode 100755 index 0000000..4147a9c --- /dev/null +++ b/system_files/shared/usr/libexec/aurora-deployment-notifier @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +set -euo pipefail + +STATUS_DIR="${AURORA_DEPLOYMENT_STATUS_DIR:-/run/aurora}" +FLAG_FILE="${STATUS_DIR}/staged-ready" +STATUS_FILE="${STATUS_DIR}/deployment-status.json" +STATE_DIR="${XDG_STATE_HOME:-${HOME}/.local/state}/aurora/deployment-notifier" +LAST_NOTIFIED_FILE="${STATE_DIR}/last-notified" + +[[ -f "${FLAG_FILE}" ]] || exit 0 +command -v notify-send >/dev/null 2>&1 || exit 0 + +staged_id="" +if [[ -f "${STATUS_FILE}" ]] && command -v jq >/dev/null 2>&1; then + staged_id="$(jq -r '.staged_id // empty' "${STATUS_FILE}" 2>/dev/null || true)" +fi + +if [[ -z "${staged_id}" ]]; then + staged_id="$(sha256sum "${FLAG_FILE}" | cut -d ' ' -f 1)" +fi + +mkdir -p "${STATE_DIR}" + +if [[ -f "${LAST_NOTIFIED_FILE}" ]] && [[ "$(<"${LAST_NOTIFIED_FILE}")" == "${staged_id}" ]]; then + exit 0 +fi + +notify-send \ + --app-name="Aurora" \ + --icon="system-software-update" \ + --urgency=normal \ + "System update ready" \ + "Restart your device to finish applying the staged Aurora update." + +printf '%s\n' "${staged_id}" > "${LAST_NOTIFIED_FILE}" diff --git a/system_files/shared/usr/share/ublue-os/just/00-entry.just b/system_files/shared/usr/share/ublue-os/just/00-entry.just index e5a321c..3740a5f 100644 --- a/system_files/shared/usr/share/ublue-os/just/00-entry.just +++ b/system_files/shared/usr/share/ublue-os/just/00-entry.just @@ -13,4 +13,5 @@ import "/usr/share/ublue-os/just/apps.just" import "/usr/share/ublue-os/just/default.just" import "/usr/share/ublue-os/just/shared.just" import "/usr/share/ublue-os/just/update.just" +import "/usr/share/ublue-os/just/60-aurora-deployment-notifier.just" import? "/usr/share/ublue-os/just/60-custom.just" diff --git a/system_files/shared/usr/share/ublue-os/just/60-aurora-deployment-notifier.just b/system_files/shared/usr/share/ublue-os/just/60-aurora-deployment-notifier.just new file mode 100644 index 0000000..0580d89 --- /dev/null +++ b/system_files/shared/usr/share/ublue-os/just/60-aurora-deployment-notifier.just @@ -0,0 +1,36 @@ +# Toggle Aurora staged deployment desktop notifications. +[group('Aurora')] +aurora-update-notifier action="status": + #!/usr/bin/env bash + set -euo pipefail + + action="{{ action }}" + system_timer="aurora-deployment-check.timer" + system_service="aurora-deployment-check.service" + user_path="aurora-deployment-notifier.path" + + case "${action}" in + enable|on) + systemctl --user enable --now "${user_path}" + sudo systemctl enable --now "${system_timer}" + sudo systemctl start "${system_service}" || true + echo "Aurora update notifications enabled." + ;; + disable|off) + systemctl --user disable --now "${user_path}" || true + sudo systemctl disable --now "${system_timer}" || true + rm -f "${XDG_STATE_HOME:-${HOME}/.local/state}/aurora/deployment-notifier/last-notified" + echo "Aurora update notifications disabled." + ;; + status) + echo "User notifier:" + systemctl --user --no-pager status "${user_path}" || true + echo + echo "System checker:" + systemctl --no-pager status "${system_timer}" || true + ;; + *) + echo "Usage: ujust aurora-update-notifier [enable|disable|status]" + exit 2 + ;; + esac