Split user defaults into skel seed, finalize, and resync

Reorganizes Omarchy 4 around three layers for populating $HOME:

  Seed:     omarchy-settings ships defaults to /etc/skel; useradd -m
            copies them on user creation
  Finalize: omarchy-finalize-user (renamed from omarchy-setup-user)
            handles only the runtime tweaks /etc/skel can't do — skill
            symlinks, xdg-user-dirs, default browser/mailto, vconsole→hypr
            keyboard sync, and install/user/all.sh
  Resync:   omarchy-reinstall-configs is the explicit, destructive
            resync of /etc/skel into an existing user's $HOME

Package-owned files move out of config/ into default/, where the
omarchy-settings PKGBUILD installs them to real system paths:

  config/environment.d/fcitx.conf            -> /usr/lib/environment.d/
  config/fontconfig/fonts.conf               -> /usr/share/fontconfig/conf.avail/
  config/mimeapps.list                       -> /usr/share/applications/
  config/omarchy.ttf                         -> /usr/share/fonts/omarchy/
  config/systemd/user/*.service              -> /usr/lib/systemd/user/
  config/uwsm/default                        -> /usr/share/omarchy/default/uwsm/
  config/uwsm/env                            -> /usr/share/uwsm/env.d/10-omarchy
  config/xdg-terminals.list                  -> /usr/share/xdg-terminal-exec/

omarchy-upgrade-to-4 grows a 'retire' action (renamed from 'move' to
clarify nothing is copied — the system path is owned by the new package
once the user's hash-matched ~/.config copy is removed). Mismatched
copies are kept as backups so user overrides survive the upgrade.

Other simplifications:

  - Single env bootstrap at default/bash/env-bootstrap sourced by
    /etc/profile.d/omarchy.sh, /etc/skel/.bashrc,
    /usr/share/uwsm/env.d/10-omarchy, and default/bash/envs. PATH
    prepend only in dev-link mode (production uses /usr/bin/omarchy-*).
  - omarchy-refresh-config reads from /etc/skel/.config so refresh
    means 'snap to skel'.
  - omarchy-reinstall-configs collapses to 'cp -af /etc/skel/. ~/'
    plus limine/plymouth/nvim refresh.
  - omarchy-font-set uses awk against our own 30-omarchy.conf instead
    of xmlstarlet; xmlstarlet dropped from omarchy-base.packages.
  - Defer user systemd enables (bt-agent, sleep-lock,
    recover-internal-monitor) to first-run via
    install/user/first-run/enable-user-units.sh; delete
    omarchy-user-systemctl-enable and the per-hardware install
    scripts that called it.
  - Wireplumber bluetooth-a2dp-autoconnect.conf moves to config/ so
    /etc/skel ships it; install/user/hardware/bluetooth.sh deleted.
  - Default terminal switched to foot.desktop.
  - docs/file-layout.md documents the three-layer model and the
    build-time repo→path map.
This commit is contained in:
Ryan Hughes
2026-06-04 18:38:25 -04:00
parent 85da6af886
commit babfafa5e9
39 changed files with 823 additions and 205 deletions
+2 -2
View File
@@ -78,7 +78,7 @@ The ISO owns installation orchestration. This repo ships target-side setup comma
- `bin/omarchy-setup-system` runs root-owned system setup during ISO finalization.
- `bin/omarchy-setup-hardware` runs idempotent hardware-specific setup and is called by `omarchy-setup-system`.
- `bin/omarchy-setup-user` runs user setup for the initial user and future users.
- `bin/omarchy-finalize-user` runs the per-user runtime finalization (skill symlinks, xdg-user-dirs, mime defaults, `install/user/all.sh`). Shipped user defaults are seeded by `/etc/skel` from `omarchy-settings`, not by this command. `bin/omarchy-reinstall-configs` is the explicit destructive resync of those defaults into an existing user's `$HOME`.
- leaf scripts under `install/` are sourced by `run_logged $OMARCHY_INSTALL/path/to/script.sh` and intentionally do not have shebangs.
- avoid `exit` in sourced setup scripts unless intentionally aborting setup.
- use `$OMARCHY_INSTALL` and `$OMARCHY_PATH` instead of hard-coded Omarchy paths.
@@ -171,7 +171,7 @@ To copy a default config to user config with automatic backup:
omarchy-refresh-config hypr/hyprlock.conf
```
This copies `$OMARCHY_PATH/config/hypr/hyprlock.conf` to `~/.config/hypr/hyprlock.conf`.
This copies `/etc/skel/.config/hypr/hyprlock.conf` to `~/.config/hypr/hyprlock.conf`.
# Migrations
+1
View File
@@ -39,6 +39,7 @@ GROUP_DESCRIPTIONS[clipboard]="Clipboard helpers"
GROUP_DESCRIPTIONS[cmd]="Command and shortcut helpers"
GROUP_DESCRIPTIONS[config]="System configuration helpers"
GROUP_DESCRIPTIONS[debug]="Diagnostics and support logs"
GROUP_DESCRIPTIONS[finalize]="Finalize user setup"
GROUP_DESCRIPTIONS[default]="Default application selection"
GROUP_DESCRIPTIONS[dev]="Omarchy development tools"
GROUP_DESCRIPTIONS[dns]="DNS resolver configuration"
+2 -1
View File
@@ -5,7 +5,8 @@
# omarchy:examples=omarchy default terminal ghostty | omarchy default terminal kitty
if (($# == 0)); then
desktop_id=$(grep -vE '^($|#)' ~/.config/xdg-terminals.list 2>/dev/null | head -n 1)
desktop_id=$(xdg-terminal-exec --print-id 2>/dev/null || true)
desktop_id=${desktop_id%%:*}
case "$desktop_id" in
Alacritty.desktop) echo "alacritty" ;;
foot.desktop) echo "foot" ;;
@@ -1,29 +1,31 @@
#!/bin/bash
# omarchy:summary=Set up Omarchy for the current user (~/.config, ~/.local, etc.)
# omarchy:group=setup
# omarchy:examples=omarchy setup user | omarchy setup user --force
# omarchy:summary=Finalize Omarchy user setup (runtime tweaks /etc/skel can't do)
# omarchy:group=finalize
# omarchy:examples=omarchy finalize user | omarchy finalize user --force
set -euo pipefail
usage() {
cat <<USAGE
Usage: omarchy setup user [--force] [--first-install]
Usage: omarchy finalize user [--force] [--first-install]
Idempotent user-level setup: copies/symlinks defaults from \$OMARCHY_PATH
into \$HOME, installs user services/session defaults, refreshes app launchers,
and applies user-scoped hardware/session tweaks.
Runs the per-user setup steps that /etc/skel can't seed:
dev-aware skill symlinks, xdg-user-dirs + gtk bookmarks (need \$HOME),
vconsole→hypr keyboard sync, default browser/mailto, and install/user/all.sh.
For shipped configs see /etc/skel (new users) and omarchy-reinstall-configs
(existing users explicitly resyncing).
--first-install is used by the ISO in the target chroot. It marks shipped
migrations complete for the freshly-created user and enables the first graphical
login one-shot.
migrations complete for the freshly-created user.
Idempotency marker: ~/.local/state/omarchy/setup-user.done
Idempotency marker: ~/.local/state/omarchy/finalize-user.done
USAGE
}
if (( EUID == 0 )); then
echo "Error: run omarchy-setup-user as the user being configured, not as root." >&2
echo "Error: run omarchy-finalize-user as the user being configured, not as root." >&2
exit 1
fi
@@ -53,11 +55,11 @@ while (($#)); do
done
state_dir="$HOME/.local/state/omarchy"
marker="$state_dir/setup-user.done"
marker="$state_dir/finalize-user.done"
mkdir -p "$state_dir"
if [[ -f $marker && $force -eq 0 ]]; then
echo "User setup already complete (rerun with --force to refresh)."
echo "User finalization already complete (rerun with --force to refresh)."
exit 0
fi
@@ -79,60 +81,14 @@ else
}
fi
omarchy_user_systemctl_enable() {
local unit="$1" unit_path target
local -a targets=()
if systemctl --user enable "$unit" >/dev/null 2>&1; then
return 0
fi
# ISO chroot setup has no running user systemd manager. Enabling a user unit
# is just creating the [Install] WantedBy symlinks, so do that directly.
unit_path="$HOME/.config/systemd/user/$unit"
if [[ -f $unit_path ]]; then
mapfile -t targets < <(awk -F= '
/^\[Install\]/ { in_install=1; next }
/^\[/ { in_install=0 }
in_install && $1 == "WantedBy" {
n=split($2, values, /[[:space:]]+/)
for (i = 1; i <= n; i++) if (values[i] != "") print values[i]
}
' "$unit_path")
fi
((${#targets[@]})) || targets=(default.target)
for target in "${targets[@]}"; do
mkdir -p "$HOME/.config/systemd/user/$target.wants"
ln -sfn "../$unit" "$HOME/.config/systemd/user/$target.wants/$unit"
done
}
export -f omarchy_user_systemctl_enable
mkdir -p ~/.config
cp -R "$OMARCHY_PATH/config/." ~/.config/
cp "$OMARCHY_PATH/default/bashrc" ~/.bashrc
echo '[[ -f ~/.bashrc ]] && . ~/.bashrc' > ~/.bash_profile
# Dev-aware skill symlinks. Cannot live in /etc/skel because OMARCHY_PATH may
# point at a dev checkout (omarchy dev link) where the target differs.
mkdir -p ~/.agents/skills ~/.claude/skills ~/.codex/skills ~/.pi/agent/skills
ln -sfn "$OMARCHY_PATH/default/omarchy-skill" ~/.agents/skills/omarchy
ln -sfn "$OMARCHY_PATH/default/omarchy-skill" ~/.claude/skills/omarchy
ln -sfn "$OMARCHY_PATH/default/omarchy-skill" ~/.codex/skills/omarchy
ln -sfn "$OMARCHY_PATH/default/omarchy-skill" ~/.pi/agent/skills/omarchy
mkdir -p ~/.local/state/omarchy/toggles/hypr
cp "$OMARCHY_PATH/default/hypr/toggles/flags.lua" ~/.local/state/omarchy/toggles/hypr/
mkdir -p "$HOME/.local/share/nautilus-python/extensions"
cp "$OMARCHY_PATH/default/nautilus-python/extensions/localsend.py" \
"$HOME/.local/share/nautilus-python/extensions/"
cp "$OMARCHY_PATH/default/nautilus-python/extensions/transcode.py" \
"$HOME/.local/share/nautilus-python/extensions/"
mkdir -p ~/.config/omarchy/branding
cp "$OMARCHY_PATH/icon.txt" ~/.config/omarchy/branding/about.txt
cp "$OMARCHY_PATH/logo.txt" ~/.config/omarchy/branding/screensaver.txt
mkdir -p ~/Downloads ~/Pictures ~/Videos ~/.config/gtk-3.0
xdg-user-dirs-update --set TEMPLATES "$HOME"
xdg-user-dirs-update --set PUBLICSHARE "$HOME"
@@ -174,4 +130,4 @@ if (( first_install )); then
fi
touch "$marker"
echo "User setup complete."
echo "User finalization complete."
+5 -5
View File
@@ -15,12 +15,12 @@ USAGE
}
force=0
setup_user_args=()
finalize_user_args=()
while (($#)); do
case "$1" in
--force)
force=1
setup_user_args+=(--force)
finalize_user_args+=(--force)
shift
;;
-h|--help)
@@ -35,7 +35,7 @@ while (($#)); do
esac
done
omarchy-setup-user "${setup_user_args[@]}" || true
omarchy-finalize-user "${finalize_user_args[@]}" || true
state_dir=~/.local/state/omarchy
mkdir -p "$state_dir"
@@ -97,8 +97,8 @@ if [[ ! -f $USER_MARKER || $force -eq 1 ]]; then
run_first_run_step "install Voxtype post-update hook" \
omarchy-hook-install post-update "$OMARCHY_PATH/install/user/first-run/install-voxtype.hook"
run_first_run_step "recover internal monitor" \
bash "$OMARCHY_PATH/install/user/hardware/recover-internal-monitor.sh"
run_first_run_step "enable user systemd units" \
bash "$OMARCHY_PATH/install/user/first-run/enable-user-units.sh"
run_first_run_step "set GNOME theme" \
bash "$OMARCHY_PATH/install/user/first-run/gnome-theme.sh"
run_first_run_step "set GTK primary paste" \
+73 -36
View File
@@ -4,50 +4,87 @@
# omarchy:args=<font-name>
# omarchy:examples=omarchy font list | omarchy font set "CaskaydiaMono Nerd Font"
font_name="$1"
usage() {
echo "Usage: omarchy-font-set <font-name>"
}
if [[ -n $font_name ]]; then
if fc-list | grep -iq "$font_name"; then
if [[ -f ~/.config/alacritty/alacritty.toml ]]; then
sed -i "s/family = \".*\"/family = \"$font_name\"/g" ~/.config/alacritty/alacritty.toml
fi
font_name="${1:-}"
omarchy_root="${OMARCHY_PATH:-/usr/share/omarchy}"
if [[ -f ~/.config/kitty/kitty.conf ]]; then
sed -i "s/^font_family .*/font_family $font_name/g" ~/.config/kitty/kitty.conf
pkill -USR1 kitty
fi
case "$font_name" in
-h|--help)
usage
exit 0
;;
"")
usage >&2
exit 1
;;
esac
if [[ -f ~/.config/ghostty/config ]]; then
sed -i "s/font-family = \".*\"/font-family = \"$font_name\"/g" ~/.config/ghostty/config
pkill -SIGUSR2 ghostty
fi
if ! fc-list | grep -Fqi -- "$font_name"; then
echo "Font '$font_name' not found."
exit 1
fi
if [[ -f ~/.config/foot/foot.ini ]]; then
sed -i "s/^font=.*/font=$font_name:size=9/g" ~/.config/foot/foot.ini
fi
if [[ -f ~/.config/alacritty/alacritty.toml ]]; then
sed -i "s/family = \".*\"/family = \"$font_name\"/g" ~/.config/alacritty/alacritty.toml
fi
# fontconfig is the canonical source of truth — the omarchy shell, Qt
# apps, and anything else that resolves "monospace" all read from here.
xmlstarlet ed -L \
-u '//match[@target="pattern"][test/string="monospace"]/edit[@name="family"]/string' \
-v "$font_name" \
~/.config/fontconfig/fonts.conf
if [[ -f ~/.config/kitty/kitty.conf ]]; then
sed -i "s/^font_family .*/font_family $font_name/g" ~/.config/kitty/kitty.conf
pkill -USR1 kitty
fi
omarchy-restart-shell
if [[ -f ~/.config/ghostty/config ]]; then
sed -i "s/font-family = \".*\"/font-family = \"$font_name\"/g" ~/.config/ghostty/config
pkill -SIGUSR2 ghostty
fi
if pgrep -x ghostty; then
omarchy-notification-send -g  "You must restart Ghostty to see font change"
fi
if [[ -f ~/.config/foot/foot.ini ]]; then
sed -i "s/^font=.*/font=$font_name:size=9/g" ~/.config/foot/foot.ini
fi
if pgrep -x foot; then
omarchy-notification-send -g  "You must restart Foot to see font change"
fi
omarchy-hook font-set "$font_name"
else
echo "Font '$font_name' not found."
# fontconfig is the canonical source of truth — the omarchy shell, Qt apps,
# and anything resolving "monospace" all read from here. The shipped default
# is package-owned; create a user override only when the user changes fonts.
fontconfig_file="$HOME/.config/fontconfig/fonts.conf"
if [[ ! -f $fontconfig_file ]]; then
fontconfig_default="$omarchy_root/default/fontconfig/conf.avail/30-omarchy.conf"
if [[ ! -f $fontconfig_default ]]; then
echo "Default fontconfig file not found: $fontconfig_default" >&2
exit 1
fi
else
echo "Usage: omarchy-font-set <font-name>"
mkdir -p "$(dirname "$fontconfig_file")"
cp "$fontconfig_default" "$fontconfig_file"
fi
# We own the markup in 30-omarchy.conf, so the monospace block is predictable:
# the <string>FAMILY</string> immediately after <string>monospace</string> is
# the family we're replacing.
tmp=$(mktemp)
if ! awk -v new_font="$font_name" '
in_mono && /<string>[^<]*<\/string>/ {
sub(/<string>[^<]*<\/string>/, "<string>" new_font "</string>")
in_mono = 0
}
/<string>monospace<\/string>/ { in_mono = 1 }
{ print }
' "$fontconfig_file" >"$tmp"; then
rm -f "$tmp"
echo "Failed to update $fontconfig_file" >&2
exit 1
fi
mv "$tmp" "$fontconfig_file"
omarchy-restart-shell
if pgrep -x ghostty; then
omarchy-notification-send -g "You must restart Ghostty to see font change"
fi
if pgrep -x foot; then
omarchy-notification-send -g "You must restart Foot to see font change"
fi
omarchy-hook font-set "$font_name"
+13 -14
View File
@@ -1,37 +1,37 @@
#!/bin/bash
# omarchy:summary=Copies the named config from $OMARCHY_PATH/config/X/Y/Z -> ~/.config/X/Y/Z.
# omarchy:summary=Copy a shipped user config from /etc/skel/.config into ~/.config (backs up your version).
# omarchy:args=<config-path>
config_file=$1
config_file=${1:-}
if [[ -z $config_file ]]; then
cat <<USAGE
Usage: $0 [config_file]
Must provide a file path from the .config directory to be refreshed.
To copy $OMARCHY_PATH/config/hypr/hyprland.lua to ~/.config/hypr/hyprland.lua
Must provide a file path relative to .config to be refreshed.
To copy /etc/skel/.config/hypr/hyprland.lua to ~/.config/hypr/hyprland.lua:
$0 hypr/hyprland.lua
USAGE
exit 1
fi
# Backup the destination file (with timestamp) to avoid clobbering (Ex: hyprland.lua.bak.1753817951)
user_config_file="${HOME}/.config/$config_file"
default_config_file="$OMARCHY_PATH/config/$config_file"
default_config_file="/etc/skel/.config/$config_file"
backup_config_file="$user_config_file.bak.$(date +%s)"
if [[ ! -e $default_config_file ]]; then
echo "Not a shipped user config: $config_file" >&2
exit 1
fi
mkdir -p "$(dirname "$user_config_file")"
if [[ -f $user_config_file ]]; then
# Create preliminary backup
cp -f "$user_config_file" "$backup_config_file" 2>/dev/null
cp -f "$user_config_file" "$backup_config_file"
cp -f "$default_config_file" "$user_config_file"
# Replace config with new default
cp -f "$default_config_file" "$user_config_file" 2>/dev/null
# Compare and delete/inform accordingly
if cmp -s "$user_config_file" "$backup_config_file"; then
rm "$backup_config_file"
else
@@ -39,6 +39,5 @@ if [[ -f $user_config_file ]]; then
diff "$user_config_file" "$backup_config_file" || true
fi
else
# Config file did not exist already
cp -f "$default_config_file" "$user_config_file" 2>/dev/null
cp -f "$default_config_file" "$user_config_file"
fi
+11 -5
View File
@@ -1,17 +1,23 @@
#!/bin/bash
# omarchy:summary=Reset all Omarchy user configs to the defaults
# omarchy:requires-sudo=true
# omarchy:summary=Reset Omarchy user configs and shipped defaults in $HOME (destructive)
set -e
set -euo pipefail
if (( EUID == 0 )); then
echo "Error: This script should not be run as root"
exit 1
fi
echo "Resetting all Omarchy configs"
omarchy-setup-user --force
echo "Resetting Omarchy user configs to shipped defaults..."
# /etc/skel is the same tree useradd -m copies to seed a fresh user's $HOME.
# Replaying it over an existing user resyncs every package-shipped user
# default in one pass: .bashrc, .config/**, .local/share/applications,
# nautilus-python extensions, branding, hypr toggles, and migration markers.
# Trailing dot copies dotfiles; the shell wouldn't glob them with /etc/skel/*.
cp -af /etc/skel/. ~/
omarchy-refresh-limine
omarchy-refresh-plymouth
+399 -9
View File
@@ -1027,14 +1027,390 @@ root=/usr/share/omarchy
state_dir="$HOME/.local/state/omarchy"
mkdir -p "$state_dir" "$HOME/.config"
if [[ -d $root/config ]]; then
cp -R "$root/config/." "$HOME/.config/"
file_sha256() {
sha256sum "$1" | awk '{print $1}'
}
known_config_default_hashes() {
cat <<'DEFAULT_HASHES'
# action<TAB>relative ~/.config path<TAB>sha256
# refresh: hash-matched shipped defaults
refresh alacritty/alacritty.toml d43ab27e10ba1fd0505d50fe374b9bd02fa2e1d888fe2252c5208542fe5ddc8f
refresh alacritty/alacritty.toml 4e627909552ceebc80f5839955a09a7f804c501fee9a1fe76e13d95ad6cf5030
refresh alacritty/alacritty.toml 73dc34be744039613ef522aab84c27468b61ff13d7bc0ed2d57a83904e13f613
refresh alacritty/alacritty.toml e25fae44db040d97714cf4ca3d77e48f0ae32fed9e02e6b3db9ad4d705bdaed5
refresh alacritty/alacritty.toml 999562ef35642910bec05c2444c3acdcd6e29bc60ec541529d9c69a20cad5c88
refresh alacritty/alacritty.toml 8bf6dee79e634949617cd7acdd2756da52f7c3031b8eac98a7948f2fef0cf07e
refresh alacritty/alacritty.toml e4d201b6b9afd2f591d64f98eb50eb19247b8b839c2eaacfe2623628203c486f
refresh autostart/org.fcitx.Fcitx5.desktop a0260e7f35579a73f77e257e9e3795abd8b33119a17b931ca6016a07036e1670
refresh btop/btop.conf e6b86ddda073e06bf64cfbbde9652f9335171d8ca8c622e617478a3c696a6a3d
refresh btop/btop.conf 7f4122f2ed5f4c95489950cce2c0b192e19ae6fa74b0666d25e0b10040b2cee8
refresh btop/btop.conf 4e17dd28c43948af48a408ed9b0901c008e57dfe3b8fcf09649ec10f28746137
refresh chromium/Default/Preferences 67aef8ae3a6e0ee1180057198f47f2ea412ea1f01051d77edce4c465fb83d887
refresh chromium-flags.conf 3a8fe5b85bc42b27f5a0f36f3b674dc8e22a7d670a6aabbdc4f2d4057b97a1d4
refresh chromium-flags.conf 3d52925a48ac3704f9427ecec70310d9c89be5442c41ece183122a9246837be4
refresh chromium-flags.conf 4bc8710615d2151464e9bd6d1123fbfbb8f56dea4cf0568320bc7b50d24ad205
refresh chromium-flags.conf 1df1a5282f9525bfbb97a697c8169c08c1adb2b5a4de5ae94d84df7b4a78f2fc
refresh chromium-flags.conf 0408c524892830f5cf43e94c389b897cbc093ede312f49a4627bfabf692653c2
refresh fastfetch/config.jsonc 6adebf87d93902fb4bc68824a8e7c71e482846af23fea199e489c5496f791590
refresh fastfetch/config.jsonc fe61d9f7cfac800adb7bd21f75f1bfcca266c609b6efa02a6e25303ac192e86e
refresh fastfetch/config.jsonc 62fa26fa5fb8e11d31c5ec9f3a53b9b6b1124b29134b19329a092878ff624581
refresh fastfetch/config.jsonc 22565532d139ab8c2ddb21dcfe56460dc9f2f8a712b8d29f125c1c8b29beca37
refresh fastfetch/config.jsonc b65cb9ca36eed45929bfc35443762b14957d3847f6bed361a338224a87450e3a
refresh fastfetch/config.jsonc 3d153225131d3031b99b2e1a07e84d22cd0e51fcae1e701b08f6b6a558bc7a9c
refresh fastfetch/config.jsonc 404d0273c380cfdad4a037fa87202fc1d96be178c867e8a9d27b0a772f6e2fa1
refresh fastfetch/config.jsonc 4a98ff77ed46109d08ef7301249ea6823d1abba6c39c0871680feebf96620bf2
refresh fastfetch/config.jsonc c827c3a24f64c418d309244a8a967def0c522d824addf65d8ee0741afbc970bd
refresh fastfetch/config.jsonc c909b43ba000887cd418d9eaf98c1c9c5ba938aa27f9246cf8fa5662946c4e87
refresh fastfetch/config.jsonc b2e5757851f0703edb8bcfc4b51a00bf5e303c1df26b8c63e6b7df6be4ead4ed
refresh fcitx5/conf/clipboard.conf 2d53b40811ee7ceb71c7db0422a3ed733647f389379a57dbf2915d522dff4193
refresh fcitx5/conf/xcb.conf f5cf059182c4361fa7b16d5149f20a09eb1e0915f4463ba60dae626b86df7d9c
refresh foot/foot.ini 52e30647172522459b2179af714c19b62a35fe11bc23c49839fda5dca3253e84
refresh foot/foot.ini ac16453a4b4ebc997ff6732e7e4448dc759c25587fdb8aa1d1a7eee92b518987
refresh ghostty/config ed589d69296f5aa083e4dba43fc3d40cfbe2f6d10b382f3d0dc2ff76d5ec8fe5
refresh ghostty/config 2832c9c27cdcd735e913d538c3c61005fd90e909541bdcb746bb454851ae79ca
refresh ghostty/config 8200bc34b7ced8170d512c619f0408ac7dd7d7b4e64021ac66393d8a8bf1d951
refresh ghostty/config 006fbe4048de02fc040eeefd0974081c6f7df5dae419f8d542fc1adf08b0d6fb
refresh ghostty/config e55a409a490d4f80c19c9242a59db8e00e271e01feddb8fda6a5a7102d39af20
refresh ghostty/config b65e4903813d442a25540eef5a24313121c640a57114ee3d76d1ec7408d86b27
refresh ghostty/config 4e6ccba3d0584832cedf22713842f8ce7eca383c2ea3bfe61286d2b8df73f3fe
refresh git/config 3e1bdf77aa65b7bb412cc25c02bac4734e98c10e4a63815b98737bf817b6f682
refresh hypr/hyprsunset.conf 3ac2e9fe716fb8c612f07ca608ba7eee491921c2386118aae54ff9c8121726f2
refresh hypr/hyprsunset.conf e6fa7d909a90b64df7f75e8842bc2541e75463a47bbc7213ec79a56364f563f2
refresh hypr/hyprsunset.conf afe595d9d31574bde0f3ab8f7d370d0710bc188d6e5643f73255ffa7a202b647
refresh hypr/hyprsunset.conf 3042e4a923030239f4edf7b1943f41f4ed57107550177130c96f3b215b226e22
refresh hypr/xdph.conf 637899cd3926c4b8c758d3cb812cc1e7ac7643aca83a8e5030208234d064c839
refresh hypr/xdph.conf 2fded88a27b89f33cbc2b64a1258117df74b591f39a84c78ec3504a7d3b09136
refresh hyprland-preview-share-picker/config.yaml cb7f8768566ce41f8b09249b504ea8f6dbd6ed830637eef24c4e94e94486d113
refresh hyprland-preview-share-picker/config.yaml cf88364e497e4af2adaff9a0b7ca12227d29f831bc7e18846dd8f03bf4c618a8
refresh imv/config 58fd37d4862eb484d50c2c0cc507836e685a776d8da8dd9f20bd894809c31cd6
refresh imv/config 569b52b9614a78be67ce9f0a828ff1a7a73e4741d4751577ec6b247ab78e29ea
refresh imv/config 832b19821b4c69f9381aea6902b1b80c5682c30091e6d4a59493c8edf827bf72
refresh kitty/kitty.conf 69d6cbd2f8adcaec09407b5fb71ec7956cd510f6d64c36600ab7f7e7d3812cc6
refresh kitty/kitty.conf 6c5fe293bb92b1ea5fd955afcec126597ae0a665395db1d38b9beb6461c587d1
refresh kitty/kitty.conf 68779240358653a03ede95c4c872d94e3ab3742cf64a982b17e94f42aa2b74d4
refresh kitty/kitty.conf 9599cfd1f1fbd7e14fe1ba48196137be3cf8bcfa95f6c24d15d8053d88f73f0a
refresh kitty/kitty.conf ea3bc5e353f7312d774362310f7c511584efd6076c163b4cad1a9cc05c0f80ab
refresh kitty/kitty.conf 8984216fe0c0d006b969a7494dd4aad79ac78e9d15bd7a82ccbf5f10eac48fa6
refresh lazygit/config.yml e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
refresh obsidian/user-flags.conf 6c00f4daeff2b018bda247151a8edee44115273f554cfda00909bc5e56a3ae85
refresh omarchy/hooks/battery-low.d/play-warning-sound.sample 9623f5534cd52f76a51cfbb4e8a0699a550bf7f0f5f2ff54a1c78534d907fdf1
refresh omarchy/hooks/font-set.d/show-font-notification.sample d28090fd796b02fa799dcf7160e46ce9fc8d7a17b476ef87639d0b83a347dc37
refresh omarchy/hooks/post-boot.d/weather.sample fe37567a09046f1e4fcff9a9c74e4a036270cdf6da73dcc5cd3aa617f9a7c2ba
refresh omarchy/hooks/post-update.d/show-update-notification.sample 7995adc93a0ac6cd1afcd7ed9ac6d0ff058751ce4df4594aa78855603a613815
refresh omarchy/hooks/theme-set.d/show-theme-notification.sample b9d6754d95057bdb2f37595fd2f40055872118cb2f442bdc7b0b900317bac332
refresh omarchy/themed/alacritty.toml.tpl.sample ea5889bf79e273aa9cf1e7a1b2916dae7751fc0dced2cd53af36a2ebeecb4f1e
refresh omarchy/themed/alacritty.toml.tpl.sample a05d015227a406470b571d9d8f372eb8042df94b32cf746e71fd2f513c9880ac
refresh opencode/opencode.json 3806450805c8661a729c46aea325826e9ee7d158d650c9d750f5d0a763ec2c03
refresh opencode/opencode.json fb067af039252d48ef5b5c573a5e12c17357af4324b218aca5efa36f882846dd
refresh starship.toml b17c9b5f096fc125e97050e359171c6011d6b3c7d7e9ac28f630e75b4d4bb9db
refresh tmux/tmux.conf 345abaa3f128529abbbe4da3a24c227170c9679fb0af1f3d87eb15959e183219
refresh tmux/tmux.conf 93088311b8b67eb10ede3040b55e81193ff43fe16c5b2a6e3799300e00f39e3d
refresh tmux/tmux.conf 02d7c3914b73b8fec139316b9e1e88a40764617c6f4f1b6a3c05ad9c4a68b944
refresh tmux/tmux.conf 6633a3c59441087fdead8c30601e50c39fc7e74a66a32514296657332ff6cde9
refresh tmux/tmux.conf 473abae53d3aa7da59be65cc6301a15f6cc878045ae3f4c5e180ee013a0a04a7
refresh tmux/tmux.conf bf881713284aa2cc6722a199e42d57c005ae5922ef4eb6305593ead09b13ca7c
refresh Typora/themes/ia_typora.css 8bb2493c1dc24c8217c35ce9c0d29054b3733316923a0ddd2e63908b11ec073e
refresh Typora/themes/ia_typora_night.css ff3d670351fdaf64aca339fbb94453c4962feae4705be9ed7ff31237e6da5791
refresh Typora/themes/ia_typora_night.css d3708c850dcedf2ec210454c38bd1fa19b390e51bfce42ebac73d5f469aa1462
refresh xournalpp/settings.xml 47eafeeb3c246af1a5fb2cd1d87a748ce127d13410c351194c5d89bbaefb623d
refresh xournalpp/settings.xml 1c1a9efbf1b6dc7813bf3440fd5bf8409fc283e8d0192ca297dc36a10e12c846
# retire: nothing is actually copied. The new package owns the file at the
# system path below (shipped by omarchy-settings / omarchy-limine), so once
# the user's hash-matched copy in ~/.config is removed, the system path takes
# effect via the usual XDG/fontconfig/systemd lookup order. User overrides
# (hash mismatch) are kept active as backups.
# Legacy ~/.config path -> package-owned destination (where the file now lives):
# environment.d/fcitx.conf -> /usr/lib/environment.d/10-omarchy-fcitx.conf
# fontconfig/fonts.conf -> /usr/share/fontconfig/conf.avail/30-omarchy.conf
# mimeapps.list -> /usr/share/applications/mimeapps.list
# omarchy.ttf -> /usr/share/fonts/omarchy/omarchy.ttf
# systemd/user/bt-agent.service -> /usr/lib/systemd/user/bt-agent.service
# systemd/user/omarchy-recover-internal-monitor.service -> /usr/lib/systemd/user/omarchy-recover-internal-monitor.service
# systemd/user/omarchy-sleep-lock.service -> /usr/lib/systemd/user/omarchy-sleep-lock.service
# uwsm/default -> /usr/share/omarchy/default/uwsm/default
# uwsm/env -> /usr/share/uwsm/env.d/10-omarchy
# xdg-terminals.list -> /usr/share/xdg-terminal-exec/hyprland-xdg-terminals.list
retire environment.d/fcitx.conf faacbd703a8f797013968b235fec6289a9c921d3ec7b7f8b4e1cd24392f68b9c
retire environment.d/fcitx.conf 8b077b6ea2594dc1d4b57592a3bc62703df4959e86b93ea0fc488b6686309095
retire fontconfig/fonts.conf 7db33b0f8673f71f42604255cad222194294cd401315fecde6b83151eecebe91
retire fontconfig/fonts.conf f35b5b4bd0d8d3926f5d9a49ca791fc66b00793786420508d4a4c41ae9ccbefc
retire fontconfig/fonts.conf 6dec98b539388b95ecfdb3c7ab951001c712820eb0581002832cb7bfdde1a654
retire fontconfig/fonts.conf 3545f6c5a8c1465df7a4e251b3c2047d4ba03654c86636794f64cb0d8ea8ef63
retire fontconfig/fonts.conf 0f085b449f1cbe8eda3b59a6235bf2a3ddb6e982ab5d22fa642248916e63bafc
retire mimeapps.list 3b574cef135b5deb7a8a0c7e17139037cf1fe155300e3809f60bed0e04120975
retire omarchy.ttf e55e67119e82f56f92d90cbf54b7ccc1b2946b32c535a29370439d7ef5215966
retire systemd/user/bt-agent.service 0406b577a1225dc2a9f86638d3c346eb3635168576f04050be50ebcc0be6be12
retire systemd/user/omarchy-recover-internal-monitor.service b9b92cedc44cf3cb6216948629be55b53d16746e31896dc6469fd49ba55e82f4
retire systemd/user/omarchy-recover-internal-monitor.service e1483079b9f2aefcd43b4722c75a31643e5f3bb5a2eada5f52f1d7d201e8c289
retire systemd/user/omarchy-sleep-lock.service bcd1a76cb5c63514922bc5e11af22ae480fc6d06a99863364e02bdf3c7bdceaf
retire uwsm/default 2e89b03a3710b70cf754ea0c8db4388bb35a7519870036abff5ea8a584eafbc0
retire uwsm/default 3622ba134d29b639a155424a5226215e213c8508144e98001ffe128a8967c9b5
retire uwsm/default 73712c5a677532bd4afcee5800ae9b76e3d02a2130659a96a8b7fbe2fd2c3d53
retire uwsm/default 56f968d461da1b8360bc21d5fb7d5375121b772dd06d9397ebcda55a9097c2a0
retire uwsm/default f7558795e1f6b89357a780ad4e1c63ffdd56e423f31c9a888738afe894e16623
retire uwsm/env 60eae2629f2dbc70bb068de7bcb245032a429dd989f98b573eb315c0760b4ea5
retire uwsm/env daf70280edd80bae94660f8509d294df2aaa242d3428320bf6542341efff5a6d
retire uwsm/env 61a4147404a2b6b920f6f414a8c9bcef69b8bcf2a1251e1ddd2fbc37fe45e04d
retire uwsm/env 32130b0033e1d3229d89e6f73394e272eb3adad07314f740f8df9629c1f89beb
retire uwsm/env 278df755a1679f2735742c61de9789614cdb2021fbbb4e802f056c0403d1a6b8
retire uwsm/env 7be477d4e734d30ebc2caf9dbad6133dcb4aef38d64eb0979beab46f1c0e2464
retire uwsm/env 0c2cd9c32bd8aea26c62bc18c4a287a29999d4b2ec2b3216b666fb204ab3c471
retire uwsm/env 957dc0a95f8e909a378ce82967d7a55eb474e2fe5b2672212272e88530c4d4aa
retire uwsm/env 425036ed11f004f59a5fca324305b13189a6aca4b4dd1582532f075d14413d93
retire uwsm/env 9eb186609afce36a525e58e0b0858eb586869c5d067a7e5545bb5be606ab8a44
retire xdg-terminals.list 7303fa8d1d74eda73cf5fb4c90183a8608c191bb80d7fb9d96ab60f9df06022e
retire xdg-terminals.list d035da571a6bfc7d626d4a88846ea86a8adb6f6662f1f8b4abe2330e53ceb71b
DEFAULT_HASHES
}
declare -A known_config_default_hashes_by_key=()
while IFS=$' ' read -r action rel hash; do
[[ -n ${action:-} && $action != \#* ]] || continue
known_config_default_hashes_by_key["$action:$rel:$hash"]=1
done < <(known_config_default_hashes)
default_hash_paths() {
local action_filter="$1" action rel hash
known_config_default_hashes | while IFS=$' ' read -r action rel hash; do
[[ -n ${action:-} && $action != \#* ]] || continue
[[ $action == "$action_filter" ]] || continue
printf '%s\n' "$rel"
done | sort -u
}
is_known_default_hash() {
local action="$1" rel="$2" file="$3" hash
[[ -f $file ]] || return 1
hash=$(file_sha256 "$file")
[[ -n ${known_config_default_hashes_by_key["$action:$rel:$hash"]+set} ]]
}
backup_config_file() {
local file="$1"
[[ -e $file || -L $file ]] || return 0
cp -af "$file" "$file.omarchy-upgrade-to-4.$BACKUP_SUFFIX.bak"
}
mapfile -t retired_config_files < <(default_hash_paths retire)
# These are new Omarchy 4 config entry points. Older installs cannot have an
# Omarchy-shipped legacy copy to hash-match, so always install the v4 default.
always_copy_config_files=(
hypr/.luarc.json
hypr/autostart.lua
hypr/bindings.lua
hypr/hyprland.lua
hypr/input.lua
hypr/looknfeel.lua
hypr/monitors.lua
omarchy/bar.json
omarchy/extensions/omarchy-menu.jsonc
omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample
omarchy/shell.json
)
migrate_uwsm_env_customizations() {
local source="$1" dest="$HOME/.config/uwsm/env.d/99-omarchy-upgrade-env" tmp
[[ -f $source ]] || return 0
is_known_default_hash retire uwsm/env "$source" && return 0
tmp=$(mktemp)
awk '
function omitted(reason) { print ": # omarchy-upgrade omitted legacy Omarchy " reason; next }
/^[[:space:]]*export[[:space:]]+OMARCHY_PATH=\$HOME\/\.local\/share\/omarchy[[:space:]]*$/ { omitted("OMARCHY_PATH") }
/^[[:space:]]*export[[:space:]]+OMARCHY_PATH="\$\{OMARCHY_PATH:-\/usr\/share\/omarchy\}"[[:space:]]*$/ { omitted("OMARCHY_PATH") }
/^[[:space:]]*\[ -f \/etc\/omarchy\.conf \] && \. \/etc\/omarchy\.conf[[:space:]]*$/ { omitted("/etc/omarchy.conf source") }
/^[[:space:]]*export[[:space:]]+PATH=\$OMARCHY_PATH\/bin:\$PATH:\$HOME\/\.local\/bin[[:space:]]*$/ { omitted("PATH setup") }
/^[[:space:]]*export[[:space:]]+PATH="\$OMARCHY_PATH\/bin:\$PATH:\$HOME\/\.local\/bin"[[:space:]]*$/ { omitted("PATH setup") }
/^[[:space:]]*source[[:space:]]+~\/\.config\/uwsm\/default[[:space:]]*$/ { omitted("uwsm/default source") }
/^[[:space:]]*\[ -f "\$HOME\/\.config\/uwsm\/default" \] && \. "\$HOME\/\.config\/uwsm\/default"[[:space:]]*$/ { omitted("uwsm/default source") }
/^[[:space:]]*omarchy-cmd-present[[:space:]]+mise && eval "\$\(mise activate bash --shims\)"[[:space:]]*$/ { omitted("mise activation") }
{ print }
' "$source" >"$tmp"
if [[ -s $tmp ]]; then
if ! sh -n "$tmp" >/dev/null 2>&1; then
cp -f "$source" "$tmp"
fi
mkdir -p "$(dirname "$dest")"
{
echo '# Preserved custom ~/.config/uwsm/env content during Omarchy 4 upgrade.'
echo '# Exact legacy Omarchy-managed setup lines were replaced with no-op markers where safe.'
cat "$tmp"
} >"$dest"
fi
rm -f "$tmp"
}
is_retired_config_file() {
local rel="$1" retired
[[ $rel == systemd/user/* ]] && return 0
for retired in "${retired_config_files[@]}"; do
[[ $rel == "$retired" ]] && return 0
done
return 1
}
copy_config_default() {
local rel="$1" source="$root/config/$1" target="$HOME/.config/$1"
[[ -e $source || -L $source ]] || return 0
if [[ -e $target || -L $target ]]; then
if [[ -f $source && -f $target ]] && cmp -s "$source" "$target"; then
return 0
fi
backup_config_file "$target"
rm -f "$target"
fi
mkdir -p "$(dirname "$target")"
cp -P "$source" "$target"
}
copy_missing_config_defaults() {
local source_dir="$1" target_dir="$2" source_path rel target_path
[[ -d $source_dir ]] || return 0
while IFS= read -r -d '' source_path; do
rel="${source_path#$source_dir/}"
is_retired_config_file "$rel" && continue
target_path="$target_dir/$rel"
[[ -e $target_path || -L $target_path ]] && continue
mkdir -p "$(dirname "$target_path")"
cp -P "$source_path" "$target_path"
done < <(find "$source_dir" \( -type f -o -type l \) -print0)
}
refresh_known_config_defaults() {
local rel file default
while IFS= read -r rel; do
file="$HOME/.config/$rel"
default="$root/config/$rel"
[[ -f $file && -f $default ]] || continue
is_known_default_hash refresh "$rel" "$file" || continue
copy_config_default "$rel"
done < <(default_hash_paths refresh)
}
copy_always_config_defaults() {
local rel
for rel in "${always_copy_config_files[@]}"; do
copy_config_default "$rel"
done
}
is_known_bashrc_default() {
local file="$1" hash
[[ -f $file ]] || return 1
hash=$(file_sha256 "$file")
case "$hash" in
06ef4dcaa0530d7410513b65b82eea45f147d386a7689dcf1f5636fdad7561ff|\
907d367c7589361788a2b0456003f6ef0a2e049548b77b9acf5b0f8cecc2ab0f|\
7f46fdc7e3016ab2309d49607a9dddc907b06066cebcf23a63e0110f5d962d73|\
322f495563435da1dc70e16968ee33f6a9d81ab361b79ebfd97814b83a89e011|\
56009cd83e46e0715911877f173bf1dab966f3e22b6995c530937f2feb18b667|\
1a8c98df85360a85acde31effa33c419ce9d27bc17e3a0d5900d4a6cb1743bcc)
return 0
;;
esac
return 1
}
install_bash_startup() {
local bashrc="$HOME/.bashrc" bash_profile="$HOME/.bash_profile"
[[ -f $root/default/bashrc ]] || return 0
backup_config_file "$bashrc"
if [[ ! -e $bashrc && ! -L $bashrc ]] || is_known_bashrc_default "$bashrc"; then
cp "$root/default/bashrc" "$bashrc"
elif grep -qxF 'source ~/.local/share/omarchy/default/bash/rc' "$bashrc" || grep -qxF 'source "$OMARCHY_PATH/default/bash/rc"' "$bashrc"; then
python3 - "$bashrc" <<'BASHRC_PATCH_PY'
import sys
from pathlib import Path
path = Path(sys.argv[1])
text = path.read_text()
new_block = '''# /etc/omarchy.conf is written by omarchy-dev-link. When absent, force the
# package default instead of preserving a stale inherited dev-link value before
# we decide which rc file to source.
if [[ -f /etc/omarchy.conf ]]; then
source /etc/omarchy.conf
export OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}"
else
export OMARCHY_PATH=/usr/share/omarchy
fi
if [[ -f $root/default/bashrc ]]; then
cp "$root/default/bashrc" "$HOME/.bashrc"
echo '[[ -f ~/.bashrc ]] && . ~/.bashrc' >"$HOME/.bash_profile"
source "$OMARCHY_PATH/default/bash/rc"
'''
text = text.replace('source ~/.local/share/omarchy/default/bash/rc\n', new_block)
text = text.replace(': "${OMARCHY_PATH:=/usr/share/omarchy}"\nsource "$OMARCHY_PATH/default/bash/rc"\n', new_block)
path.write_text(text)
BASHRC_PATCH_PY
fi
if [[ ! -e $bash_profile && ! -L $bash_profile ]]; then
echo '[[ -f ~/.bashrc ]] && . ~/.bashrc' >"$bash_profile"
fi
}
# Some former ~/.config defaults are now package-owned elsewhere so future
# updates can change defaults without rewriting user files. Back them up before
# cleanup, then keep only real user customizations active.
uwsm_env="$HOME/.config/uwsm/env"
if [[ -f $uwsm_env ]]; then
cp -f "$uwsm_env" "$uwsm_env.omarchy-upgrade-to-4.$BACKUP_SUFFIX.bak"
migrate_uwsm_env_customizations "$uwsm_env"
fi
for rel in "${retired_config_files[@]}"; do
file="$HOME/.config/$rel"
backup_config_file "$file"
done
# Existing user config may be customized. Refresh only files whose hash matches
# a default Omarchy has shipped before, fill in missing defaults for old installs,
# and always write the v4-only entry points that older installs cannot have.
refresh_known_config_defaults
copy_missing_config_defaults "$root/config" "$HOME/.config"
copy_always_config_defaults
for rel in "${retired_config_files[@]}"; do
file="$HOME/.config/$rel"
backup="$file.omarchy-upgrade-to-4.$BACKUP_SUFFIX.bak"
# UWSM env was hardcoded to the legacy checkout on older installs. Custom
# non-Omarchy lines were migrated to env.d above; never keep the active file.
if [[ $rel == uwsm/env ]]; then
rm -f "$file"
continue
fi
if [[ -f $backup ]] && ! is_known_default_hash retire "$rel" "$backup"; then
# Keep intentional user overrides active; package defaults remain lower
# priority under /usr/lib or /usr/share.
mkdir -p "$(dirname "$file")"
cp -af "$backup" "$file"
else
rm -f "$file"
fi
done
install_bash_startup
unset rel file backup retired_config_files always_copy_config_files uwsm_env known_config_default_hashes_by_key
unset -f file_sha256 known_config_default_hashes default_hash_paths is_known_default_hash backup_config_file migrate_uwsm_env_customizations is_retired_config_file copy_config_default copy_missing_config_defaults refresh_known_config_defaults copy_always_config_defaults is_known_bashrc_default install_bash_startup
mkdir -p "$HOME/.agents/skills" "$HOME/.claude/skills" "$HOME/.codex/skills" "$HOME/.pi/agent/skills"
if [[ -d $root/default/omarchy-skill ]]; then
ln -sfn "$root/default/omarchy-skill" "$HOME/.agents/skills/omarchy"
@@ -1119,9 +1495,22 @@ if [[ -f $root/default/wireplumber/wireplumber.conf.d/bluetooth-a2dp-autoconnect
fi
enable_user_unit() {
local unit="$1" unit_path="$HOME/.config/systemd/user/$1" target
local unit="$1" unit_path link_target target
local -a targets=()
[[ -f $unit_path ]] || return 0
if [[ -f $HOME/.config/systemd/user/$unit ]]; then
unit_path="$HOME/.config/systemd/user/$unit"
link_target="../$unit"
elif [[ -f /usr/lib/systemd/user/$unit ]]; then
unit_path="/usr/lib/systemd/user/$unit"
link_target="$unit_path"
elif [[ -f $root/default/systemd/user/$unit ]]; then
unit_path="$root/default/systemd/user/$unit"
link_target="$unit_path"
else
return 0
fi
mapfile -t targets < <(awk -F= '
/^\[Install\]/ { in_install=1; next }
/^\[/ { in_install=0 }
@@ -1133,11 +1522,12 @@ enable_user_unit() {
((${#targets[@]})) || targets=(default.target)
for target in "${targets[@]}"; do
mkdir -p "$HOME/.config/systemd/user/$target.wants"
ln -sfn "../$unit" "$HOME/.config/systemd/user/$target.wants/$unit"
ln -sfn "$link_target" "$HOME/.config/systemd/user/$target.wants/$unit"
done
}
enable_user_unit bt-agent.service
enable_user_unit omarchy-sleep-lock.service
enable_user_unit omarchy-recover-internal-monitor.service
mkdir -p "$HOME/.config/systemd/user/default.target.wants"
for unit in pipewire-pulse.service pipewire-pulse.socket; do
if [[ -e /usr/lib/systemd/user/$unit ]]; then
@@ -1248,7 +1638,7 @@ if command -v xdg-mime >/dev/null 2>&1; then
xdg-mime default HEY.desktop x-scheme-handler/mailto || true
fi
touch "$state_dir/setup-user.done"
touch "$state_dir/finalize-user.done"
USER_SETUP
}
@@ -1 +0,0 @@
../bt-agent.service
@@ -1 +0,0 @@
../omarchy-sleep-lock.service
-15
View File
@@ -1,15 +0,0 @@
# Changes require a restart to take effect.
# Source /etc/omarchy.conf so omarchy-dev-link (which writes
# OMARCHY_PATH=<checkout> there) wins over the packaged default. UWSM doesn't
# source /etc/profile.d/, so this duplicates that logic for the Hyprland
# session.
[ -f /etc/omarchy.conf ] && . /etc/omarchy.conf
export OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}"
export PATH="$OMARCHY_PATH/bin:$PATH:$HOME/.local/bin"
# Set default terminal and editor
source ~/.config/uwsm/default
# Activate mise if present on the system
omarchy-cmd-present mise && eval "$(mise activate bash --shims)"
+2 -2
View File
@@ -6,9 +6,9 @@
<property name="pressureMultiplier" value="1"/>
<property name="zoomGesturesEnabled" value="true"/>
<property name="selectedToolbar" value="Portrait"/>
<property name="lastSavePath" value="/home/dhh/Downloads"/>
<property name="lastSavePath" value=""/>
<property name="lastOpenPath" value=""/>
<property name="lastImagePath" value="/home/dhh/Dropbox/Images"/>
<property name="lastImagePath" value=""/>
<property name="edgePanSpeed" value="20"/>
<property name="edgePanMaxMult" value="5"/>
<property name="zoomStep" value="10"/>
+27
View File
@@ -0,0 +1,27 @@
# Single source of truth for OMARCHY_PATH + PATH adjustments. Sourced by:
# /etc/profile.d/omarchy.sh (system login shells)
# /etc/skel/.bashrc (interactive shells)
# /usr/share/uwsm/env.d/10-omarchy (Hyprland session via uwsm)
# /usr/share/omarchy/default/bash/envs (bash rc chain; SSH/non-login)
# /etc/omarchy.conf is written by omarchy-dev-link. When absent, force the
# packaged default instead of preserving a stale inherited value.
if [ -f /etc/omarchy.conf ]; then
. /etc/omarchy.conf
: "${OMARCHY_PATH:=/usr/share/omarchy}"
else
OMARCHY_PATH=/usr/share/omarchy
fi
export OMARCHY_PATH
# 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
# /usr/share/omarchy/bin would just be noise. omarchy-dev-unlink strips its
# checkout bin/ from the active PATH explicitly, so we don't dedupe here.
if [ "$OMARCHY_PATH" != /usr/share/omarchy ]; then
case ":$PATH:" in
*":${OMARCHY_PATH%/}/bin:"*) ;;
*) PATH="${OMARCHY_PATH%/}/bin:$PATH" ;;
esac
export PATH
fi
+8 -6
View File
@@ -6,9 +6,11 @@ export BAT_THEME=ansi
export MANROFFOPT="-c"
export MANPAGER="sh -c 'col -bx | bat -l man -p'"
# Duplicated from .config/uwsm/env so SSH/non-login shells also see the
# override. /etc/omarchy.conf wins (omarchy-dev-link writes it); otherwise
# the packaged runtime under /usr/share/omarchy is used.
[ -f /etc/omarchy.conf ] && . /etc/omarchy.conf
export OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}"
export PATH="$OMARCHY_PATH/bin:$PATH:$HOME/.local/bin"
# Idempotent — already sourced by /etc/skel/.bashrc; re-source here so SSH and
# other non-interactive non-login bash sessions still get OMARCHY_PATH.
[ -r /usr/share/omarchy/default/bash/env-bootstrap ] && . /usr/share/omarchy/default/bash/env-bootstrap
case ":$PATH:" in
*":$HOME/.local/bin:"*) ;;
*) export PATH="$PATH:$HOME/.local/bin" ;;
esac
+1 -1
View File
@@ -3,7 +3,7 @@
# All the default Omarchy aliases and functions
# (don't mess with these directly, just overwrite them here!)
: "${OMARCHY_PATH:=/usr/share/omarchy}"
[[ -r /usr/share/omarchy/default/bash/env-bootstrap ]] && source /usr/share/omarchy/default/bash/env-bootstrap
source "$OMARCHY_PATH/default/bash/rc"
# Add your own exports, aliases, and functions here.
@@ -23,7 +23,7 @@
<test name="family" qual="any">
<string>monospace</string>
</test>
<edit name="family" mode="append" binding="same">
<edit name="family" mode="assign" binding="strong">
<string>JetBrainsMono Nerd Font</string>
</edit>
</match>
+20
View File
@@ -0,0 +1,20 @@
# Changes require a restart to take effect.
# UWSM doesn't source /etc/profile.d, so pull the env bootstrap in directly.
[ -r /usr/share/omarchy/default/bash/env-bootstrap ] && . /usr/share/omarchy/default/bash/env-bootstrap
# Omarchy session defaults. Users can override these in ~/.config/uwsm/default
# or, preferably, ~/.config/uwsm/env.d/*.
if [ -f "${OMARCHY_PATH%/}/default/uwsm/default" ]; then
. "${OMARCHY_PATH%/}/default/uwsm/default"
else
export TERMINAL=xdg-terminal-exec
export BROWSER=omarchy-launch-browser
export EDITOR="omarchy-launch-editor --inline"
fi
# Optional user overrides kept for compatibility with older installs.
[ -f "$HOME/.config/uwsm/default" ] && . "$HOME/.config/uwsm/default"
# Activate mise if present on the system
omarchy-cmd-present mise && eval "$(mise activate bash --shims)"
@@ -1,3 +1,3 @@
# Terminal emulator preference order for xdg-terminal-exec
# The first found and valid terminal will be used
Alacritty.desktop
foot.desktop
+186
View File
@@ -0,0 +1,186 @@
# File layout
How `omarchy/` is organized and where everything ends up on an installed
system.
## Mental model
Four Arch packages are built from this one repo (PKGBUILDs live in
`omarchy-pkgs/pkgbuilds/`):
- **`omarchy`** — runtime binaries (`bin/`), install/finalize scripts
(`install/`), migrations, themes, and the Quickshell desktop (`shell/`).
- **`omarchy-settings`** — user defaults (seeded via `/etc/skel`), `/etc/`
drop-ins, package-owned system files under `/usr/share` and `/usr/lib`,
fonts, plymouth theme, sddm theme, branding. Carves out `default/limine/`
and `default/snapper/` (owned by `omarchy-limine`).
- **`omarchy-dev-tools`** — just `bin/omarchy-dev-*`. Optional dep of
`omarchy`; installed for contributors, not end users.
- **`omarchy-limine`** — `default/limine/` and `default/snapper/` from this
repo, plus mkinitcpio and limine-entry-tool drop-ins that live alongside
the PKGBUILD in `omarchy-pkgs/`. Owns the boot/snapshot story end-to-end.
Three layers populate `$HOME`:
1. **Seed**`omarchy-settings` ships static defaults to `/etc/skel/`.
Arch's `useradd -m` copies that tree into a new user's `$HOME` at user
creation. This is the only mechanism that touches a brand-new user's home
for these files.
2. **Finalize**`omarchy-finalize-user` runs once per user and handles the
things `/etc/skel` can't do because they need `$HOME` expansion, the live
`$OMARCHY_PATH`, or runtime detection of system state.
3. **Resync**`omarchy-reinstall-configs` is the explicit, destructive
command for an existing user to clobber their configs back to shipped
defaults.
`/etc/skel` only fires at user creation. Existing users picking up new
defaults must use the resync command.
## Build-time map (repo → installed paths)
```
omarchy/ built into installed at
───────────────────────── ────────────── ────────────────────────────────────
bin/omarchy-* ──► omarchy /usr/bin/omarchy-*
(and symlinks in /usr/share/omarchy/bin/)
bin/omarchy-dev-* ──► omarchy-dev-tools /usr/bin/omarchy-dev-*
bin/omarchy-debug,
bin/omarchy-debug-idle,
bin/omarchy-upload-log ──► omarchy-settings /usr/bin/ (needed before omarchy is installed)
install/** ──► omarchy /usr/share/omarchy/install/
migrations/** ──► omarchy /usr/share/omarchy/migrations/
themes/** ──► omarchy /usr/share/omarchy/themes/
shell/** ──► omarchy /usr/share/omarchy/shell/
version ──► omarchy /usr/share/omarchy/version
+ /etc/skel/.local/state/omarchy/migrations/*
config/** ──► omarchy-settings /etc/skel/.config/** (seeds new users)
/usr/share/omarchy/config/** (resync source)
applications/*.desktop ──► omarchy-settings /etc/skel/.local/share/applications/
/usr/share/omarchy/applications/
applications/icons/* ──► omarchy-settings /usr/share/icons/hicolor/{48,256,scalable}/apps/
etc/** ──► omarchy-settings /etc/** (drop-ins we own outright)
default/limine/limine.conf ──► omarchy-limine /usr/share/omarchy/default/limine/limine.conf
default/limine/default.conf ──► omarchy-limine /usr/share/omarchy/default/limine/default.conf
(template; ISO substitutes @@CMDLINE@@ → /etc/default/limine)
default/snapper/root ──► omarchy-limine /etc/snapper/config-templates/omarchy
default/** ──► omarchy-settings /usr/share/omarchy/default/ (excluding default/{limine,snapper})
├─ bashrc /usr/share/omarchy/etc-overrides/dot.bashrc
│ → /etc/skel/.bashrc (post_install cp -f)
├─ hypr/toggles/flags.lua /etc/skel/.local/state/omarchy/toggles/hypr/
├─ nautilus-python/extensions/*.py /etc/skel/.local/share/nautilus-python/extensions/
├─ uwsm/env.d/10-omarchy /usr/share/uwsm/env.d/
├─ environment.d/*.conf /usr/lib/environment.d/
├─ fontconfig/conf.avail/30-omarchy.conf /usr/share/fontconfig/conf.avail/
│ + symlink /etc/fonts/conf.d/30-omarchy.conf
├─ xdg-terminal-exec/*.list /usr/share/xdg-terminal-exec/
├─ applications/mimeapps.list /usr/share/applications/mimeapps.list
├─ systemd/user/*.service /usr/lib/systemd/user/
├─ systemd/system-sleep/unmount-fuse /usr/lib/systemd/system-sleep/
├─ fonts/omarchy/omarchy.ttf /usr/share/fonts/omarchy/
├─ sddm/omarchy/ /usr/share/sddm/themes/omarchy/
├─ sddm/hyprland.lua /usr/share/sddm/hyprland.lua
├─ wayland-sessions/omarchy.desktop /usr/local/share/wayland-sessions/
├─ plymouth/ /usr/share/plymouth/themes/omarchy/
└─ security/faillock, nsswitch, cups-browsed,
plymouthd.conf, os-release /usr/share/omarchy/etc-overrides/
→ /etc/* (post_install cp -f, see below)
logo.{txt,svg}, icon.{txt,png} ──► omarchy-settings /usr/share/omarchy/ (resync source)
/usr/share/pixmaps/omarchy.png
/usr/share/icons/hicolor/256x256/apps/omarchy.png
/etc/skel/.config/omarchy/branding/{about,screensaver}.txt
```
### Why `etc-overrides/` exists
Some files under `/etc/` (`.bashrc` in `/etc/skel`, `nsswitch.conf`,
`security/faillock.conf`, `cups/cups-browsed.conf`, `plymouth/plymouthd.conf`,
`os-release`) are owned by upstream Arch packages, so we can't install over
them via pacman without a file conflict. Instead they ship at
`/usr/share/omarchy/etc-overrides/` and the `omarchy-settings` `post_install`
/ `post_upgrade` scriptlet `cp -f`'s them into place.
Tradeoff: user edits to those files get clobbered on every `omarchy-settings`
upgrade. This is documented in the PKGBUILD.
## Runtime finalization (`omarchy-finalize-user`)
Runs once per user. It does **not** copy `~/.config/**`, `~/.bashrc`,
`flags.lua`, or the nautilus extensions — `/etc/skel` already seeded those.
It only does the things `/etc/skel` can't:
- Skill symlinks `~/.{agents,claude,codex,pi/agent}/skills/omarchy`
`$OMARCHY_PATH/default/omarchy-skill`. Symlinks (not copies) so
`omarchy dev link` against a dev checkout repoints them correctly.
- `xdg-user-dirs-update` (Templates/Public/Desktop folded back into `$HOME`)
and `~/.config/gtk-3.0/bookmarks` (needs `$HOME` expansion).
- Sync `XKBLAYOUT` / `XKBVARIANT` from `/etc/vconsole.conf` into
`~/.config/hypr/input.lua`.
- `xdg-settings set default-web-browser chromium.desktop` and
`xdg-mime default HEY.desktop x-scheme-handler/mailto` (XDG-aware paths).
- `omarchy-refresh-applications` (composes generated `.desktop` launchers).
- Sources `install/user/all.sh` — theme, git, mise, keyring, per-user
hardware quirks (bluetooth, asus mic/mixer, framework f13 audio, …).
- On `--first-install`, marks every shipped migration as already applied
for the freshly-created user.
Idempotency marker: `~/.local/state/omarchy/finalize-user.done`.
The ISO calls it as `omarchy-finalize-user --force --first-install` in the
target chroot as the install user, after `omarchy-setup-system` has finished
the root-side work.
## Root-side install orchestration
`omarchy-setup-system` (root, in chroot) runs target-side setup at ISO
finalization. It sources:
- `install/config/*.sh` — theme links, lockout limits, lockscreen PAM,
powerprofilesctl shebang fix, docker setup, service enablement, firewall.
- `install/hardware/all.sh` via `omarchy-setup-hardware` — vendor- and
device-specific kernel modules, udev rules, microcode, wireless regdom,
ASUS / Framework / Intel / Apple / Lenovo quirks.
- `install/login/*.sh` — SDDM theme/session config.
- `install/post-install/*.sh` — final pacman/udev/localdb passes.
Logging goes to `/var/log/omarchy-install.log` via
`install/helpers/logging.sh`.
## Explicit resync (`omarchy-reinstall-configs`)
When an existing user wants to reset to shipped defaults:
```
~/ ← cp -af /etc/skel/.
```
Replaying `/etc/skel` over `$HOME` is exactly what `useradd -m` does for a
brand-new user, so this one copy resyncs `.bashrc`, `.config/**`,
`.local/share/applications/`, the nautilus-python extensions, hypr toggles,
branding files, and the shipped migration markers in a single pass.
Then it runs `omarchy-refresh-limine`, `omarchy-refresh-plymouth`, and the
nvim refresh. Destructive: existing user files at these paths are clobbered
without backup.
## Quick reference: where does X live?
| Goal | Touch |
| --- | --- |
| Default file at `~/.config/foo/` | `config/foo/` |
| `/etc/` drop-in we own outright | `etc/` |
| `/etc/` file owned by an upstream package | `default/`, then add to `etc-overrides` in `omarchy-settings` PKGBUILD + scriptlet |
| Package-owned system file (e.g. systemd user service in `/usr/lib`) | `default/`, document the mapping in `default/package-defaults.tsv`, then add the `install -Dm644` line in `omarchy-settings` PKGBUILD |
| Per-user file that's static but lives outside `~/.config` | `default/`, then add `install -Dm644 ... $pkgdir/etc/skel/...` in `omarchy-settings` PKGBUILD |
| Runtime tweak that needs `$HOME` or live system state | extend `omarchy-finalize-user`, or add a per-user leaf under `install/user/` and wire into `install/user/all.sh` |
| One-time root-side setup step | `install/config/*.sh` or `install/hardware/*.sh`, wire into `omarchy-setup-system` or `install/hardware/all.sh` |
| User-facing `omarchy-*` command | `bin/omarchy-<group>-<verb>` — see `GROUP_DESCRIPTIONS` in `bin/omarchy` |
| New theme | `themes/<name>/` (+ matching templates under `default/themed/` if they need theme colors) |
| One-shot fix for installed systems | `migrations/<unix-timestamp>.sh` (use `omarchy-dev-add-migration --no-edit`) |
+1 -8
View File
@@ -1,8 +1 @@
[ -f /etc/omarchy.conf ] && . /etc/omarchy.conf
export OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}"
# omarchy-dev-link: prepend checkout bin/ so it shadows /usr/bin/omarchy-*.
case ":$PATH:" in
*":$OMARCHY_PATH/bin:"*) ;;
*) [ -d "$OMARCHY_PATH/bin" ] && export PATH="$OMARCHY_PATH/bin:$PATH" ;;
esac
[ -r /usr/share/omarchy/default/bash/env-bootstrap ] && . /usr/share/omarchy/default/bash/env-bootstrap
-1
View File
@@ -137,7 +137,6 @@ woff2-font-awesome
xdg-desktop-portal-gtk
xdg-desktop-portal-hyprland
xdg-terminal-exec
xmlstarlet
xournalpp
yaru-icon-theme
yay
-2
View File
@@ -2,9 +2,7 @@ run_logged "$OMARCHY_INSTALL/user/theme.sh"
run_logged "$OMARCHY_INSTALL/user/git.sh"
run_logged "$OMARCHY_INSTALL/user/xcompose.sh"
run_logged "$OMARCHY_INSTALL/user/mise-work.sh"
run_logged "$OMARCHY_INSTALL/user/sleep-lock.sh"
run_logged "$OMARCHY_INSTALL/user/hardware/bluetooth.sh"
run_logged "$OMARCHY_INSTALL/user/hardware/asus/fix-audio-mixer.sh"
run_logged "$OMARCHY_INSTALL/user/hardware/asus/fix-mic.sh"
run_logged "$OMARCHY_INSTALL/user/hardware/framework/fix-f13-amd-audio-input.sh"
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
# Enable the user systemd units we ship. Runs at first-run rather than at
# finalize-user time because the user manager isn't live during the ISO
# chroot — by first-run, the Hyprland/uwsm session is up and `systemctl
# --user enable` writes the correct .wants symlinks based on each unit's
# [Install]/WantedBy. ConditionPath* in the unit files keep the enabled
# units inert on hardware they don't apply to.
set -euo pipefail
systemctl --user enable \
bt-agent.service \
omarchy-recover-internal-monitor.service \
omarchy-sleep-lock.service
-13
View File
@@ -1,13 +0,0 @@
mkdir -p ~/.config/wireplumber/wireplumber.conf.d/
cp "$OMARCHY_PATH/default/wireplumber/wireplumber.conf.d/bluetooth-a2dp-autoconnect.conf" ~/.config/wireplumber/wireplumber.conf.d/
# Quickshell.Bluetooth has no Agent API, so the omarchy.bluetooth plugin
# can't answer the auth prompts bluez issues during pair(). bt-agent registers
# a NoInputNoOutput agent on the system bus so pair() actually completes.
mkdir -p ~/.config/systemd/user/
cp "$OMARCHY_PATH/config/systemd/user/bt-agent.service" ~/.config/systemd/user/
if declare -F omarchy_user_systemctl_enable >/dev/null; then
omarchy_user_systemctl_enable bt-agent.service
else
systemctl --user enable bt-agent.service
fi
@@ -1 +0,0 @@
systemctl --user enable omarchy-recover-internal-monitor.service
-7
View File
@@ -1,7 +0,0 @@
mkdir -p ~/.config/systemd/user/
cp "$OMARCHY_PATH/config/systemd/user/omarchy-sleep-lock.service" ~/.config/systemd/user/
if declare -F omarchy_user_systemctl_enable >/dev/null; then
omarchy_user_systemctl_enable omarchy-sleep-lock.service
else
systemctl --user enable omarchy-sleep-lock.service
fi
-9
View File
@@ -1,9 +0,0 @@
echo "Gate bluetooth pairing agent on active bluetooth service"
mkdir -p "$HOME/.config/systemd/user/"
cp "$OMARCHY_PATH/config/systemd/user/bt-agent.service" "$HOME/.config/systemd/user/"
systemctl --user daemon-reload
if systemctl --user is-enabled --quiet bt-agent.service; then
systemctl --user reset-failed bt-agent.service 2>/dev/null || true
fi
+1 -1
View File
@@ -4,7 +4,7 @@ set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
grep -q '^ConditionPathIsDirectory=/sys/class/bluetooth$' "$ROOT/config/systemd/user/bt-agent.service" || \
grep -q '^ConditionPathIsDirectory=/sys/class/bluetooth$' "$ROOT/default/systemd/user/bt-agent.service" || \
fail "bt-agent is skipped on machines without Bluetooth hardware"
pass "bt-agent is skipped on machines without Bluetooth hardware"
+35
View File
@@ -87,6 +87,41 @@ if missing or bad:
PY
pass "default bar widget ids resolve to manifests and entry points"
ROOT="$ROOT" python3 <<'PY'
import os
import sys
from pathlib import Path
root = Path(os.environ["ROOT"])
pkgbuild = (root.parent / "omarchy-pkgs/pkgbuilds/omarchy-settings/PKGBUILD").read_text()
errors = []
package_defaults = [
("default/uwsm/env.d/10-omarchy", "/usr/share/uwsm/env.d/10-omarchy", "uwsm/env"),
("default/uwsm/default", None, "uwsm/default"),
("default/environment.d/10-omarchy-fcitx.conf", "/usr/lib/environment.d/10-omarchy-fcitx.conf", "environment.d/fcitx.conf"),
("default/fontconfig/conf.avail/30-omarchy.conf", "/usr/share/fontconfig/conf.avail/30-omarchy.conf", "fontconfig/fonts.conf"),
("default/xdg-terminal-exec/hyprland-xdg-terminals.list", "/usr/share/xdg-terminal-exec/hyprland-xdg-terminals.list", "xdg-terminals.list"),
("default/applications/mimeapps.list", "/usr/share/applications/mimeapps.list", "mimeapps.list"),
("default/systemd/user/bt-agent.service", "/usr/lib/systemd/user/bt-agent.service", "systemd/user/bt-agent.service"),
("default/systemd/user/omarchy-sleep-lock.service", "/usr/lib/systemd/user/omarchy-sleep-lock.service", "systemd/user/omarchy-sleep-lock.service"),
("default/systemd/user/omarchy-recover-internal-monitor.service", "/usr/lib/systemd/user/omarchy-recover-internal-monitor.service", "systemd/user/omarchy-recover-internal-monitor.service"),
("default/fonts/omarchy/omarchy.ttf", "/usr/share/fonts/omarchy/omarchy.ttf", "omarchy.ttf"),
]
for source, destination, legacy in package_defaults:
if not (root / source).exists():
errors.append(f"missing package default source: {source}")
if (root / "config" / legacy).exists():
errors.append(f"legacy path still in config/: {legacy}")
if destination and (source not in pkgbuild or destination not in pkgbuild):
errors.append(f"PKGBUILD does not explicitly install {source} -> {destination}")
if errors:
print("\n".join(errors), file=sys.stderr)
sys.exit(1)
PY
pass "package-owned defaults live outside config"
mapfile -t migrations < <(find "$ROOT/migrations" -maxdepth 1 -type f -name '*.sh' -printf '%f\n' | sort)
[[ ${#migrations[@]} -eq 0 ]] || fail "4.0 upgrade is not modeled as a migration"
pass "4.0 upgrade is handled outside the migration runner"
+1 -1
View File
@@ -4,7 +4,7 @@ set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
service="$ROOT/config/systemd/user/bt-agent.service"
service="$ROOT/default/systemd/user/bt-agent.service"
grep -Fx 'ExecCondition=/usr/bin/systemctl is-active --quiet bluetooth.service' "$service" >/dev/null
pass "bt-agent skips when bluetooth.service is inactive"