Files
omarchy/bin/omarchy-channel-set
T
Afonso OliveiraandClaude Fable 5.1 43b91163f6 Install cleanup traps before the entry revocation and bound the bus probe
The protected entrypoints revoked the sudo timestamp before installing
their cleanup traps, so a signal or failure during that first sudo -k
exited without the cleanup path. Install the traps first.

The shell restart probed the notification bus name with busctl's
default 25 second timeout, so an unresponsive user bus could stall the
restart by that much per probe. Bound each probe to one second.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 20:33:59 +01:00

151 lines
5.0 KiB
Bash
Executable File

#!/bin/bash -p
# omarchy:summary=Set the Omarchy package channel.
# omarchy:args=<stable|rc|edge|dev>
# omarchy:requires-sudo=true
if [[ $- != *p* ]]; then
echo "Refusing an unsafe Bash startup for channel switching." >&2
exit 126
fi
security_entrypoint=$(/usr/bin/readlink -e -- "${BASH_SOURCE[0]}") || exit 126
source "${security_entrypoint%/*}/omarchy-security-functions" || exit 126
omarchy_security_require_privileged_bash_startup || exit 126
set -euo pipefail
omarchy_security_sanitize_bash_environment "$0" "$@"
omarchy_security_require_source_root "$0"
user_path=$PATH
# Traps first, so a signal or failure during the entry revocation still
# exits through the cleanup path.
omarchy_security_install_sudo_cleanup_traps
omarchy_security_revoke_sudo_timestamp || exit 1
omarchy_security_enable_no_update_sudo
usage() { echo "Usage: omarchy-channel-set [stable|rc|edge|dev]"; }
fail() { echo "Error: $*" >&2; exit 1; }
confirm_dev() {
cat <<'WARNING'
The dev channel links Omarchy directly to a checkout of the source in ~/omarchy.
It's exclusively intended for developers working on Omarchy itself.
WARNING
gum confirm --default=false "Switch to dev channel?"
}
validate_dev_checkout() {
local checkout="$1"
if [[ -e $checkout && ! -d $checkout/.git ]]; then
fail "$checkout already exists and is not a git checkout."
fi
if [[ -d $checkout/.git && ( ! -d $checkout/bin || ! -d $checkout/default || ! -d $checkout/shell ) ]]; then
fail "$checkout is a git checkout, but it does not look like Omarchy."
fi
}
link_dev_checkout() {
local checkout="$1" required
[[ -d $checkout/.git ]] || git clone https://github.com/omacom/omarchy.git "$checkout"
# Check the destination before changing /etc/omarchy.conf or sudo's path.
# An existing checkout is not pulled automatically and may predate this policy.
for required in bin/omarchy-security-functions bin/omarchy-update bin/omarchy-refresh-pacman default/omarchy/sudo-no-update/sudo; do
if [[ ! -f $checkout/$required || ! -r $checkout/$required ||
( $required != "bin/omarchy-security-functions" && ! -x $checkout/$required ) ]]; then
fail "Update the checkout before switching to dev; missing required update support in $required."
fi
done
omarchy-dev-link "$checkout" --no-reboot
}
# A packaged destination cannot be inspected before its package is installed,
# and a package transaction can replace the running tree with a release that
# predates the command-scoped wrapper. After that, a bare sudo would resolve to
# /usr/bin/sudo and publish a timestamp, and the destination's own updater
# authenticates the same way. Neither may run from a flow that has just run
# user hooks: stop at a consistent point and say how to finish from a fresh
# session.
stop_for_older_destination() {
cat >&2 <<EOF
The destination predates command-scoped sudo, so this switch stops before its update.
Packages are switched. Run 'omarchy update' from a new terminal to finish, then reboot if prompted.
EOF
exit 3
}
wrapper_present() {
[[ -f $OMARCHY_PATH/default/omarchy/sudo-no-update/sudo && -x $OMARCHY_PATH/default/omarchy/sudo-no-update/sudo ]]
}
(( $# > 0 )) || { usage; exit 1; }
dev_checkout=""
channel="$1"
leaving_dev=0
case "$channel" in
stable)
pacman_channel=stable
packages=(omarchy omarchy-settings)
;;
rc)
pacman_channel=rc
packages=(omarchy omarchy-settings)
;;
edge)
pacman_channel=edge
packages=(omarchy-dev omarchy-settings-dev)
;;
dev)
confirm_dev || { echo "Cancelled."; exit 0; }
dev_checkout="$HOME/omarchy"
validate_dev_checkout "$dev_checkout"
pacman_channel=edge
packages=(omarchy-dev omarchy-settings-dev)
;;
*)
echo "Unknown channel: $channel" >&2
usage >&2
exit 1
;;
esac
# A failure past this point leaves the channel switch half-applied, so say how
# to pick it back up rather than dying silently under set -e.
trap 'echo -e "\nThe channel switch did not complete. Review the error above, then rerun: omarchy-channel-set '"$channel"'" >&2' ERR
if [[ -z $dev_checkout && $OMARCHY_PATH != "/usr/share/omarchy" ]]; then
leaving_dev=1
fi
if [[ -n $dev_checkout ]]; then
link_dev_checkout "$dev_checkout"
export OMARCHY_PATH="$dev_checkout"
omarchy_security_enable_no_update_sudo
omarchy-state set reboot-required
fi
OMARCHY_UPDATE_USER_PATH="$user_path" omarchy-refresh-pacman "$pacman_channel"
# Each transaction can have replaced this tree; check before the next sudo.
wrapper_present || stop_for_older_destination
# --ask 4 accepts omarchy <-> omarchy-dev replacement prompts without file overwrites.
omarchy-update-pacman -S --needed --noconfirm --ask 4 "${packages[@]}"
wrapper_present || stop_for_older_destination
if [[ -z $dev_checkout ]]; then
omarchy-dev-unlink --no-reboot
export OMARCHY_PATH=/usr/share/omarchy
if (( leaving_dev )); then
omarchy-state set reboot-required
fi
omarchy_security_enable_no_update_sudo 2>/dev/null || stop_for_older_destination
fi
OMARCHY_UPDATE_USER_PATH="$user_path" "$OMARCHY_PATH/bin/omarchy-update" -y