Remove hot link / unlink bits

This commit is contained in:
Ryan Hughes
2026-06-08 12:03:40 -04:00
parent 38bb61b685
commit 64581ec289
9 changed files with 130 additions and 150 deletions
+23 -42
View File
@@ -1,26 +1,24 @@
#!/bin/bash #!/bin/bash
# omarchy:summary=Point Omarchy at a local checkout for live editing # omarchy:summary=Point Omarchy at a local checkout after reboot
# omarchy:group=dev # omarchy:group=dev
# omarchy:args=<path-to-checkout> # omarchy:args=<path-to-checkout>
# omarchy:examples=omarchy dev link ~/Work/omarchy/omarchy-installer # omarchy:examples=omarchy dev link ~/Work/omarchy/omarchy-installer
set -euo pipefail set -euo pipefail
# Sudo wipes HYPRLAND_INSTANCE_SIGNATURE, so the live-session refresh below if (( EUID == 0 )); then
# can't reach hyprctl. Run as user; we sudo internally for the conf write.
if [[ $EUID -eq 0 ]]; then
echo "Error: run omarchy-dev-link as your user, not under sudo." >&2 echo "Error: run omarchy-dev-link as your user, not under sudo." >&2
exit 1 exit 1
fi fi
if [[ $# -ne 1 || $1 == "-h" || $1 == "--help" ]]; then if (( $# != 1 )) || [[ $1 == "-h" || $1 == "--help" ]]; then
cat <<USAGE cat <<USAGE
Usage: omarchy dev link <path-to-checkout> Usage: omarchy dev link <path-to-checkout>
Writes /etc/omarchy.conf so OMARCHY_PATH resolves to <path-to-checkout> Writes /etc/omarchy.conf so OMARCHY_PATH resolves to <path-to-checkout>
in all new shells, the Hyprland session, and Quickshell. Restarts after reboot. This intentionally does not rewrite the running Hyprland,
omarchy-shell and reloads hyprctl so changes take effect immediately. systemd, shell, or app-launcher environment; reboot to make every layer agree.
Affects only \$OMARCHY_PATH-resolved trees: bin/, default/, shell/, Affects only \$OMARCHY_PATH-resolved trees: bin/, default/, shell/,
themes/, applications/, config/. Files installed at fixed system paths themes/, applications/, config/. Files installed at fixed system paths
@@ -31,50 +29,33 @@ USAGE
exit 0 exit 0
fi fi
omarchy_conf_quote() {
local value="$1"
value=${value//\\/\\\\}
value=${value//\"/\\\"}
value=${value//\$/\\\$}
value=${value//\`/\\\`}
printf '"%s"' "$value"
}
target=$(realpath -e "$1" 2>/dev/null) || { target=$(realpath -e "$1" 2>/dev/null) || {
echo "Error: path does not exist: $1" >&2 echo "Error: path does not exist: $1" >&2
exit 1 exit 1
} }
for required in bin default shell; do for required in bin default shell; do
if [[ ! -d "$target/$required" ]]; then if [[ ! -d $target/$required ]]; then
echo "Warning: $target/$required not found — does this look like an Omarchy source checkout?" >&2 echo "Warning: $target/$required not found — does this look like an Omarchy source checkout?" >&2
fi fi
done done
# Strip any previous dev-link's bin/ from PATH before adding the new one, {
# so repeated link calls don't accumulate stale entries. printf 'export OMARCHY_PATH='
prior_target="" omarchy_conf_quote "$target"
if [[ -f /etc/omarchy.conf ]]; then printf '\n'
prior_target=$(sed -n 's/^[[:space:]]*export[[:space:]]\+OMARCHY_PATH="\?\([^"]*\)"\?/\1/p' /etc/omarchy.conf | tail -1) } | sudo tee /etc/omarchy.conf >/dev/null
fi
echo "Pointing Omarchy at $target"
printf 'export OMARCHY_PATH="%s"\n' "$target" | sudo tee /etc/omarchy.conf >/dev/null
export OMARCHY_PATH="$target"
if [[ -n $prior_target && $prior_target != "$target" ]]; then
PATH=$(printf '%s' "$PATH" | tr ':' '\n' | grep -vFx "$prior_target/bin" | paste -sd:)
fi
export PATH="$target/bin:$PATH"
if command -v hyprctl >/dev/null 2>&1 && hyprctl version &>/dev/null; then
hyprctl setenv OMARCHY_PATH "$target" >/dev/null
hyprctl setenv PATH "$PATH" >/dev/null
echo " Updated Hyprland session env."
systemctl --user import-environment OMARCHY_PATH PATH 2>/dev/null || true
echo " Updated systemd --user env."
if pgrep -x quickshell >/dev/null 2>&1; then
omarchy-restart-shell
echo " Restarted omarchy-shell."
fi
hyprctl reload >/dev/null
echo " Reloaded Hyprland config."
fi
echo "Pointed Omarchy at $target"
echo echo
echo "Done. Open a new shell (or restart existing ones) to pick up the new" echo "Reboot to activate the linked checkout in Hyprland, app launchers, and new shells."
echo "OMARCHY_PATH. Run 'omarchy dev unlink' to restore the package install." echo "Run 'omarchy dev unlink' and reboot to restore the package install."
+6 -15
View File
@@ -3,6 +3,8 @@
# omarchy:summary=Show the current Omarchy dev-link state # omarchy:summary=Show the current Omarchy dev-link state
# omarchy:group=dev # omarchy:group=dev
set -euo pipefail
default_target="/usr/share/omarchy" default_target="/usr/share/omarchy"
configured="$default_target" configured="$default_target"
conf_present=0 conf_present=0
@@ -22,8 +24,9 @@ if [[ -f /etc/omarchy.conf ]]; then
fi fi
if (( linked )); then if (( linked )); then
echo "dev-link: ACTIVE" echo "dev-link: configured"
echo " /etc/omarchy.conf -> OMARCHY_PATH=$configured" echo " /etc/omarchy.conf -> OMARCHY_PATH=$configured"
echo " status: reboot required before all session layers use this checkout"
else else
echo "dev-link: inactive" echo "dev-link: inactive"
if (( conf_present )); then if (( conf_present )); then
@@ -33,19 +36,7 @@ fi
echo " current shell: OMARCHY_PATH=${OMARCHY_PATH:-<unset>}" echo " current shell: OMARCHY_PATH=${OMARCHY_PATH:-<unset>}"
if (( linked )) && [[ ${OMARCHY_PATH:-} != "$configured" ]]; then if [[ ${OMARCHY_PATH:-$default_target} != "$configured" ]]; then
echo echo
echo "Note: this shell predates dev-link. Open a new shell to pick up the change," echo "Note: the running session does not match /etc/omarchy.conf. Reboot to settle it."
echo "or 'export OMARCHY_PATH=$configured' to update just this shell."
elif (( ! linked )) && [[ ${OMARCHY_PATH:-$default_target} != "$default_target" ]]; then
echo
echo "Note: no dev-link is configured, but this shell still has a stale OMARCHY_PATH."
echo "Open a new shell or run 'export OMARCHY_PATH=$default_target'."
fi
if command -v hyprctl >/dev/null 2>&1 && hyprctl version &>/dev/null; then
hypr_path=$(hyprctl getoption -j env 2>/dev/null | sed -n 's/.*OMARCHY_PATH=\([^"]*\).*/\1/p' | head -1)
if [[ -n "$hypr_path" ]]; then
echo " hyprland session: OMARCHY_PATH=$hypr_path"
fi
fi fi
+7 -59
View File
@@ -1,11 +1,11 @@
#!/bin/bash #!/bin/bash
# omarchy:summary=Restore Omarchy to the package install (undo omarchy-dev-link) # omarchy:summary=Restore Omarchy to the package install after reboot
# omarchy:group=dev # omarchy:group=dev
set -euo pipefail set -euo pipefail
if [[ $EUID -eq 0 ]]; then if (( EUID == 0 )); then
echo "Error: run omarchy-dev-unlink as your user, not under sudo." >&2 echo "Error: run omarchy-dev-unlink as your user, not under sudo." >&2
exit 1 exit 1
fi fi
@@ -14,69 +14,17 @@ if [[ ${1:-} == "-h" || ${1:-} == "--help" ]]; then
cat <<USAGE cat <<USAGE
Usage: omarchy dev unlink Usage: omarchy dev unlink
Writes /etc/omarchy.conf defensively so OMARCHY_PATH resolves to Writes /etc/omarchy.conf so OMARCHY_PATH resolves to /usr/share/omarchy
/usr/share/omarchy (the package install), even from stale session env. after reboot. This intentionally does not rewrite the running Hyprland,
Updates Hyprland/systemd session env and restarts omarchy-shell so live systemd, shell, or app-launcher environment; reboot to make every layer agree.
state matches.
USAGE USAGE
exit 0 exit 0
fi fi
default_target="/usr/share/omarchy" default_target="/usr/share/omarchy"
linked=0
prior_target=""
if [[ -f /etc/omarchy.conf ]]; then
# Capture the path dev-link wrote BEFORE we reset the conf, so we know
# which bin/ to strip from PATH.
prior_target=$(sed -n 's/^[[:space:]]*export[[:space:]]\+OMARCHY_PATH="\?\([^"]*\)"\?/\1/p' /etc/omarchy.conf | tail -1)
if [[ $prior_target != "$default_target" ]]; then
linked=1
echo "Unlinking Omarchy from ${prior_target:-<empty>}"
else
echo "/etc/omarchy.conf already points at $default_target."
fi
elif [[ ${OMARCHY_PATH:-$default_target} != "$default_target" ]]; then
prior_target="$OMARCHY_PATH"
echo "Not currently linked: /etc/omarchy.conf is absent."
echo "This shell still has OMARCHY_PATH=$OMARCHY_PATH; writing the default guard."
else
echo "Not currently linked. Writing the default OMARCHY_PATH guard."
fi
printf 'export OMARCHY_PATH="%s"\n' "$default_target" | sudo tee /etc/omarchy.conf >/dev/null printf 'export OMARCHY_PATH="%s"\n' "$default_target" | sudo tee /etc/omarchy.conf >/dev/null
echo "Set /etc/omarchy.conf -> OMARCHY_PATH=$default_target" echo "Set /etc/omarchy.conf -> OMARCHY_PATH=$default_target"
export OMARCHY_PATH="$default_target"
if [[ -n $prior_target && $prior_target != "$default_target" ]]; then
PATH=$(printf '%s' "$PATH" | tr ':' '\n' | awk -v drop="$prior_target/bin" '$0 != drop' | paste -sd:)
export PATH
fi
if command -v systemctl >/dev/null 2>&1; then
if systemctl --user import-environment OMARCHY_PATH PATH 2>/dev/null; then
echo " Updated systemd --user env."
fi
fi
if command -v hyprctl >/dev/null 2>&1 && hyprctl version &>/dev/null; then
hyprctl setenv OMARCHY_PATH "$default_target" >/dev/null
hyprctl setenv PATH "$PATH" >/dev/null
echo " Updated Hyprland session env."
if pgrep -x quickshell >/dev/null 2>&1; then
omarchy-restart-shell
echo " Restarted omarchy-shell."
fi
hyprctl reload >/dev/null
echo " Reloaded Hyprland config."
fi
echo echo
if (( linked )) || [[ -n $prior_target ]]; then echo "Reboot to restore the package install in Hyprland, app launchers, and new shells."
echo "Done. Existing shells still have the old OMARCHY_PATH until restarted."
echo "For this shell, run: export OMARCHY_PATH=$default_target"
else
echo "Done."
fi
+2 -13
View File
@@ -1,18 +1,7 @@
-- Learn how to configure Hyprland: https://wiki.hypr.land/Configuring/Start/ -- Learn how to configure Hyprland: https://wiki.hypr.land/Configuring/Start/
-- Drop cached omarchy modules so hyprctl reload re-reads them from disk. -- Omarchy's bootstrap keeps path setup out of this user config.
for k in pairs(package.loaded) do dofile((os.getenv("OMARCHY_PATH") or "/usr/share/omarchy") .. "/default/hypr/bootstrap.lua")
if k:match("^default%.hypr") or k:match("^hypr%.") then
package.loaded[k] = nil
end
end
-- Load user modules from ~/.config and Omarchy defaults from $OMARCHY_PATH.
package.path = os.getenv("HOME")
.. "/.config/?.lua;"
.. (os.getenv("OMARCHY_PATH") or "/usr/share/omarchy")
.. "/?.lua;"
.. package.path
-- All Omarchy default setups -- All Omarchy default setups
require("default.hypr.omarchy") require("default.hypr.omarchy")
+2 -3
View File
@@ -17,12 +17,11 @@ export OMARCHY_PATH
# Only prepend in dev-link mode. On a production install, the binaries are # Only prepend in dev-link mode. On a production install, the binaries are
# already on PATH as /usr/bin/omarchy-* via the omarchy package — prepending # already on PATH as /usr/bin/omarchy-* via the omarchy package — prepending
# /usr/share/omarchy/bin would just be noise. omarchy-dev-unlink strips its # /usr/share/omarchy/bin would just be noise.
# checkout bin/ from the active PATH explicitly, so we don't dedupe here.
if [ "$OMARCHY_PATH" != /usr/share/omarchy ]; then if [ "$OMARCHY_PATH" != /usr/share/omarchy ]; then
case ":$PATH:" in case ":$PATH:" in
*":${OMARCHY_PATH%/}/bin:"*) ;; *":${OMARCHY_PATH%/}/bin:"*) ;;
*) PATH="${OMARCHY_PATH%/}/bin:$PATH" ;; *) PATH="${OMARCHY_PATH%/}/bin${PATH:+:$PATH}" ;;
esac esac
export PATH export PATH
fi fi
+8
View File
@@ -0,0 +1,8 @@
-- Hyprland bootstrap for Omarchy's Lua module path.
-- Load user modules from ~/.config and Omarchy defaults from $OMARCHY_PATH.
package.path = os.getenv("HOME")
.. "/.config/?.lua;"
.. (os.getenv("OMARCHY_PATH") or "/usr/share/omarchy")
.. "/?.lua;"
.. package.path
+1 -18
View File
@@ -4,26 +4,9 @@
local home = os.getenv("HOME") local home = os.getenv("HOME")
-- /etc/omarchy.conf wins over process env so dev-link/unlink survives stale sessions.
local function read_dev_link_omarchy_path()
local f = io.open("/etc/omarchy.conf", "r")
if not f then return nil end
local value
for line in f:lines() do
local v = line:match('^%s*export%s+OMARCHY_PATH=%s*"?([^"\n]+)"?')
if v and #v > 0 then value = v end
end
f:close()
return value
end
local omarchy_path = read_dev_link_omarchy_path()
or os.getenv("OMARCHY_PATH")
or "/usr/share/omarchy"
return { return {
home = home, home = home,
config_home = os.getenv("XDG_CONFIG_HOME") or (home .. "/.config"), config_home = os.getenv("XDG_CONFIG_HOME") or (home .. "/.config"),
state_home = os.getenv("XDG_STATE_HOME") or (home .. "/.local/state"), state_home = os.getenv("XDG_STATE_HOME") or (home .. "/.local/state"),
omarchy_path = omarchy_path, omarchy_path = os.getenv("OMARCHY_PATH") or "/usr/share/omarchy",
} }
+5
View File
@@ -140,6 +140,11 @@ if errors:
PY PY
pass "package-owned defaults live outside config" pass "package-owned defaults live outside config"
grep -F 'dofile((os.getenv("OMARCHY_PATH") or "/usr/share/omarchy") .. "/default/hypr/bootstrap.lua")' "$ROOT/config/hypr/hyprland.lua" >/dev/null
grep -F 'require("default.hypr.omarchy")' "$ROOT/config/hypr/hyprland.lua" >/dev/null
grep -F 'package.path = os.getenv("HOME")' "$ROOT/default/hypr/bootstrap.lua" >/dev/null
pass "Hyprland user entrypoint keeps only package path bootstrap in defaults"
TMPDIR=$(mktemp -d) TMPDIR=$(mktemp -d)
mkdir -p "$TMPDIR/home/.config/omarchy" mkdir -p "$TMPDIR/home/.config/omarchy"
+76
View File
@@ -0,0 +1,76 @@
#!/bin/bash
set -euo pipefail
source "$(dirname "$0")/base-test.sh"
run_bootstrap() {
local shell_bin="$1"
local bootstrap="$2"
local home="$3"
local path_value="$4"
HOME="$home" PATH="$path_value" "$shell_bin" -c '
. "$1"
printf "%s\n%s\n" "$OMARCHY_PATH" "$PATH"
' sh "$bootstrap"
}
assert_path_first() {
local path_value="$1"
local entry="$2"
local description="$3"
[[ ${path_value%%:*} == "$entry" ]] || fail "$description" "expected first PATH entry: $entry\nactual PATH: $path_value"
pass "$description"
}
assert_path_present() {
local path_value="$1"
local entry="$2"
local description="$3"
case ":$path_value:" in
*":$entry:"*) pass "$description" ;;
*) fail "$description" "PATH does not contain $entry in $path_value" ;;
esac
}
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
home="$tmpdir/home"
mkdir -p "$tmpdir/active/bin" "$tmpdir/unrelated/bin"
# Test against a copy so the test controls /etc/omarchy.conf without mutating the host.
bootstrap="$tmpdir/env-bootstrap"
sed "s#/etc/omarchy.conf#$tmpdir/omarchy.conf#g" "$ROOT/default/bash/env-bootstrap" >"$bootstrap"
printf 'export OMARCHY_PATH="/usr/share/omarchy"\n' >"$tmpdir/omarchy.conf"
mapfile -t default_result < <(run_bootstrap bash "$bootstrap" "$home" "$tmpdir/unrelated/bin:/usr/bin")
default_path=${default_result[1]}
[[ ${default_result[0]} == /usr/share/omarchy ]] || fail "env-bootstrap resolves default OMARCHY_PATH" "actual: ${default_result[0]}"
pass "env-bootstrap resolves default OMARCHY_PATH"
assert_path_present "$default_path" "$tmpdir/unrelated/bin" "env-bootstrap preserves PATH entries in default mode"
printf 'export OMARCHY_PATH="%s"\n' "$tmpdir/active" >"$tmpdir/omarchy.conf"
mapfile -t linked_result < <(run_bootstrap bash "$bootstrap" "$home" "$tmpdir/unrelated/bin:/usr/bin")
linked_path=${linked_result[1]}
[[ ${linked_result[0]} == "$tmpdir/active" ]] || fail "env-bootstrap resolves linked OMARCHY_PATH" "actual: ${linked_result[0]}"
pass "env-bootstrap resolves linked OMARCHY_PATH"
assert_path_first "$linked_path" "$tmpdir/active/bin" "env-bootstrap prepends active checkout bin in linked mode"
assert_path_present "$linked_path" "$tmpdir/unrelated/bin" "env-bootstrap preserves unrelated PATH entries in linked mode"
mapfile -t linked_duplicate_result < <(run_bootstrap bash "$bootstrap" "$home" "$tmpdir/active/bin:/usr/bin")
linked_duplicate_path=${linked_duplicate_result[1]}
[[ $linked_duplicate_path == "$tmpdir/active/bin:/usr/bin" ]] || fail "env-bootstrap does not duplicate active checkout bin" "actual PATH: $linked_duplicate_path"
pass "env-bootstrap does not duplicate active checkout bin"
if command -v zsh >/dev/null 2>&1; then
mapfile -t zsh_result < <(run_bootstrap zsh "$bootstrap" "$home" "$tmpdir/unrelated/bin:/usr/bin")
zsh_path=${zsh_result[1]}
assert_path_first "$zsh_path" "$tmpdir/active/bin" "env-bootstrap works when sourced by zsh"
assert_path_present "$zsh_path" "$tmpdir/unrelated/bin" "env-bootstrap zsh mode preserves unrelated PATH entries"
fi