Add Omarchy 4 upgrade bridge

Introduce a self-contained omarchy-upgrade-to-4 command for legacy installs and remove the 4.0 package-layout transition from the normal migration runner.
This commit is contained in:
Ryan Hughes
2026-06-04 18:37:32 -04:00
parent 75cb4f7195
commit bcf07e424a
5 changed files with 413 additions and 27 deletions
+1 -1
View File
@@ -184,6 +184,6 @@ New migration format:
- Use `$OMARCHY_PATH` to reference the omarchy directory
- Prefer helper commands such as `omarchy-cmd-present`, `omarchy-cmd-missing`, `omarchy-pkg-present`, and `omarchy-pkg-missing`
Omarchy 4.0 intentionally collapses historical migrations into a single package-layout migration. Do not add compatibility migrations for old installer layouts.
Omarchy 4.0 is upgraded through `bin/omarchy-upgrade-to-4`, not through the normal migration runner. Do not add compatibility migrations for old installer layouts; put pre-4 package-layout transition work in the upgrade command instead.
Migrations may use raw `pacman`, `command -v`, or direct config edits when needed for one-off repair work.
+410
View File
@@ -0,0 +1,410 @@
#!/bin/bash
# omarchy:summary=Upgrade a legacy Omarchy install to the package-backed Omarchy 4 layout.
# omarchy:args=[--yes] [--channel stable|rc|edge] [--user USER]
# omarchy:examples=omarchy upgrade to 4
# omarchy:requires-sudo=true
set -euo pipefail
usage() {
cat <<'USAGE'
Usage: omarchy-upgrade-to-4 [--yes] [--channel stable|rc|edge] [--user USER]
Upgrade any pre-4 Omarchy install to the package-backed Omarchy 4 layout.
This script is intentionally self-contained so it can be shipped as a command
in Omarchy 3.8.x or run directly with curl | bash on older systems.
Options:
-y, --yes Do not prompt before upgrading
--channel stable|rc|edge Override detected Omarchy package channel
--user USER User account to refresh after package install
-h, --help Show this help
USAGE
}
log() {
printf '\033[32m==>\033[0m %s\n' "$*"
}
warn() {
printf '\033[33mWarning:\033[0m %s\n' "$*" >&2
}
fail() {
printf '\033[31mError:\033[0m %s\n' "$*" >&2
exit 1
}
normalize_channel() {
case "${1:-}" in
stable|master|main) echo stable ;;
rc) echo rc ;;
edge|dev) echo edge ;;
*) return 1 ;;
esac
}
valid_channel() {
normalize_channel "$1" >/dev/null 2>&1
}
channel_override="${OMARCHY_UPGRADE_CHANNEL:-${OMARCHY_CHANNEL:-${OMARCHY_MIRROR:-}}}"
target_user="${OMARCHY_INSTALL_USER:-}"
yes=0
while (($#)); do
case "$1" in
-y|--yes)
yes=1
shift
;;
--channel)
channel_override="${2:-}"
shift 2
;;
--user)
target_user="${2:-}"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
fail "Unknown option: $1"
;;
esac
done
if ! command -v pacman >/dev/null 2>&1; then
fail "This upgrade is only supported on Arch/Omarchy systems with pacman."
fi
if ! command -v sudo >/dev/null 2>&1 && (( EUID != 0 )); then
fail "sudo is required when not running as root."
fi
if [[ -z $target_user ]]; then
if (( EUID == 0 )); then
target_user="${SUDO_USER:-}"
else
target_user="${USER:-$(id -un)}"
fi
fi
if [[ -z $target_user || $target_user == root ]]; then
fail "Could not determine the non-root Omarchy user. Re-run with --user USER."
fi
if ! getent passwd "$target_user" >/dev/null; then
fail "User '$target_user' does not exist."
fi
target_home=$(getent passwd "$target_user" | cut -d: -f6)
[[ -n $target_home && -d $target_home ]] || fail "Home directory for '$target_user' was not found."
run_as_user() {
if (( EUID == 0 )); then
sudo -u "$target_user" -H "$@"
else
"$@"
fi
}
detect_channel() {
local candidate="${channel_override:-}"
if [[ -n $candidate ]] && valid_channel "$candidate"; then
normalize_channel "$candidate"
return 0
fi
if command -v omarchy-version-channel >/dev/null 2>&1; then
candidate=$(omarchy-version-channel 2>/dev/null | awk '{ print $1 }' || true)
if [[ -n $candidate ]] && valid_channel "$candidate"; then
normalize_channel "$candidate"
return 0
fi
fi
if [[ -f /etc/pacman.conf ]]; then
if grep -q 'https://pkgs\.omarchy\.org/rc/' /etc/pacman.conf; then
echo rc
return 0
elif grep -q 'https://pkgs\.omarchy\.org/edge/' /etc/pacman.conf; then
echo edge
return 0
elif grep -q 'https://pkgs\.omarchy\.org/stable/' /etc/pacman.conf; then
echo stable
return 0
fi
fi
if [[ -f /etc/pacman.d/mirrorlist ]]; then
if grep -q 'https://rc-mirror\.omarchy\.org/' /etc/pacman.d/mirrorlist; then
echo rc
return 0
elif grep -q 'https://mirror\.omarchy\.org/' /etc/pacman.d/mirrorlist; then
echo edge
return 0
elif grep -q 'https://stable-mirror\.omarchy\.org/' /etc/pacman.d/mirrorlist; then
echo stable
return 0
fi
fi
if [[ -d $target_home/.local/share/omarchy/.git ]]; then
if (( EUID == 0 )); then
candidate=$(sudo -u "$target_user" -H git -C "$target_home/.local/share/omarchy" branch --show-current 2>/dev/null || true)
else
candidate=$(git -C "$target_home/.local/share/omarchy" branch --show-current 2>/dev/null || true)
fi
if [[ -n $candidate ]] && valid_channel "$candidate"; then
normalize_channel "$candidate"
return 0
fi
fi
echo stable
}
channel=$(detect_channel)
if ! valid_channel "$channel"; then
fail "Invalid channel '$channel'. Use stable, rc, or edge."
fi
case "$channel" in
stable)
mirror_server='https://stable-mirror.omarchy.org/$repo/os/$arch'
package_server='https://pkgs.omarchy.org/stable/$arch'
;;
rc)
mirror_server='https://rc-mirror.omarchy.org/$repo/os/$arch'
package_server='https://pkgs.omarchy.org/rc/$arch'
;;
edge)
mirror_server='https://mirror.omarchy.org/$repo/os/$arch'
package_server='https://pkgs.omarchy.org/edge/$arch'
;;
esac
if (( ! yes )); then
cat <<EOF
This will upgrade Omarchy for user '$target_user' to Omarchy 4 using the '$channel' channel.
It will install the package-backed Omarchy 4 stack, overwrite files previously
written by the legacy installer, remove retired default packages, and refresh
system/user setup. A reboot is strongly recommended afterward.
EOF
if [[ -r /dev/tty ]]; then
printf '\nContinue? [y/N] ' >/dev/tty
read -r answer </dev/tty
[[ $answer =~ ^[Yy]$ ]] || exit 0
else
fail "No TTY available for confirmation. Re-run with --yes to proceed non-interactively."
fi
fi
if (( EUID != 0 )); then
sudo -v
while true; do sudo -n true; sleep 60; done 2>/dev/null &
sudo_keepalive_pid=$!
trap 'kill "$sudo_keepalive_pid" 2>/dev/null || true' EXIT
fi
backup_suffix=$(date +%Y%m%d%H%M%S)
configure_pacman_channel() {
log "Configuring Omarchy pacman repositories ($channel)"
sudo install -d -m 0755 /etc/pacman.d
if [[ -f /etc/pacman.d/mirrorlist ]]; then
sudo cp -f /etc/pacman.d/mirrorlist "/etc/pacman.d/mirrorlist.omarchy-upgrade-to-4.$backup_suffix.bak"
fi
printf 'Server = %s\n' "$mirror_server" | sudo tee /etc/pacman.d/mirrorlist >/dev/null
if [[ ! -f /etc/pacman.conf ]]; then
fail "/etc/pacman.conf does not exist."
fi
sudo cp -f /etc/pacman.conf "/etc/pacman.conf.omarchy-upgrade-to-4.$backup_suffix.bak"
tmp=$(mktemp)
awk -v server="$package_server" '
BEGIN { in_omarchy = 0; wrote = 0 }
/^\[omarchy\]$/ {
if (!wrote) {
print "[omarchy]"
print "SigLevel = Optional TrustAll"
print "Server = " server
wrote = 1
}
in_omarchy = 1
next
}
/^\[/ && in_omarchy { in_omarchy = 0 }
!in_omarchy { print }
END {
if (!wrote) {
print ""
print "[omarchy]"
print "SigLevel = Optional TrustAll"
print "Server = " server
}
}
' /etc/pacman.conf >"$tmp"
sudo install -m 0644 "$tmp" /etc/pacman.conf
rm -f "$tmp"
}
install_keyrings() {
log "Refreshing package databases and keyrings"
sudo pacman -Sy --noconfirm archlinux-keyring omarchy-keyring || {
warn "Keyring package install failed; attempting to trust the Omarchy packaging key directly."
sudo pacman-key --recv-keys 40DFB630FF42BCFFB047046CF0134EE680CAC571 --keyserver keys.openpgp.org || true
sudo pacman-key --lsign-key 40DFB630FF42BCFFB047046CF0134EE680CAC571 || true
sudo pacman -Sy --noconfirm archlinux-keyring omarchy-keyring
}
}
remove_legacy_installer_package() {
if pacman -Q omarchy-installer >/dev/null 2>&1; then
log "Removing legacy omarchy-installer package"
# The pre-4 omarchy meta package may depend on omarchy-installer. Ignore
# that edge temporarily; the next transaction installs the Omarchy 4 meta.
sudo pacman -Rdd --noconfirm omarchy-installer
fi
}
install_omarchy_4_packages() {
local core_packages=(
omarchy-keyring
omarchy-settings
omarchy-limine
omarchy-nvim
omarchy
)
log "Installing Omarchy 4 packages"
sudo pacman -Syu --needed --noconfirm --overwrite='*' "${core_packages[@]}"
if [[ -f /usr/share/omarchy/install/omarchy-base.packages ]]; then
log "Installing Omarchy 4 default package set"
mapfile -t base_packages < <(sed -e 's/[[:space:]]*#.*$//' -e '/^[[:space:]]*$/d' /usr/share/omarchy/install/omarchy-base.packages)
if (( ${#base_packages[@]} )); then
sudo pacman -S --needed --noconfirm --overwrite='*' "${base_packages[@]}"
fi
else
warn "/usr/share/omarchy/install/omarchy-base.packages was not found; skipping default package set install."
fi
}
remove_retired_default_packages() {
local retired_packages=(
1password-beta
1password-cli
alacritty
bluetui
claude-code
github-cli
hypridle
hyprlock
impala
iwd
mako
omarchy-walker
playerctl
polkit-gnome
signal-desktop
spotify
swaybg
swayosd
waybar
wiremix
)
log "Removing packages retired from the Omarchy 4 default install"
local pkg
for pkg in "${retired_packages[@]}"; do
if pacman -Q "$pkg" >/dev/null 2>&1; then
sudo pacman -Rns --noconfirm "$pkg" || warn "Could not remove retired package '$pkg'; leaving it installed."
fi
done
}
cleanup_legacy_user_paths() {
log "Cleaning legacy user command shims"
run_as_user env TARGET_HOME="$target_home" bash <<'USER_CLEANUP'
set -euo pipefail
local_bin="$TARGET_HOME/.local/bin"
legacy_root="$TARGET_HOME/.local/share/omarchy/bin/"
[[ -d $local_bin ]] || exit 0
shopt -s nullglob
for link in "$local_bin"/omarchy*; do
[[ -L $link ]] || continue
target=$(readlink "$link" || true)
case "$target" in
"$legacy_root"*|../share/omarchy/bin/*|*.local/share/omarchy/bin/*)
rm -f "$link"
;;
esac
done
USER_CLEANUP
}
cleanup_retired_services() {
log "Disabling services retired by Omarchy 4"
# Do not stop iwd mid-upgrade; disabling is enough and avoids dropping a
# running Wi-Fi connection before the user can reboot into NetworkManager.
sudo systemctl disable iwd.service >/dev/null 2>&1 || true
run_as_user systemctl --user disable \
omarchy-battery-monitor.timer \
swayosd-server.service \
elephant.service \
app-walker@autostart.service \
>/dev/null 2>&1 || true
}
run_omarchy_4_setup() {
log "Running Omarchy 4 system setup"
[[ -x /usr/bin/omarchy-setup-system ]] || fail "/usr/bin/omarchy-setup-system was not installed."
sudo env \
OMARCHY_PATH=/usr/share/omarchy \
OMARCHY_INSTALL=/usr/share/omarchy/install \
OMARCHY_INSTALL_USER="$target_user" \
OMARCHY_MIRROR="$channel" \
/usr/bin/omarchy-setup-system --install-user "$target_user" --upgrade
log "Running Omarchy 4 user setup"
[[ -x /usr/bin/omarchy-setup-user ]] || fail "/usr/bin/omarchy-setup-user was not installed."
run_as_user env \
OMARCHY_PATH=/usr/share/omarchy \
OMARCHY_INSTALL=/usr/share/omarchy/install \
OMARCHY_SETUP_CONTEXT=runtime \
/usr/bin/omarchy-setup-user --force
if [[ -x /usr/bin/omarchy-refresh-applications ]]; then
run_as_user env OMARCHY_PATH=/usr/share/omarchy /usr/bin/omarchy-refresh-applications
fi
}
configure_pacman_channel
install_keyrings
remove_legacy_installer_package
install_omarchy_4_packages
cleanup_legacy_user_paths
run_omarchy_4_setup
cleanup_retired_services
remove_retired_default_packages
cat <<EOF
Omarchy 4 upgrade complete.
Please reboot to finish switching to the package-backed Omarchy 4 session.
EOF
View File
-24
View File
@@ -1,24 +0,0 @@
echo "Migrate to Omarchy 4.0 package layout"
# Omarchy 4.0 collapses the old git/online-installer layout into the packaged
# runtime layout. Packages own most static system files; these commands refresh
# the machine-specific and user-specific pieces in place.
channel=$(omarchy-version-channel 2>/dev/null | awk '{print $1}')
case "$channel" in
stable|rc|edge) ;;
*) channel=stable ;;
esac
# Fold post-4.0 pre-release migration work into the single 4.0 migration.
rm -f "$HOME/.local/bin/playwright-cli"
omarchy-pkg-drop claude-code github-cli
sudo env \
OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}" \
OMARCHY_INSTALL="${OMARCHY_INSTALL:-${OMARCHY_PATH:-/usr/share/omarchy}/install}" \
OMARCHY_INSTALL_USER="$USER" \
OMARCHY_MIRROR="${OMARCHY_MIRROR:-$channel}" \
/usr/bin/omarchy-setup-system --install-user "$USER" --upgrade
OMARCHY_SETUP_CONTEXT=runtime omarchy-setup-user --force
omarchy-refresh-applications
+2 -2
View File
@@ -88,5 +88,5 @@ PY
pass "default bar widget ids resolve to manifests and entry points"
mapfile -t migrations < <(find "$ROOT/migrations" -maxdepth 1 -type f -name '*.sh' -printf '%f\n' | sort)
[[ ${#migrations[@]} -eq 1 && ${migrations[0]} == "1780000000_4_0.sh" ]] || fail "historical migrations are collapsed"
pass "historical migrations are collapsed into the 4.0 migration"
[[ ${#migrations[@]} -eq 0 ]] || fail "4.0 upgrade is not modeled as a migration"
pass "4.0 upgrade is handled outside the migration runner"