Bring docs/ up to date and cover the undocumented subsystems
Every reference doc was audited claim-by-claim against the code. file-layout and omarchy-shell were the most decayed (renamed commands, the etc/ overrides source split, dead IPC entry points and example keys); update-process lagged the recent pipeline changes and gains a channels section; theming and audio-tuning were accurate but thin around their lifecycles. New reference docs for the subsystems that had none: the menu system, the CLI router, the notification daemon, and the non-acceptance test architecture. AGENTS.md links the two of those agents will need most. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
b68c22208a
commit
f4189398bc
@@ -90,7 +90,8 @@ Exceptions are allowed for migration and package-helper scripts where the helper
|
||||
|
||||
# Menu
|
||||
|
||||
- The menu definition lives in `default/omarchy/omarchy-menu.jsonc`.
|
||||
- The menu definition lives in `default/omarchy/omarchy-menu.jsonc`;
|
||||
[`docs/menu.md`](docs/menu.md) covers the schema, guards, and providers.
|
||||
- Do not add `aliases` to new menu entries. Aliases are reserved for
|
||||
established alternate names users already type, kept for compatibility.
|
||||
|
||||
@@ -102,7 +103,9 @@ Exceptions are allowed for migration and package-helper scripts where the helper
|
||||
|
||||
# Tests
|
||||
|
||||
Run focused automated tests for the area you changed. Current test entry points:
|
||||
Run focused automated tests for the area you changed;
|
||||
[`docs/testing.md`](docs/testing.md) covers how the suites are shaped. Current
|
||||
test entry points:
|
||||
|
||||
- `./test/all` - aggregate runner for CLI and shell tests; it intentionally does not run graphical acceptance tests
|
||||
- `./test/cli` - CLI routing, command metadata, theme helpers, and safe dispatch coverage
|
||||
|
||||
+19
-2
@@ -12,7 +12,9 @@ default/audio/tunings/<vendor>-<model>/
|
||||
```
|
||||
|
||||
`on` renders the graph into `~/.config/pipewire/omarchy-speaker-tuning.conf.d/`
|
||||
and runs it as its own PipeWire client via `omarchy-speaker-tuning.service`, rather than
|
||||
(plus the shared host config `omarchy-speaker-tuning.conf`, from
|
||||
`default/audio/filter-chain-host.conf`) and runs it as its own PipeWire client via
|
||||
`omarchy-speaker-tuning.service`, rather than
|
||||
loading it into the audio daemon. The daemon only reads its own config at startup,
|
||||
so a daemon-loaded tuning could only be switched by restarting PipeWire — which
|
||||
drops every PulseAudio client's connection, and applications that do not reconnect
|
||||
@@ -40,6 +42,13 @@ omarchy audio tuning status # installed? in use? what matches?
|
||||
`match` and `fronted-sink` are also accepted; they exist for the install hooks
|
||||
and the sink-listing scripts rather than for daily use.
|
||||
|
||||
`on` verifies its own work: it waits for the tuning sink to appear and confirms
|
||||
its output linked to the expected physical sink, and uninstalls everything again
|
||||
on either failure — so a broken tuning cannot be left half-applied. It also
|
||||
switches the default sink to the tuning and moves existing app streams onto it
|
||||
(`off` reverses both). When everything is already installed and linked, `on` is
|
||||
a no-op; pass `--force` when iterating on a tuning in place.
|
||||
|
||||
## Adding a tuning
|
||||
|
||||
Add a directory with a `tuning.conf` and a `filter-chain.conf`. No new command is
|
||||
@@ -59,8 +68,16 @@ been validated on:
|
||||
match_sku=("0DB9" "0DBA") # XPS 14 and XPS 16
|
||||
```
|
||||
|
||||
Only the first *defined* key is consulted, in the order `match_command`,
|
||||
`match_sku`, `match_dmi` — keys below a defined one are ignored, and a tuning
|
||||
defining none never matches.
|
||||
|
||||
`sink_pattern` is required whichever method you use, since the graph's target sink
|
||||
is substituted from it.
|
||||
is substituted from it. The graph must also keep two fixed properties: the sink
|
||||
node is named `omarchy_speaker_tuning` (the install checks for that exact name,
|
||||
so renaming it silently fails verification), and its output stream sets
|
||||
`node.dont-fallback` alongside `node.dont-move`, so WirePlumber cannot link it
|
||||
somewhere else when the target sink is absent at host start.
|
||||
|
||||
Gate narrowly and widen as models are validated; a tuning aimed at the wrong
|
||||
drivers can sound worse than none and can stress them. When a tuning covers a model
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
# The Omarchy CLI router
|
||||
|
||||
`bin/omarchy` maps spaced commands onto the flat `bin/omarchy-*` namespace:
|
||||
`omarchy theme set foo` becomes `exec bin/omarchy-theme-set foo`. There is no
|
||||
registry to maintain — every executable `bin/omarchy-*` file is a command, and
|
||||
its filename is its default route. Metadata comments in the file header refine
|
||||
how it presents and routes; the keys are documented in
|
||||
[`agents/skills/command-metadata.md`](../agents/skills/command-metadata.md).
|
||||
This document covers what that guide does not: how resolution and dispatch
|
||||
actually work.
|
||||
|
||||
## How a binary becomes routes
|
||||
|
||||
The stem after `omarchy-` splits at the first hyphen: `omarchy-theme-set` gets
|
||||
group `theme` and name `set`, with remaining hyphens becoming spaces
|
||||
(`omarchy-hw-asus-rog` → group `hw`, name `asus rog`). A single-segment stem
|
||||
(`omarchy-update`) is the root command of its own group, with an empty name.
|
||||
|
||||
Every command registers two routes: the canonical route `omarchy <group>
|
||||
<name>` after metadata overrides, and the filename route with *all* hyphens
|
||||
turned to spaces. When metadata moves nothing, they are the same route. When it
|
||||
does, both keep working — `# omarchy:name=gaming xbox-cloud` on
|
||||
`omarchy-install-gaming-xbox-cloud` keeps the hyphen inside the name, so
|
||||
`omarchy install gaming xbox-cloud` is canonical while the filename route
|
||||
`omarchy install gaming xbox cloud` still resolves. An explicitly *empty* `#
|
||||
omarchy:name=` makes a command the root of its group: `omarchy-menu-share` sets
|
||||
`group=share` and an empty name, so its canonical route is `omarchy share`
|
||||
while `omarchy menu share` remains as the filename route. Alias routes register
|
||||
the same way but are flagged, so listings show them as aliases rather than
|
||||
commands.
|
||||
|
||||
Two routes claiming different binaries is a collision: the first registration
|
||||
wins, dispatch is unaffected, and the conflict is recorded for `omarchy
|
||||
commands --check` to report. Hidden commands (`# omarchy:hidden=true`) still
|
||||
register and dispatch normally — hiding only removes them from listings, which
|
||||
is how install-time plumbing like `omarchy apply hardware` stays callable
|
||||
without being browsable.
|
||||
|
||||
Metadata is read from the comment header only: the first 80 lines, stopping at
|
||||
the first non-comment line, so metadata-shaped comments after code never take
|
||||
effect. Malformed `omarchy:` lines and unknown keys are ignored rather than
|
||||
fatal — a typo degrades a command to its filename route instead of breaking the
|
||||
router. The first plain comment line doubles as a fallback summary, and a
|
||||
command with no comments at all still gets a generated one, though `--check`
|
||||
demands the explicit form (below).
|
||||
|
||||
## Dispatch
|
||||
|
||||
Resolution is longest-prefix: the router tries the full argument list as a
|
||||
route, then drops trailing words until something matches. Whatever it drops is
|
||||
passed to the binary as arguments. This runs in two passes.
|
||||
|
||||
The fast path joins argument prefixes with hyphens and checks for an
|
||||
executable file: `omarchy theme set foo` probes `omarchy-theme-set-foo`, then
|
||||
`omarchy-theme-set`, which exists — resolved without reading a single metadata
|
||||
header. This exists because plain dispatch is the hot path: parsing the
|
||||
headers of several hundred binaries on every invocation is measurable
|
||||
latency (`omarchy dev benchmark cli` tracks it), and a filename probe is a few
|
||||
stat calls. Metadata loads lazily, only for the resolved command when help is
|
||||
needed.
|
||||
|
||||
When no filename matches — metadata-moved routes like `omarchy share`, and
|
||||
aliases like `omarchy screenshot` — the router falls back to loading all
|
||||
metadata and resolving against the registered route table, with the same
|
||||
longest-prefix rule.
|
||||
|
||||
Both paths intercept `--help`/`-h` *anywhere* in the leftover arguments, not
|
||||
just the first one. Resolution can succeed with unresolved words still ahead of
|
||||
the flag — `omarchy update aur --help` resolves `update` with leftovers `aur
|
||||
--help` — and checking only the first leftover once let that invocation start a
|
||||
real update. A `--` ends the scan: everything after it belongs to the command,
|
||||
so `omarchy foo run -- --help` forwards the flag. `--json` alongside `--help`
|
||||
switches the help output to the command's JSON record; `--json` alone is just
|
||||
an argument for the command.
|
||||
|
||||
Bare invocations are also guarded. If a command declares required arguments
|
||||
(its `args` metadata, minus `[bracketed]` optional parts, is non-empty) and
|
||||
none were given, the router shows help instead of executing — `omarchy theme
|
||||
set` prints usage rather than running an interactive setter. A bare group name
|
||||
with child commands shows the group help.
|
||||
|
||||
Dispatch is `exec`: the router process is replaced, the binary sees only the
|
||||
leftover arguments, and the exit code is the binary's own. The router itself
|
||||
exits 127 for unknown routes or missing binaries.
|
||||
|
||||
When nothing resolves, the router tries a prefix listing — `omarchy hw asus`
|
||||
prints every command whose usage starts with that prefix — and otherwise
|
||||
errors with a "did you mean" suggestion (a known route extending the first
|
||||
word) and a pointer to `omarchy commands --all`.
|
||||
|
||||
## Groups and the top-level listing
|
||||
|
||||
Group help is synthesized from metadata, not written anywhere. A command
|
||||
belongs to a group when either its metadata group or its filename group
|
||||
matches, listed under the route that fits the group being viewed: `omarchy
|
||||
menu --help` shows `omarchy-menu-share` as `omarchy menu share`, while its
|
||||
canonical `omarchy share` stands alone. On the fast path, group help loads
|
||||
only that group's filename-prefixed binaries rather than everything.
|
||||
|
||||
The top-level `omarchy` listing is driven entirely by the hand-curated
|
||||
`GROUP_DESCRIPTIONS` table in `bin/omarchy`, which also titles each group's
|
||||
help. An entry there advertises the group even when every command in it is
|
||||
hidden — which is exactly why `apply` and `provision` have none: they route,
|
||||
but a listing entry would put install-time plumbing back in front of users
|
||||
(see the Command Naming section of `AGENTS.md`). Adding a browsable command
|
||||
group means adding its `GROUP_DESCRIPTIONS` entry; adding hidden plumbing
|
||||
means deliberately not doing so.
|
||||
|
||||
## Introspection
|
||||
|
||||
`omarchy commands` prints every non-hidden command with its summary, plus an
|
||||
alias table. `--all` includes hidden commands, `--markdown` emits a table, and
|
||||
`--json` emits full records: route, binary, group, name, summary, flags, args,
|
||||
examples, aliases, `filename_route`, and `routes` (the union of everything
|
||||
that resolves to the binary). Per-command JSON comes from `omarchy <route>
|
||||
--help --json`.
|
||||
|
||||
`omarchy commands --check` is the metadata lint, run by `test/cli`. It fails
|
||||
on:
|
||||
|
||||
- route collisions between binaries
|
||||
- a missing explicit `# omarchy:summary=` — a plain-comment fallback renders
|
||||
in help but does not satisfy the check
|
||||
- invalid boolean metadata: `hidden` and `requires-sudo` must be `true` or
|
||||
omitted, never `false`
|
||||
- a registered command whose binary is missing or not executable
|
||||
|
||||
When debugging a routing surprise, `omarchy <route> --help` shows the resolved
|
||||
binary and, when it differs, the filename route; `omarchy commands --all
|
||||
--json` shows every route the router knows.
|
||||
+80
-46
@@ -5,8 +5,8 @@ system.
|
||||
|
||||
## Mental model
|
||||
|
||||
Two Arch packages are built from this one repo (PKGBUILDs live in
|
||||
`omarchy-pkgs/pkgbuilds/`):
|
||||
Two Arch packages are built from this one repo (PKGBUILDs live in the
|
||||
separate `omarchy-pkgs` repository, under `pkgbuilds/`):
|
||||
|
||||
- **`omarchy`** — runtime binaries (`bin/`, including `bin/omarchy-dev-*`),
|
||||
install/finalize scripts (`install/`), migrations, themes, and the
|
||||
@@ -22,19 +22,24 @@ Two Arch packages are built from this one repo (PKGBUILDs live in
|
||||
(`omarchy-debug`, `omarchy-debug-idle`, `omarchy-upload-log`) needed by
|
||||
the live ISO env.
|
||||
|
||||
Two other packages live in `omarchy-pkgs/` but stand alone:
|
||||
Two other packages live in `omarchy-pkgs` but stand alone:
|
||||
`omarchy-keyring` (GPG keys for pacman) and `omarchy-nvim` (the Neovim
|
||||
setup; independently seeds `/etc/skel`).
|
||||
|
||||
Some trees ship in neither package and exist only in the repo: `manual/`
|
||||
(user manual chapters), `agents/skills/` (contributor task guides), `docs/`,
|
||||
`test/`, and `plans/`.
|
||||
|
||||
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.
|
||||
2. **Finalize** — `omarchy-provision-user` (routed as `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.
|
||||
@@ -42,6 +47,13 @@ Three layers populate `$HOME`:
|
||||
`/etc/skel` only fires at user creation. Existing users picking up new
|
||||
defaults must use the resync command.
|
||||
|
||||
Deferred-provisioning installs (`omarchy-apply-system --defer-provisioning`)
|
||||
create no user at all: the ISO leaves `/var/lib/omarchy/provisioning/pending`
|
||||
behind, which arms `omarchy-provision-owner.service` (shipped from
|
||||
`install/provisioning/`, alongside the factory-reset finish unit and
|
||||
`setup-form.sh`). On first boot `bin/omarchy-provision-owner` creates the
|
||||
user on tty1 and runs the finalize step itself.
|
||||
|
||||
Current generated theme state lives under
|
||||
`~/.local/state/omarchy/current/`. Keep `~/.config/omarchy/` for files a user
|
||||
may intentionally version in a dotfile manager, such as user themes, hooks,
|
||||
@@ -82,7 +94,12 @@ applications/icons/* ──► omarchy-settings /usr/share/icons/h
|
||||
|
||||
etc/** ──► omarchy-settings /etc/** (drop-ins we own outright)
|
||||
├─ mkinitcpio.conf.d/{omarchy_hooks,thunderbolt_module}.conf
|
||||
└─ limine-entry-tool.d/{omarchy-defaults,omarchy-uki}.conf
|
||||
├─ limine-entry-tool.d/{omarchy-defaults,omarchy-uki}.conf
|
||||
├─ NetworkManager/, sudoers.d/, sysctl.d/, tmpfiles.d/,
|
||||
│ profile.d/omarchy.sh, … (a summary — `ls etc/` for the full ~17-entry tree)
|
||||
└─ security/faillock.conf, nsswitch.conf,
|
||||
cups/cups-browsed.conf, plymouth/plymouthd.conf /usr/share/omarchy/etc-overrides/
|
||||
→ /etc/* (post_install cp -f, see below)
|
||||
|
||||
default/limine/limine.conf ──► omarchy-settings /usr/share/omarchy/default/limine/limine.conf
|
||||
default/limine/default.conf ──► omarchy-settings /usr/share/omarchy/default/limine/default.conf
|
||||
@@ -95,7 +112,8 @@ default/** ──► omarchy-settings /usr/share/omarchy
|
||||
│ (sourced by every shell/session entry point; see "Env bootstrap")
|
||||
├─ 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/
|
||||
├─ hypr/toggles/*.lua (flags,
|
||||
│ single-window-aspect-ratio, window-no-gaps) /etc/skel/.local/state/omarchy/toggles/hypr/
|
||||
├─ nautilus-python/extensions/*.py /etc/skel/.local/share/nautilus-python/extensions/
|
||||
├─ tensaku/state.toml /etc/skel/.local/state/tensaku/state.toml
|
||||
├─ uwsm/env.d/10-omarchy /usr/share/uwsm/env.d/
|
||||
@@ -104,18 +122,16 @@ default/** ──► omarchy-settings /usr/share/omarchy
|
||||
│ + symlink /etc/fonts/conf.d/50-omarchy.conf
|
||||
├─ xdg-terminal-exec/*.list /usr/share/xdg-terminal-exec/
|
||||
├─ applications/mimeapps.list /usr/share/applications/mimeapps.list
|
||||
├─ systemd/user/*.{service,path} /usr/lib/systemd/user/
|
||||
├─ systemd/user/*.service /usr/lib/systemd/user/
|
||||
├─ systemd/user/app.slice.d/10-oomd.conf /usr/lib/systemd/user/app.slice.d/
|
||||
├─ systemd/system-sleep/unmount-fuse /usr/lib/systemd/system-sleep/
|
||||
├─ systemd/system-sleep/{force-igpu,
|
||||
│ keyboard-backlight,unmount-fuse} /usr/lib/systemd/system-sleep/
|
||||
├─ systemd/zram-generator.conf.d/90-omarchy.conf /usr/lib/systemd/zram-generator.conf.d/
|
||||
├─ 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)
|
||||
└─ plymouth/ /usr/share/plymouth/themes/omarchy/
|
||||
|
||||
logo.{txt,svg}, icon.{txt,png} ──► omarchy-settings /usr/share/omarchy/ (resync source)
|
||||
/usr/share/pixmaps/omarchy.png
|
||||
@@ -126,9 +142,10 @@ logo.{txt,svg}, icon.{txt,png} ──► omarchy-settings /usr/share/omarchy
|
||||
### 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
|
||||
`security/faillock.conf`, `cups/cups-browsed.conf`, `plymouth/plymouthd.conf`)
|
||||
are owned by upstream Arch packages, so we can't install over them via pacman
|
||||
without a file conflict. Instead their sources (under `etc/` in the repo;
|
||||
`.bashrc` from `default/bashrc`) ship at
|
||||
`/usr/share/omarchy/etc-overrides/` and the `omarchy-settings` `post_install`
|
||||
/ `post_upgrade` scriptlet `cp -f`'s them into place.
|
||||
|
||||
@@ -146,6 +163,10 @@ Single source of truth for `OMARCHY_PATH` and dev-link-aware `PATH`. It:
|
||||
- Prepends `$OMARCHY_PATH/bin` to `PATH` **only when** `OMARCHY_PATH` is
|
||||
not `/usr/share/omarchy`. On a production install the binaries are
|
||||
already on `PATH` as `/usr/bin/omarchy-*` via the `omarchy` package.
|
||||
- Appends `~/.local/share/mise/shims` and `~/.local/bin` so login shells and
|
||||
the uwsm session find mise-managed tools — kept in sync with the PAM `PATH`
|
||||
line written by `install/config/ssh-command-path.sh`, which covers SSH
|
||||
commands that run no shell setup at all.
|
||||
|
||||
Sourced by every entry point that needs the env set:
|
||||
|
||||
@@ -171,15 +192,17 @@ yet and silently runs the packaged copy of one it has. The drop-in is validated
|
||||
with `visudo -c` before install and removed by `omarchy-dev-unlink`; unlike
|
||||
`/etc/omarchy.conf`, it takes effect without a reboot.
|
||||
|
||||
## Runtime finalization (`omarchy-finalize-user`)
|
||||
## Runtime finalization (`omarchy-provision-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/agents/skills/omarchy`. Symlinks (not copies) so
|
||||
`omarchy dev link` against a dev checkout repoints them correctly.
|
||||
- Skill symlinks `~/.{agents,claude,codex,pi/agent}/skills/<name>` →
|
||||
`$OMARCHY_PATH/default/agents/skills/<name>`, looping over every skill
|
||||
directory there (currently `omarchy` and `diagnose-crash`) so new skills
|
||||
need no edit. 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).
|
||||
- Hyprland's package-owned default input reads `XKBLAYOUT` / `XKBVARIANT`
|
||||
@@ -187,17 +210,19 @@ It only does the things `/etc/skel` can't:
|
||||
- `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 (asus mic/mixer, framework f13 audio, …).
|
||||
- Sources `install/user/all.sh` — theme, chromium, git, xcompose, mise,
|
||||
keyring, per-user hardware quirks (asus mic/mixer, framework f13 audio, …).
|
||||
- On `--first-install`, marks every shipped user migration as already applied
|
||||
for the freshly-created user.
|
||||
|
||||
Idempotency marker: `~/.local/state/omarchy/done/finalize-user`, managed
|
||||
by `omarchy-done`.
|
||||
|
||||
The ISO calls it as `omarchy-finalize-user --force --first-install` in the
|
||||
The ISO calls it as `omarchy-provision-user --force --first-install` in the
|
||||
target chroot as the install user, after `omarchy-apply-system` has finished
|
||||
the root-side work.
|
||||
the root-side work. `omarchy-provision-owner` makes the same call (with
|
||||
`OMARCHY_SETUP_CONTEXT=provision-owner`) when it creates the user during
|
||||
deferred first-boot provisioning.
|
||||
|
||||
## Migrations (`omarchy-migrate`)
|
||||
|
||||
@@ -213,9 +238,10 @@ machine-wide repairs should no-op when another user already applied them.
|
||||
|
||||
Each graphical user has `omarchy-migrate-notify.service`, started once per login
|
||||
through `WantedBy=graphical-session.target` and ordered after that target so
|
||||
notification actions can safely launch through UWSM. The package also ships
|
||||
`omarchy-update-user-notify.service` as a symlink onto it, so users enabled
|
||||
under the old unit name keep working before they reach migration `1785095882`.
|
||||
notification actions can safely launch through UWSM. The `omarchy-pkgs`
|
||||
PKGBUILD has shipped `omarchy-update-user-notify.service` as a symlink onto
|
||||
it, so users enabled under the old unit name keep working before they reach
|
||||
migration `1785095882`.
|
||||
It runs `omarchy-migrate-notify` as
|
||||
that user, which checks `omarchy-migrate --pending`. If this user has missing
|
||||
migration state, it shows a notification that opens a terminal for
|
||||
@@ -234,27 +260,31 @@ transaction in the already-visible update terminal, then runs
|
||||
|
||||
## First-run (`omarchy-provision-first-run`)
|
||||
|
||||
Runs once on first interactive login, after the user manager is live. Used
|
||||
for steps that need a running graphical session and/or a working user
|
||||
systemd instance:
|
||||
Runs once on first interactive login, after the user manager is live. It
|
||||
first runs `omarchy-provision-user || true` so finalize catches up if it
|
||||
never ran, then handles the steps that need a running graphical session
|
||||
and/or a working user systemd instance:
|
||||
|
||||
- `omarchy-hook-install post-update install-voxtype.hook` — register the
|
||||
Voxtype post-update hook.
|
||||
- `install/user/first-run/enable-user-units.sh` — `systemctl --user enable`
|
||||
the shipped user units (`bt-agent`, `omarchy-sleep-lock`,
|
||||
`omarchy-recover-internal-monitor`, `omarchy-migrate-notify.service`,
|
||||
`omarchy-fcitx5.service`).
|
||||
- `omarchy-hook-install post-update` for the three shipped hooks
|
||||
(`install-voxtype.hook`, `setup-fingerprint.hook`, `setup-agent.hook`).
|
||||
- `install/user/first-run/enable-user-units.sh` — daemon-reload, then
|
||||
`systemctl --user enable --now` the shipped user units (`bt-agent`,
|
||||
`omarchy-sleep-lock`, `omarchy-recover-internal-monitor`,
|
||||
`omarchy-migrate-notify.service`, `omarchy-fcitx5.service`,
|
||||
`omarchy-crash-watch.service`) so they run in the first session too.
|
||||
Done here, not at finalize, because
|
||||
the user manager isn't reachable from the ISO chroot; `ConditionPath*`
|
||||
in the unit files keeps services inert when they don't apply.
|
||||
- `install/user/first-run/gnome-theme.sh`,
|
||||
`install/user/first-run/gtk-primary-paste.sh` — GNOME/GTK settings that
|
||||
need the dconf daemon.
|
||||
- `install/user/first-run/audio-tuning.sh` — apply speaker tuning.
|
||||
- `install/user/first-run/welcome.sh` — keybindings toast that greets the
|
||||
first login and opens the cheatsheet when clicked.
|
||||
- `install/user/first-run/wifi.sh` — Wi-Fi/update toasts (waits for a live
|
||||
notification server before firing, then waits detached on `nm-online` so the
|
||||
update prompt only lands once there is a connection).
|
||||
first login and opens the cheatsheet when clicked. The caller runs
|
||||
`omarchy-notification-wait` once before this and the Wi-Fi step, so both
|
||||
toasts land on a live notification server.
|
||||
- `install/user/first-run/wifi.sh` — Wi-Fi/update toasts (waits detached on
|
||||
`nm-online` so the update prompt only lands once there is a connection).
|
||||
|
||||
The entire sequence has one idempotency marker:
|
||||
`~/.local/state/omarchy/done/first-run-user`, managed by `omarchy-done`.
|
||||
@@ -274,8 +304,8 @@ the legacy finalization marker from `~/.local/state/omarchy/` into `done/`.
|
||||
finalization. It sources:
|
||||
|
||||
- `install/config/all.sh` — theme links, lockout limits, lockscreen PAM,
|
||||
powerprofilesctl shebang fix, docker setup, Snapper retention, locate
|
||||
index tuning, service enablement, firewall.
|
||||
powerprofilesctl shebang fix, SSH command path and keepalive, docker setup,
|
||||
Snapper retention, locate index tuning, service enablement, firewall.
|
||||
- `install/hardware/all.sh` via `omarchy-apply-hardware` — vendor- and
|
||||
device-specific kernel modules, udev rules, microcode, wireless regdom,
|
||||
ASUS / Framework / Intel / Apple / Lenovo quirks.
|
||||
@@ -285,6 +315,10 @@ finalization. It sources:
|
||||
Logging goes to `/var/log/omarchy-install.log` via
|
||||
`install/helpers/logging.sh`.
|
||||
|
||||
The package lists the ISO pacstraps live at `install/omarchy-base.packages`
|
||||
and `install/omarchy-other.packages`; the ISO builder also reads them when
|
||||
constructing its offline mirror.
|
||||
|
||||
## Explicit resync (`omarchy-reinstall-configs`)
|
||||
|
||||
When an existing user wants to reset to shipped defaults:
|
||||
@@ -310,10 +344,10 @@ return to the packaged default.
|
||||
| --- | --- |
|
||||
| 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/path in `/usr/lib`) | `default/`, document the mapping in `default/package-defaults.tsv`, then add the `install -Dm644` line in `omarchy-settings` PKGBUILD |
|
||||
| `/etc/` file owned by an upstream package | `etc/` (see `etc/security/faillock.conf`), then add to `etc-overrides` in `omarchy-settings` PKGBUILD + scriptlet |
|
||||
| Package-owned system file (e.g. systemd user service in `/usr/lib`) | `default/`, 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` |
|
||||
| Runtime tweak that needs `$HOME` or live system state | extend `omarchy-provision-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 `install/config/all.sh` or `install/hardware/all.sh` |
|
||||
| One-time fix for existing installs | `migrations/<unix-timestamp>.sh` |
|
||||
| Package-owned path something else may already write | Prefer a path nothing else writes, such as a vendor drop-in under `/usr/lib`. Otherwise the `--overwrite` entry in `bin/omarchy-update-system-pkgs` has to ship a release before the file |
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
# The Omarchy menu
|
||||
|
||||
The menu is the `omarchy.menu` plugin of the Quickshell desktop, with its
|
||||
content defined as data in `default/omarchy/omarchy-menu.jsonc` (read at
|
||||
runtime from `$OMARCHY_PATH`) and overlaid by the user's
|
||||
`~/.config/omarchy/extensions/omarchy-menu.jsonc`. The shell parses both files
|
||||
at startup and watches them for changes, so the keybind → IPC → visible path
|
||||
never shells out to parse anything, and edits to either file take effect
|
||||
without restarting the shell. Rendering and behavior live in
|
||||
`shell/plugins/menu/Menu.qml`; the pure logic lives in
|
||||
`shell/plugins/menu/MenuModel.js`, which is plain JavaScript that Node can
|
||||
also load — the shell tests in `test/shell.d/menu-test.sh` and
|
||||
`menu-guards-test.sh` exercise it directly.
|
||||
|
||||
JSONC here means JSON plus comments and trailing commas, stripped by the
|
||||
parser rather than a real JSONC grammar: only whole-line `//` comments are
|
||||
removed, so an inline trailing comment breaks the parse. A file that fails to
|
||||
parse contributes no entries — a broken user extension silently drops every
|
||||
user entry while the shipped menu keeps working.
|
||||
|
||||
## Entry schema
|
||||
|
||||
Entries are object keys. The dotted id is the tree: `trigger.share.file` is a
|
||||
child of `trigger.share`, and an id with no dot sits on the root menu. There
|
||||
is no separate parent field to keep in sync — where an entry appears follows
|
||||
from what it is called (an explicit `parent` is accepted but nothing shipped
|
||||
uses one).
|
||||
|
||||
Kind is inferred rather than declared: an entry with `action` is an action,
|
||||
one with `target` is a link to another submenu, and anything else is a
|
||||
submenu. The fields:
|
||||
|
||||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `icon` | Glyph in the icon column (usually Nerd Font) |
|
||||
| `iconFont` | Font family for the glyph when it differs from the menu font — how the private `omarchy` font's brand glyphs render |
|
||||
| `label` | Visible row title; defaults to the id |
|
||||
| `title` | Header text when the submenu is open; defaults to `label`. Lets a row read "Browser" under Defaults while the open menu says "Default Browser" |
|
||||
| `action` | Shell command to run, detached, when selected |
|
||||
| `target` | Existing submenu id to open; makes the row a link |
|
||||
| `provider` | Runtime row source for this submenu (see Providers) |
|
||||
| `aliases` | Alternate `omarchy menu summon <name>` routes; also searchable |
|
||||
| `description` | Subtitle shown while searching, and extra search text matched by whole word |
|
||||
| `when` / `checked` / `disabled` | Shell conditions (see Guards) |
|
||||
|
||||
Do not add `aliases` to new entries. They are reserved for established
|
||||
alternate names users already type (`power-menu`, `settings`), kept for
|
||||
compatibility — see `AGENTS.md`. Search does not need them: labels, the last
|
||||
id segment, and descriptions are all searchable.
|
||||
|
||||
## Load and merge
|
||||
|
||||
`mergeMenuSources` overlays user entries on the defaults per key: reusing a
|
||||
shipped id replaces only the fields you declare, so an extension can retitle
|
||||
or re-icon a row without re-declaring its action, and an overridden entry
|
||||
keeps its original position in the list. New ids append. A `root` entry is
|
||||
injected if neither file declares one.
|
||||
|
||||
The sample extension at `config/omarchy/extensions/omarchy-menu.jsonc`
|
||||
(refreshed into `~/.config/`) documents the format in its header and ships
|
||||
only comments, so the default state adds nothing.
|
||||
|
||||
## Guards
|
||||
|
||||
`when`, `checked`, and `disabled` are bash conditions. The shell never
|
||||
evaluates them on the open path: all guards in the menu are batched into a
|
||||
single bash process per (re)load and per open, reporting `<id>:<w|c|d>:<0|1>`
|
||||
lines. The menu opens immediately on the previous evaluation's answers, so
|
||||
the batch's runtime is exactly how long a row can contradict the state it
|
||||
describes — which is why the batch works hard to be fast:
|
||||
|
||||
- Package and command presence (`omarchy-pkg-present` and friends) are
|
||||
answered in-process from one `pacman -Q` snapshot instead of a fork per
|
||||
row. The snapshot resolves provides too, so gvim answers for vim.
|
||||
- Commands that several rows read a value from — every Defaults > Browser row
|
||||
compares against `$(omarchy-default-browser)` — run once, with the captured
|
||||
answer substituted into each expression. The reader list is
|
||||
`GUARD_READERS` in `MenuModel.js`; a new `$(omarchy-...)` reader used by
|
||||
more than one row must be added there, and `menu-guards-test.sh` fails the
|
||||
build if it is not.
|
||||
|
||||
The three guards differ in what failure means:
|
||||
|
||||
- `when` hides the row when it fails. A submenu whose visible descendants
|
||||
are all hidden disappears with them (provider-backed submenus stay, since
|
||||
their rows load on demand).
|
||||
- `checked` appends ✓ when it succeeds — the "this is the current choice"
|
||||
marker on defaults, DNS, channel rows.
|
||||
- `disabled` keeps the row listed but dims it, marks it ✓, and makes it
|
||||
unselectable: cursor, pointer, and Enter all step over it, and search omits
|
||||
it. The Install submenus use it so software already on the machine reads as
|
||||
installed rather than vanishing from the list it was installed from — the
|
||||
list stays a catalog of what Omarchy can install. Since a dimmed row means
|
||||
"you already have this", it earns the same ✓ as `checked` does elsewhere.
|
||||
|
||||
Install rows should therefore carry `disabled:` with the presence check, not
|
||||
`when:`; Remove rows are the opposite, hiding via `when:` what is not there
|
||||
to remove. `menu-test.sh` enforces the Install side of this convention.
|
||||
|
||||
## Providers
|
||||
|
||||
A submenu with `provider: "name"` gets its rows at runtime instead of from
|
||||
JSONC. The names are defined by the shell, not the menu file — an extension
|
||||
can point a submenu at an existing provider but cannot declare a new one:
|
||||
|
||||
- `apps` is QML-native: rows come from the shared AppLibrary (desktop
|
||||
entries), carrying image icons, launch feedback, and uninstall support like
|
||||
the launcher. App rows are searchable by their desktop Keywords but never
|
||||
routable, so an installed app cannot capture a menu route (htop ships
|
||||
`Keywords=system;...`, and SUPER+ESCAPE must still open the system menu).
|
||||
- `fonts` and `power-profiles` are bash one-liners in the `providers` map in
|
||||
`Menu.qml`. The contract is one tab-delimited line per row:
|
||||
`label\tvalue\tcurrent`. The row whose value equals `current` gets the ✓
|
||||
icon, and selection runs the spec's `actionFor(value)`. Row ids are
|
||||
`<menuId>.<slugify(value)>`, with a `-` appended on collision so two values
|
||||
that slugify alike cannot silently drop a row.
|
||||
|
||||
A provider marked `volatile` re-runs every time its submenu is entered — a
|
||||
font installed since the shell started shows up without a restart — but not
|
||||
on search keystrokes, which would restart the same enumeration per key.
|
||||
|
||||
`swapProviderRows` in `MenuModel.js` merges the results: rows carry the id of
|
||||
the submenu that produced them, so a provider that runs again drops its
|
||||
previous batch without disturbing static children declared in JSONC. Both it
|
||||
and the app merge return fresh item maps for the caller to assign in one go —
|
||||
writing into a map held by a QML `var` property occasionally loses the write,
|
||||
which used to duplicate launcher rows. Never mutate `root.items` in place.
|
||||
|
||||
Adding a provider means adding an entry to the `providers` map in `Menu.qml`
|
||||
(script, icon, `actionFor`, optionally `volatile`) and pointing a submenu at
|
||||
it with `provider:`.
|
||||
|
||||
## Driving the menu from the CLI
|
||||
|
||||
`bin/omarchy-menu` is a thin wrapper over the standard plugin IPC surface:
|
||||
|
||||
```bash
|
||||
omarchy menu # toggle the root menu
|
||||
omarchy menu toggle system # open at a route, or close if already open
|
||||
omarchy menu summon style.theme # always open (no close-if-visible)
|
||||
omarchy menu close
|
||||
omarchy menu refresh # re-parse the JSONC files
|
||||
omarchy menu ping
|
||||
```
|
||||
|
||||
A route is an item id or a declared alias, case-insensitive, with
|
||||
underscores normalized to dashes. An exact id beats any alias; empty input,
|
||||
`go`, and `menu` mean root; an unknown string falls through as a literal id
|
||||
so a misspelling still attempts to open that id. Summoning a route that
|
||||
resolves to an action — an alias for a leaf, like `screenrecord-stop` — runs
|
||||
the action directly instead of opening an action with no children, and a
|
||||
link is followed to its target. The default Hyprland bindings in
|
||||
`default/hypr/bindings/utilities.lua` all go through this surface
|
||||
(SUPER+SPACE toggles root, SUPER+ESCAPE the system menu, and so on).
|
||||
|
||||
## Select and input modes
|
||||
|
||||
The same plugin doubles as the system's dmenu. `omarchy-menu-select` and
|
||||
`omarchy-menu-input` summon it with a `mode: select` or `mode: input`
|
||||
payload, then block on a tempfile handshake: the shell writes the selection
|
||||
to `selectionFile` and touches `doneFile`, and cancellation (empty
|
||||
selection) exits 1. A select option is `label`, `glyph\tlabel`, or
|
||||
`glyph\tlabel\tsubtext` — the glyph shows but never returns, the subtext
|
||||
renders under the label, filters with it, and comes back as
|
||||
`label\tsubtext` so callers with same-named rows get a stable key. This is
|
||||
how the pickers behind menu actions (`omarchy-menu-plugin`,
|
||||
`omarchy-menu-timezone`, ...) present lists without owning any UI.
|
||||
@@ -0,0 +1,143 @@
|
||||
# Notifications
|
||||
|
||||
The shell is the notification daemon: `shell/plugins/notifications/Service.qml`
|
||||
hosts a Quickshell `NotificationServer` that claims `org.freedesktop.Notifications`
|
||||
on the session bus. There is no dunst or mako — anything that speaks the
|
||||
freedesktop notification protocol (notify-send, libnotify apps, Chromium web
|
||||
apps) lands in the shell, which renders it as a toast card stacked in the
|
||||
top-right corner. The pure decision logic lives in `NotificationLogic.js`,
|
||||
which is also loadable from Node so `test/shell.d/` can exercise it without
|
||||
a compositor.
|
||||
|
||||
The end-user view (hotkey notices for time, battery, weather) is in
|
||||
`manual/10-notices.md`; this document is the system shape behind it.
|
||||
|
||||
## Toast lifecycle
|
||||
|
||||
A toast lives on screen for at least 5s (low), 8s (normal), or forever
|
||||
(critical), stretched up to 30s if the sender asked for a longer
|
||||
`expire_timeout`. Hovering pauses the countdown, and a content update restarts
|
||||
it — new text deserves a full look. Left-click invokes the default action,
|
||||
right-click or the hover-revealed close button dismisses.
|
||||
|
||||
Every on-screen popup is mirrored to its own file under
|
||||
`~/.local/state/omarchy/notifications/` (one JSON line per file, named
|
||||
`<timestamp>-<id>.json`), so live toasts survive the shell restart that
|
||||
`omarchy-update` performs. When a toast leaves the screen — expiry, dismissal,
|
||||
or click — its file moves into `notifications/history/`, trimmed to the newest
|
||||
ten. That directory *is* the history: `showHistory` replays exactly what has
|
||||
been moved in there. Referenced avatars/images are copied into
|
||||
`notifications/images/`, because senders delete their originals on close.
|
||||
|
||||
`replaces_id` updates never produce a second notification signal: the server
|
||||
writes new content onto the object the service already holds, so the service
|
||||
watches the object's property-change signals and rewrites the row and its file
|
||||
in place, under the popup's original file identity. Restored rows carry ids
|
||||
from a dead server generation (ids restart from 1 each shell process), so
|
||||
they are keyed by timestamp+id and never matched against live objects — a
|
||||
fresh notification reusing an old id must not dismiss or replace them.
|
||||
|
||||
## Silencing
|
||||
|
||||
Do-not-disturb is a single boolean, persisted as the `dnd` key in
|
||||
`~/.local/state/omarchy/notifications.json` and toggled via shell IPC
|
||||
(`omarchy-shell notifications toggleDnd` / `setDnd` / `dndState`).
|
||||
`omarchy-toggle-notification-silencing` wraps the toggle and refreshes the
|
||||
bar's `omarchy.indicators` widget, whose Dnd indicator binds directly to the
|
||||
service's `doNotDisturb` property.
|
||||
|
||||
Two kinds of notification punch through DND, chosen to be intentional and
|
||||
rare:
|
||||
|
||||
- `app_name` = `omarchy-action` — Omarchy's own user-action confirmation
|
||||
toasts ("Theme changed"). The user just did something; their feedback shows.
|
||||
- urgency critical *and* `app_name` = `notify-send` — bare-CLI emergency
|
||||
alerts. Critical alone is not enough, because chat apps abuse it to force
|
||||
visibility; they set `app_name` to their brand, which fails this rule.
|
||||
|
||||
A silenced notification that anyone might look back at is written straight
|
||||
into history — "what did I miss while silenced" is what history is for.
|
||||
Ephemeral ones (the freedesktop `transient` hint, or an `app_name` of
|
||||
`notify-send`/`omarchy-action`) are dropped entirely.
|
||||
|
||||
## The sender contract
|
||||
|
||||
`bin/omarchy-notification-send` is the one way Omarchy code sends
|
||||
notifications — never raw `notify-send`. It translates its flags into
|
||||
notify-send arguments and passes any unrecognized options through:
|
||||
|
||||
| Flag | Becomes | Meaning |
|
||||
|---|---|---|
|
||||
| `-g` / `--glyph` | `--hint=string:omarchy-glyph:` | Nerd Font glyph for the icon slot when no image icon resolves |
|
||||
| `--exec` | `--hint=string:omarchy-exec:` | shell command the card runs when clicked |
|
||||
| `--image` | `--hint=string:image-path:` | the standard freedesktop image hint |
|
||||
| `--app-name` | `-a` | defaults to `omarchy-action` |
|
||||
| `-u` / `--urgency` | `-u` | defaults to `low` |
|
||||
|
||||
The defaults are the point: an unadorned `omarchy-notification-send "Done"`
|
||||
is a low-urgency user-action toast that pops through DND and is treated as
|
||||
ephemeral noise when silenced.
|
||||
|
||||
`--exec` is deliberately not a libnotify action. An action keeps the sender
|
||||
blocked waiting for `ActionInvoked`, and dies unanswered whenever the shell
|
||||
restarts underneath it — the installer toasts restart the shell as their
|
||||
first act. Carrying the command as a hint means the shell executes the click
|
||||
itself (detached, so the command outlives the shell process) from the copy it
|
||||
keeps with the popup, which the persistence files preserve: a restored toast
|
||||
clicks through exactly like a live one, and oneshot senders can exit
|
||||
immediately. For third-party clients the click falls back to the libnotify
|
||||
`default` action while the sender is alive, then to focusing the sender's
|
||||
window by class via `omarchy-hyprland-focus-app` — chat apps rarely register
|
||||
an action and just expect click-to-jump.
|
||||
|
||||
## Helper commands
|
||||
|
||||
- `omarchy-notification-wait [timeout]` — polls until the shell answers IPC
|
||||
*and* has claimed the bus name. Anything sending near session start or a
|
||||
shell restart uses it, or the toast is sent into the void.
|
||||
- `omarchy-notification-dismiss <summary>` — dismiss by summary substring,
|
||||
used by the first-run toasts once their action has been clicked.
|
||||
- `omarchy-notification-time` / `-battery` — the hotkey notices: one-line
|
||||
low-urgency glyph toasts wrapping `date` and `omarchy-battery-status`.
|
||||
- `omarchy-notification-weather` — despite the name, not a sender: it toggles
|
||||
the `omarchy.weather` shell panel.
|
||||
|
||||
Keybindings live in `default/hypr/bindings/utilities.lua`: `Super+comma`
|
||||
variants map to the IPC methods `dismissOne`, `dismissAll`, `invokeLast`,
|
||||
`showHistory`, and the silencing toggle.
|
||||
|
||||
## How subsystems plug in
|
||||
|
||||
Everything goes through the same sender contract, so the pieces are small:
|
||||
|
||||
- **Low battery** — `omarchy-battery-low` sends a critical toast and runs the
|
||||
`battery-low` hook.
|
||||
- **Crash capture** — `omarchy-crash-watch` follows the systemd-coredump
|
||||
journal stream and announces each crashed program (deduped per minute) as a
|
||||
critical toast whose `--exec` runs `omarchy-agent-crash`. It waits for the
|
||||
server first: a shell crash takes the notification server down with it, and
|
||||
that crash is the one most worth reporting.
|
||||
- **Pending migrations** — `omarchy-migrate-notify` (from its user service
|
||||
after `graphical-session.target`) waits for the server, then sends a
|
||||
critical toast whose click opens a terminal running `omarchy-migrate`,
|
||||
falling back to printing in the terminal if the hand-off fails.
|
||||
|
||||
## Reminders
|
||||
|
||||
Reminders ride on notifications rather than being their own daemon.
|
||||
`bin/omarchy-reminder <minutes> [message]` creates a transient systemd user
|
||||
timer via `systemd-run --user --collect --on-active=<minutes>m` under the
|
||||
unit name `omarchy-reminder-<minutes>m-<epoch>`; the timer's payload sends the
|
||||
reminder toast, deletes its message file, and refreshes the bar indicator.
|
||||
Custom messages are stashed in `$XDG_RUNTIME_DIR/omarchy-reminders/<unit>.message`
|
||||
since a unit name cannot carry arbitrary text. `--collect` means fired timers
|
||||
leave nothing behind.
|
||||
|
||||
The state therefore lives entirely in systemd: `show` and `clear` enumerate
|
||||
`systemctl --user list-timers "omarchy-reminder-*.timer"` — `show` as a
|
||||
summary toast, `show --json` as the JSON the bar's Reminder indicator polls
|
||||
(refreshed by the same `omarchy-shell -q omarchy.indicators refresh` call the
|
||||
timers and mutations make). `omarchy-reminder -i` summons the
|
||||
`omarchy.reminders` overlay (`shell/plugins/reminders/ReminderFlow.qml`), a
|
||||
two-step minutes/message prompt that shells back out to `omarchy-reminder` to
|
||||
do the setting.
|
||||
+51
-23
@@ -2,9 +2,12 @@
|
||||
|
||||
A single long-running [Quickshell](https://quickshell.org/) instance
|
||||
that hosts the Omarchy desktop. The bar, panels, overlays, menus, and
|
||||
services all run inside as plugins. IPC is the canonical way for CLIs
|
||||
to talk to a running shell — `omarchy-shell-ipc` auto-starts it on
|
||||
first call.
|
||||
services all run inside as plugins. Hyprland autostart launches the
|
||||
shell via `omarchy-launch-shell`; restart it with `omarchy-restart-shell`.
|
||||
IPC is the canonical way for CLIs to talk to a running shell —
|
||||
`omarchy-shell` forwards a call and fails when the shell is not running
|
||||
(`-q` makes it quiet best-effort; `OMARCHY_SHELL_IPC_TIMEOUT` bounds the
|
||||
wait).
|
||||
|
||||
## Plugin manifest
|
||||
|
||||
@@ -34,9 +37,14 @@ first call.
|
||||
|
||||
Only one full bar option is active at a time. The built-in `omarchy.bar` is
|
||||
used when `bar.id` is omitted or when a selected third-party bar cannot load.
|
||||
Panels, overlays, and menus are loaded when summoned. Plugins can set
|
||||
`keepLoaded: true` to survive between summons. First-party services are
|
||||
loaded at startup.
|
||||
Panels, overlays, and menus are loaded when summoned. Plugins can set the
|
||||
top-level manifest key `keepLoaded: true` to survive between summons.
|
||||
First-party services are loaded at startup.
|
||||
|
||||
Entry points are QML `Item`s. Panel, overlay, and menu entry points expose
|
||||
`open(payloadJson)` and `close()` for summon/hide; on load the host injects
|
||||
`omarchyPath`, `shell`, `manifest`, and the registries (`pluginRegistry` /
|
||||
`barWidgetRegistry`) as properties.
|
||||
|
||||
Full schema: [`shell/services/PluginRegistry.qml`](../shell/services/PluginRegistry.qml).
|
||||
|
||||
@@ -76,20 +84,25 @@ widgets that omit it default to `center`.
|
||||
Plugins run as **unsandboxed code** inside `omarchy-shell`. Adding warns you
|
||||
before cloning, plugins land disabled so you can review the code before
|
||||
`omarchy plugin enable`, and updates show a diff before touching anything.
|
||||
Commands prompt when run bare in a terminal and run unattended when given
|
||||
arguments — add `--yes` to skip every prompt (the path for scripts and agents).
|
||||
Commands confirm in a terminal even when given arguments; without one they
|
||||
refuse rather than guess. Add `--yes` to skip every prompt (the path for
|
||||
scripts and agents).
|
||||
|
||||
You can still install by hand: drop a plugin into
|
||||
`~/.config/omarchy/plugins/<id>/`, run `omarchy-shell shell rescanPlugins`, then
|
||||
`omarchy plugin enable <id>`. A bar widget starts in its declared default
|
||||
section and can be moved with `omarchy bar move`; enabling a full bar
|
||||
replaces the one in use.
|
||||
section; enabling a full bar replaces the one in use. `omarchy bar` drives the
|
||||
bar from the CLI — `use | reset | defaults | position | transparent | put |
|
||||
move | set`, with placement flags such as `--section` and `--index`.
|
||||
The lower-level IPC methods remain available through `omarchy-shell shell ...`.
|
||||
|
||||
## IPC
|
||||
|
||||
The shell exposes a `shell` target plus extra targets registered by
|
||||
individual plugins (`bar`, `image-selector`, …).
|
||||
The shell exposes a `shell` target (the host also registers
|
||||
`image-selector`) plus targets registered by individual plugins, named
|
||||
for the plugin rather than for where it appears: `background`, `osd`,
|
||||
`media`, `notifications`, and per-widget targets such as `omarchy.clock`
|
||||
or `omarchy.power`. There is no `bar` target.
|
||||
|
||||
| Method | Effect |
|
||||
|---------------------------------------|---------------------------------|
|
||||
@@ -97,17 +110,24 @@ individual plugins (`bar`, `image-selector`, …).
|
||||
| `summon <id> <payloadJson>` | load + open a plugin |
|
||||
| `hide <id>` | close a previously-summoned |
|
||||
| `toggle <id> <payloadJson>` | summon if closed, hide if open |
|
||||
| `togglePanelAt <section> <index>` | toggle the panel at a bar position |
|
||||
| `call <id> <method> <arg>` | call an already-loaded plugin |
|
||||
| `rescanPlugins` | re-walk plugin dirs and hot-reload plugin code |
|
||||
| `reloadConfig` | reload shell.json |
|
||||
| `applyTheme <colorsB64> <shellB64>` | push theme colors + shell.toml |
|
||||
| `toggleBarTransparency` | flip the bar background between solid and transparent |
|
||||
| `setPluginEnabled <id> <"true"\|…>` | flip enabled bit (`ok` / `unknown`) |
|
||||
| `enablePlugin <id> <placementJson>` | enable and place in one mutation |
|
||||
| `putBarWidget <id> <placementJson>` | place a widget only where absent (`omarchy bar put`) |
|
||||
| `moveBarWidget <id> <placementJson>` | move a configured widget |
|
||||
| `setBarWidget <id> <key> <valueJson> <selectorJson>` | set an inline widget option |
|
||||
| `listPlugins` | JSON of every discovered plugin |
|
||||
| `listShellConfig` | effective shell.json as JSON |
|
||||
| `debugBarGeometry` | bar geometry dump for debugging |
|
||||
|
||||
`setPluginEnabled` takes a string; only literal `"true"` enables.
|
||||
`setPluginEnabled` takes a string; only literal `"true"` enables. Methods
|
||||
answer on stdout with exit 0 — `ok` on success, `unknown` or an error
|
||||
string on a miss.
|
||||
|
||||
## shell.json
|
||||
|
||||
@@ -122,8 +142,7 @@ individual plugins (`bar`, `image-selector`, …).
|
||||
"id": "omarchy.bar",
|
||||
"position": "top",
|
||||
"transparent": false,
|
||||
"centerAnchor": "calendar",
|
||||
"fontFamily": "JetBrainsMono Nerd Font",
|
||||
"centerAnchor": "omarchy.clock",
|
||||
"layout": {
|
||||
"left": [ { "id": "omarchy.menu" } ],
|
||||
"center": [ { "id": "omarchy.clock", "format": "HH:mm" } ],
|
||||
@@ -146,10 +165,9 @@ Rules:
|
||||
3. Settings are inline on the entry. No `config:` sub-object, no
|
||||
merge layers.
|
||||
4. Built-in bar widget ids are namespaced (`omarchy.clock`, `omarchy.audio`, …).
|
||||
The migration rewrites older ids such as `Clock` and `AudioPanel` forward.
|
||||
5. Third-party enabled ⇔ present; for full bar options that means `bar.id`.
|
||||
First-party non-bar plugins are enabled unless listed in `disabledPlugins[]`.
|
||||
6. `allowMultiple: true` in the manifest permits multiple instances.
|
||||
6. `barWidget.allowMultiple: true` in the manifest permits multiple instances.
|
||||
7. `idle.screensaver` and `idle.lock` are seconds since user idle began.
|
||||
8. `version: 1` is required.
|
||||
|
||||
@@ -157,6 +175,12 @@ Rules:
|
||||
user `shell.json` exists, defaults are used verbatim. Once the user
|
||||
customizes, `shell.json` is canonical — there is no deep-merge.
|
||||
|
||||
`shell.json` is shell configuration; theme tokens live in `shell.toml`
|
||||
(next section). Both are current — they answer different questions. A
|
||||
machine-level `~/.config/omarchy/shell.toml` is watched live by the
|
||||
shell and its keys win over the active theme's `shell.toml`, so
|
||||
overrides like `omarchy display text size` survive theme switches.
|
||||
|
||||
## Theme tokens
|
||||
|
||||
See [`theming.md`](theming.md) for the full theme/template workflow,
|
||||
@@ -165,20 +189,23 @@ including generated `*.tpl` files, gradient helpers, and shell border syntax.
|
||||
Themes ship colors in `themes/<name>/colors.toml` and surface roles +
|
||||
sizing in `themes/<name>/shell.toml`. Defaults are generated from
|
||||
`default/themed/shell.toml.tpl`; a theme may also drop a hand-written
|
||||
`shell.toml` next to its `colors.toml` to replace the generated file.
|
||||
`shell.toml` next to its `colors.toml` to replace the generated file,
|
||||
or override a single section with `shell.<section>.toml`, merged in by
|
||||
`omarchy-theme-set-templates` (see [`theming.md`](theming.md)).
|
||||
|
||||
`colors.toml` uses `foreground` and `background` for the foundational
|
||||
text/background palette, exposed to QML as `Color.foreground` and
|
||||
`Color.background`.
|
||||
|
||||
The shell exposes these tokens to QML via two singletons in
|
||||
The shell exposes these tokens to QML via three singletons in
|
||||
`qs.Commons`:
|
||||
|
||||
- `Color` — palette (`foreground`, `background`, `accent`, `urgent`)
|
||||
and per-surface roles (`Color.bar.*`, `Color.popups.*`,
|
||||
`Color.tooltip.*`, `Color.notifications.*`, `Color.menu.*`,
|
||||
`Color.launcher.*`, `Color.imagePicker.*`, `Color.polkit.*`,
|
||||
`Color.lock.*`). Clipboard and emojis share `Color.menu.*`.
|
||||
`Color.polkit.*`, `Color.lock.*`, `Color.imagePicker.*`). Clipboard
|
||||
and emojis share `Color.menu.*`; the `[launcher]` section is consumed
|
||||
by the launcher outside shell QML.
|
||||
- `Style` — structural tokens (`cornerRadius`), shared interactive
|
||||
state tokens/helpers, spacing (`Style.spacing.*` / `Style.space(px)`),
|
||||
the type scale (`Style.font.*`), and bar dimensions
|
||||
@@ -371,5 +398,6 @@ Then `~/.config/omarchy/bar/modules/gpu.qml` (or set `source` to point
|
||||
elsewhere). The module is an `Item` and receives `bar`, `moduleName`,
|
||||
`settings` properties. `bar` exposes `foreground` / `background` /
|
||||
`urgent` / `fontFamily` / `position` / `vertical` / `barSize`, plus
|
||||
`run(cmd)`, `shellQuote(v)`, `showTooltip(t, s)` / `hideTooltip(t)`,
|
||||
`requestPopout(o)` / `releasePopout(o)`.
|
||||
`run(cmd)`, `showTooltip(t, s)` / `hideTooltip(t)`,
|
||||
`requestPopout(o)` / `releasePopout(o)`. To shell-quote arguments for
|
||||
`run`, use `Util.shellQuote(v)` from `qs.Commons`.
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
# Testing
|
||||
|
||||
How the non-graphical test suites are organized: what each runner owns, the
|
||||
protocol test files speak, and the conventions that keep them runnable on any
|
||||
machine — including headless CI sandboxes with no compositor. The graphical
|
||||
acceptance suite is a separate thing that drives a live session in a disposable
|
||||
VM; see [`agents/skills/acceptance-tests.md`](../agents/skills/acceptance-tests.md).
|
||||
|
||||
## Suite map
|
||||
|
||||
`./test/all` runs both suites below and keeps going when one fails, so a single
|
||||
failure cannot hide the other suite behind it. It reports the failed suites at
|
||||
the end and exits non-zero.
|
||||
|
||||
- **`./test/cli`** — one big script, one suite. It owns the CLI router: help
|
||||
and group rendering, route resolution, aliases, hidden commands, and the
|
||||
guarantee that a trailing `--help` never executes the target. It also owns
|
||||
the metadata lint — every `omarchy-*` executable under `bin/` is checked for a
|
||||
`# omarchy:summary=` header and against removed or redundant fields — plus
|
||||
the theme pipeline: template rendering (`omarchy-theme-set-templates`,
|
||||
`omarchy-theme-color`, `omarchy-theme-osc`), the theme sync commands
|
||||
(tmux, GNOME, VS Code, Pi, Claude) run against stub binaries and a fake
|
||||
`$HOME`, and the theme-state migrations.
|
||||
- **`./test/shell`** — runs every `test/shell.d/*-test.sh` (except
|
||||
`base-test.sh` itself). Each file is an independent suite covering one area:
|
||||
a shell plugin, a `bin/` command, a config invariant, a migration. This is
|
||||
where new tests go.
|
||||
- **Acceptance** — everything that needs a real desktop doing real things.
|
||||
Deliberately excluded from `./test/all`; it runs in a VM, not the
|
||||
development session.
|
||||
|
||||
A new shell test only needs the right name: drop `<area>-test.sh` into
|
||||
`test/shell.d/` and `./test/shell` picks it up automatically. Shared fixtures
|
||||
live under `test/shell.d/fixtures/`.
|
||||
|
||||
## The base-test.sh contract
|
||||
|
||||
Every shell test starts the same way:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
```
|
||||
|
||||
`base-test.sh` refuses to be executed directly — it is a library. It discovers
|
||||
the repo root from its own location and exports it as `ROOT`, so tests
|
||||
reference files as `$ROOT/bin/...` and never depend on the caller's working
|
||||
directory or an installed Omarchy.
|
||||
|
||||
Assertions are TAP-flavored and blunt:
|
||||
|
||||
- `pass "description"` prints `ok - description`.
|
||||
- `fail "description" [detail]` prints the optional detail and
|
||||
`not ok - description` to stderr, then **exits the file**. There is no
|
||||
counting or continuing within a file: the first failed assertion ends it,
|
||||
which keeps later assertions from reporting against state the failure
|
||||
already invalidated.
|
||||
- `require_command <cmd>` fails the file when a needed tool is absent.
|
||||
|
||||
The runner compensates for that early exit: `./test/shell` continues past a
|
||||
failing file and summarizes the failures at the end. Aborting the whole run at
|
||||
the first bad file once let a single packaging failure mask 114 of 134 files.
|
||||
Failure granularity is therefore per file inside a run, per assertion inside a
|
||||
file.
|
||||
|
||||
## Compositor-dependent tests
|
||||
|
||||
Some tests launch Quickshell or query Hyprland, but the suite must stay green
|
||||
on headless machines. `require_compositor "description"` handles this: when no
|
||||
compositor answers it prints `ok - no Wayland compositor; skipping ...` and
|
||||
exits 0 — a skip is a passing test — and otherwise returns so the file
|
||||
proceeds.
|
||||
|
||||
The probe is more than an environment check, because `WAYLAND_DISPLAY` only
|
||||
proves the variable was inherited. Sandboxes pass the environment through
|
||||
while blocking `$XDG_RUNTIME_DIR`, so Quickshell clears a bare variable check
|
||||
and then aborts inside QGuiApplication — a core dump per launch where a skip
|
||||
belonged. So `compositor_reachable` checks the socket actually exists, then
|
||||
asks Hyprland itself (`hyprctl -j monitors`, retried, and only when
|
||||
`HYPRLAND_INSTANCE_SIGNATURE` makes it askable), since a compositor that died
|
||||
mid-session leaves its socket behind. When the compositor is reachable,
|
||||
`require_compositor` also sets `ulimit -c 0`: Quickshell leaves through
|
||||
`qFatal()` if its connection drops mid-run, and the test should fail without
|
||||
writing a core dump as debris.
|
||||
|
||||
Gate only what needs gating — put `require_compositor` in files whose runtime
|
||||
half needs a live session, and keep static analysis of the same area in code
|
||||
that runs unconditionally before or beside it.
|
||||
|
||||
## Unit-testing shell JavaScript from bash
|
||||
|
||||
The Quickshell plugins keep their logic in plain `.js` modules
|
||||
(`shell/plugins/menu/MenuModel.js`, `bar/BarModel.js`, ...) that end in a
|
||||
guarded `if (typeof module !== "undefined") module.exports = {...}` block. QML
|
||||
imports them directly and ignores the guard; Node loads them as CommonJS. That
|
||||
dual citizenship is what makes the shell's model logic unit-testable without a
|
||||
compositor.
|
||||
|
||||
`run_node_test` is the bridge: it prepends a JS prelude to a heredoc and pipes
|
||||
the result into `node`. The prelude mirrors the bash assertion protocol
|
||||
(`pass`, `fail`, `assert`, `assertEqual`, `assertDeepEqual` — same
|
||||
`ok`/`not ok` lines, same exit-on-first-failure) and provides `root` (from the
|
||||
exported `ROOT`), `path`, and `requireFromRoot(relativePath)`:
|
||||
|
||||
```bash
|
||||
run_node_test <<'JS'
|
||||
const menu = requireFromRoot('shell/plugins/menu/MenuModel.js')
|
||||
|
||||
const parsed = menu.parseMenuJsonc('{ "items": { "root": { "label": "Go" }, }, }')
|
||||
assertEqual(parsed.length, 1, 'menu parses JSONC with trailing commas')
|
||||
JS
|
||||
```
|
||||
|
||||
Roughly a quarter of the shell test files use this to test parsing, merging,
|
||||
and layout logic as pure functions, reserving compositor-gated tests for what
|
||||
only a live session can prove.
|
||||
|
||||
## Conventions worth copying
|
||||
|
||||
- **Stub the world, run the real code.** Tests build a scratch `bin/` of stub
|
||||
executables (`sudo`, `tmux`, `gsettings`, helper commands) that log their
|
||||
arguments to a file, prepend it to `PATH`, and then run the real script
|
||||
under test. Assertions grep the call log and the files the script wrote.
|
||||
- **Fake `$HOME`, real `$OMARCHY_PATH`.** Anything touching user state runs
|
||||
with `HOME` pointed at a `mktemp -d` directory (cleaned up via
|
||||
`trap ... EXIT`) and `OMARCHY_PATH="$ROOT"`, so tests exercise the checkout
|
||||
without touching the developer's machine.
|
||||
- **Migrations run directly.** A migration test builds the legacy state in a
|
||||
fake `$HOME`, runs `bash -euo pipefail "$ROOT/migrations/<ts>.sh"`, and
|
||||
asserts the resulting state — including running it twice to prove
|
||||
idempotence, and once against non-legacy state to prove it leaves user
|
||||
customization alone.
|
||||
- **Assert the invariant, not the snapshot.** Config tests pin the property a
|
||||
test is named for (this widget stays adjacent to that one) rather than whole
|
||||
structures, so unrelated churn does not fail them.
|
||||
+22
-5
@@ -6,6 +6,13 @@ Omarchy themes live under `themes/<name>/` in the source tree (installed at
|
||||
`colors.toml`; Omarchy generates the active theme files from
|
||||
`default/themed/*.tpl` when `omarchy-theme-set <name>` runs.
|
||||
|
||||
Beyond `colors.toml` and hand-written config overrides, a theme can ship
|
||||
`backgrounds/` (users overlay their own via
|
||||
`~/.config/omarchy/backgrounds/<name>/`; the active image is the
|
||||
`~/.local/state/omarchy/current/background` symlink), `preview.png` and
|
||||
`preview-unlock.png` for the theme switcher, `icons.theme`, `keyboard.rgb`,
|
||||
`unlock.png`, and a `light.mode` marker file.
|
||||
|
||||
## Theme activation flow
|
||||
|
||||
`omarchy-theme-set <name>` builds a clean staging directory at
|
||||
@@ -28,6 +35,14 @@ User templates in `~/.config/omarchy/themed/*.tpl` are processed before the
|
||||
built-in templates. If a user template has the same output filename as a
|
||||
built-in template, the built-in output is skipped.
|
||||
|
||||
After activation, `omarchy-theme-set` fires the `theme-set` hook
|
||||
(`~/.config/omarchy/hooks/theme-set*`, theme name in `$1`) and dispatches a
|
||||
parallel retint of running apps — terminals, Hyprland, btop, browser, editors,
|
||||
and the rest of the `post_theme_commands` list in `bin/omarchy-theme-set`.
|
||||
Making a new app follow theme changes means adding its restart/retint command
|
||||
to that list. Runs serialize on a `flock`, so scripted theme changes queue
|
||||
instead of racing.
|
||||
|
||||
## `colors.toml`
|
||||
|
||||
`colors.toml` provides the palette keys used by templates. Keys are grouped
|
||||
@@ -64,7 +79,8 @@ shell palette is loaded from:
|
||||
`color4`
|
||||
- `muted` — de-emphasized elements (comments, placeholders, dividers); also
|
||||
serves as ANSI `color8`
|
||||
- `urgent` / `red` / `color1`
|
||||
- `red` / `color1` — populate the shell's urgent role; there is no `urgent`
|
||||
palette key (one defined in `colors.toml` is ignored)
|
||||
|
||||
Themes and user templates using the legacy short names remain supported.
|
||||
Canonical names take precedence when both forms are defined, and resolved
|
||||
@@ -304,10 +320,11 @@ BorderSurface {
|
||||
}
|
||||
```
|
||||
|
||||
Use `Border.surfaceSpec(section, token, fallbackColor, fallbackWidth)` for
|
||||
shell theme tokens, `Border.controlSpec(state, foreground, accent)` for shared
|
||||
controls, and `Border.flat(color, width)` for a deliberate local border that
|
||||
should not be overridden by the active theme. `Color.<section>.border` is the
|
||||
Use `Border.surfaceSpec(section, token, fallbackColor, fallbackWidth, alphaKey)`
|
||||
for shell theme tokens (the optional `alphaKey` names the alpha token, e.g.
|
||||
`"border-alpha"`), `Border.controlSpec(state, foreground, accent, urgent)` for
|
||||
shared controls, and `Border.flat(color, width)` for a deliberate local border
|
||||
that should not be overridden by the active theme. `Color.<section>.border` is the
|
||||
flat first-stop color for consumers that cannot render full border specs.
|
||||
|
||||
## Hyprland templates
|
||||
|
||||
+30
-5
@@ -82,7 +82,7 @@ Omarchy update command, the hook exits non-zero with `AbortOnFail`, which stops
|
||||
the transaction before packages are changed.
|
||||
|
||||
`omarchy-update-system-pkgs`, `omarchy-refresh-pacman`, `omarchy-reinstall-pkgs`,
|
||||
and the v4 upgrader run pacman through:
|
||||
`omarchy-channel-set`, and the v4 upgrader run pacman through:
|
||||
|
||||
```bash
|
||||
env OMARCHY_UPDATE_PACMAN=1 pacman ...
|
||||
@@ -116,9 +116,14 @@ omarchy-update
|
||||
├─ omarchy-update-lock
|
||||
│ └─ acquire the update lock and run omarchy-update inside it
|
||||
├─ omarchy-update-requires-free-space
|
||||
│ └─ check free space on / and warn below the configured threshold
|
||||
│ └─ abort below the configured free-space threshold on /
|
||||
├─ confirm unless -y
|
||||
├─ create snapper snapshot, if snapper is installed
|
||||
├─ omarchy-update-pkg-prune
|
||||
│ └─ trim the pacman cache to two versions per package, deliberately
|
||||
│ before the snapshot since the cache lives on the snapshotted subvolume
|
||||
├─ create snapper snapshot (skipped silently without snapper; snapper
|
||||
│ installed but unconfigured fails the snapshot loudly, pointing at
|
||||
│ install/config/snapper.sh, and the update continues without one)
|
||||
├─ omarchy-update-stay-awake start
|
||||
├─ run package updates, migrations, hooks, and log analysis
|
||||
├─ omarchy-update-status
|
||||
@@ -132,6 +137,9 @@ Important behavior:
|
||||
|
||||
- In dev-link mode, `omarchy update` fast-forwards the active checkout from its
|
||||
configured upstream before changing system packages or running migrations.
|
||||
- `-y` exports `OMARCHY_UPDATE_UNATTENDED=1` — a promise not to ask anything.
|
||||
Steps that would prompt (orphan removal, conflict handoff) report and skip
|
||||
instead of blocking.
|
||||
- The free-space requirement uses a 10 GiB threshold and stops the update before
|
||||
confirmation when it is not met. If free space cannot be determined, the
|
||||
check is silently skipped. Set `OMARCHY_UPDATE_FORCE=1` to bypass the check.
|
||||
@@ -235,6 +243,20 @@ Exit codes:
|
||||
The widget runs this check on shell startup and every six hours. Clicking the
|
||||
update icon launches `omarchy-update` in a floating terminal.
|
||||
|
||||
## Channels and versions
|
||||
|
||||
Updates install whatever the active channel points at. `omarchy-channel-set
|
||||
<stable|rc|edge|dev>` switches channels: the three package channels select
|
||||
which pacman repo the mirrorlist points at (and swap between the `omarchy` and
|
||||
`omarchy-dev` packages through a guard-allowed pacman run), while `dev` links
|
||||
the runtime to a git checkout via the dev-link mechanism, after which
|
||||
`omarchy update` fast-forwards that checkout instead of upgrading a package.
|
||||
|
||||
There is no version file at runtime. `omarchy-version` derives the version from
|
||||
`pacman -Q` on whichever package is installed, or reports `dev (<hash>)` for a
|
||||
linked checkout, and `omarchy-version-channel` sniffs the mirrorlist and
|
||||
pacman.conf to answer which channel is active.
|
||||
|
||||
## Update-related binaries
|
||||
|
||||
This inventory is intentionally opinionated. Some commands are useful as stable
|
||||
@@ -250,14 +272,17 @@ scripts.
|
||||
| `omarchy-update-confirm` | Gum confirmation copy for `omarchy update`. | **Question.** Could be inlined into `omarchy-update`; separate file only helps keep copy isolated. |
|
||||
| `omarchy-update-dev` | Fast-forwards the active dev-linked checkout from its configured upstream; no-ops for package-backed installs. | **Keep.** Runs before package updates so a checkout conflict stops the update before system mutation. |
|
||||
| `omarchy-update-keyring` | Ensures Omarchy keyring and Arch keyring are current before the main transaction. | **Keep, but review.** It uses targeted `pacman -Sy` for keyring bootstrapping; acceptable for this special case but should remain tightly scoped. |
|
||||
| `omarchy-update-system-pkgs` | Runs `sudo env OMARCHY_UPDATE_PACMAN=1 pacman -Syu --noconfirm` with targeted transition `--overwrite` entries so the ALPM guard allows the transaction and early package-layout conflicts are handled. | **Keep for now.** Small leaf command, clear/testable. |
|
||||
| `omarchy-update-system-pkgs` | Runs `sudo env OMARCHY_UPDATE_PACMAN=1 pacman -Syu --noconfirm` with `--overwrite '/usr/share/omarchy/*'`, capturing stderr to a report file; on failure it execs `omarchy-update-system-pkgs-when-conflicted`. | **Keep for now.** Small leaf command, clear/testable. |
|
||||
| `omarchy-update-system-pkgs-when-conflicted` | Hidden conflict handler: quarantines unowned conflicting files under `/var/lib/omarchy/replaced`, retries the upgrade once, restores files the upgrade didn't claim, and hands package-vs-package conflicts to an interactive pacman run (never under `-y`). | **Keep internal/hidden.** Keeps conflict recovery out of the happy path. |
|
||||
| `omarchy-update-pkg-prune` | Trims the pacman cache to two versions per package (`paccache -rk2`) before the snapshot, keeping the offline downgrade path while capping snapshot growth. | **Keep internal/hidden.** |
|
||||
| `omarchy-update-requires-free-space` | Aborts the update below a 10 GiB free-space threshold on `/`; silently skipped when free space cannot be determined; `OMARCHY_UPDATE_FORCE=1` bypasses. | **Keep internal/hidden.** |
|
||||
| `omarchy-migrate` | Public migration command. Waits for pacman, then runs all pending migrations for the current user. Supports `--pending`. | **Keep.** This replaces the discarded `omarchy-update-user-finalize` name and no longer needs `--force`. |
|
||||
| `omarchy-update-pacman-guard` | ALPM pre-transaction guard that aborts direct `pacman -Syu` style upgrades unless Omarchy set `OMARCHY_UPDATE_PACMAN=1` or the user explicitly set `OMARCHY_ALLOW_DIRECT_PACMAN=1`. | **Keep internal/hidden.** This is what nudges users back to `omarchy update`. |
|
||||
| `omarchy-migrate-notify` | Internal login-time notification helper. Uses `omarchy-migrate --pending` and shows a notification only when this user has pending migrations. | **Keep internal/hidden.** Clear name now that the public command is `omarchy-migrate`. |
|
||||
| `omarchy-update-user-notify` | Hidden compatibility wrapper for `omarchy-migrate-notify`. | **Temporary.** Keep only for old callers. |
|
||||
| `omarchy-update-available` | Update checker for shell widget and post-update refresh. | **Keep.** Could eventually be renamed `omarchy-update-check`, but current name matches widget semantics. |
|
||||
| `omarchy-update-aur-pkgs` | Updates AUR packages with `yay -Sua` if foreign packages exist and AUR is reachable. | **Question.** Omarchy is package-backed now, but users may still install AUR packages. Keep for now. |
|
||||
| `omarchy-update-mise` | Runs `mise up` for mise-managed tools. | **Keep.** Mise-managed tools are intentionally part of the blessed update path. |
|
||||
| `omarchy-update-mise` | Runs `MISE_MINIMUM_RELEASE_AGE=0 mise up` for mise-managed tools — the override of mise's release-age cooldown is the point. | **Keep.** Mise-managed tools are intentionally part of the blessed update path. |
|
||||
| `omarchy-update-orphan-pkgs` | Lists orphans and prompts before removal; noninteractive mode never removes. | **Keep for now.** Safe because it is prompt-only. |
|
||||
| `omarchy-update-analyze-logs` | Scans `/tmp/omarchy-update.log` for known failure patterns, currently initramfs generation. | **Keep/expand.** Useful safety net; should grow only for high-signal checks. |
|
||||
| `omarchy-update-restart` | Prompts for reboot after kernel/Hyprland updates, restarts components with `restart-*-required` markers, and always restarts the shell. | **Keep.** Important final step; may eventually include service-restart checks. |
|
||||
|
||||
Reference in New Issue
Block a user