./install only ever adds. It copies over /, links dotfiles, installs packages -- and has no way to say "remove that file", "disable that unit", "that symlink points nowhere now". So a machine set up months ago keeps whatever this repository has since decided was wrong, and the only thing that ever fixes it is somebody reading a commit message. With a curl installer in the README, that stopped being hypothetical. A migration is one script that performs one repair, exactly once, on the machines that need it. Named by the commit timestamp that authored it, so glob order is chronological without a sequence number two branches could both pick. Marked in ~/.local/state on success and only on success, so a repair that failed stays pending rather than being recorded as done and hidden forever. Ordered, and stopped at the first failure, because a later repair may assume an earlier one landed. A fresh install marks everything without running it, the way Migrations.qml stamps a pre-versioning settings file at its baseline. The first real one removes the dangling ~/.config/forge symlink left behind when the GNOME session was cut: link-dotfiles could link it but never unlink it. Verified both ways -- a no-op on a machine that never had it, an actual repair on one that did. Root work goes through panama-sudo --reason so the password prompt names the repair, and the contract fails any migration reaching for bare sudo.
91 lines
5.6 KiB
Lua
91 lines
5.6 KiB
Lua
-- ─────────────────────────────────────────────────────────────────────────────
|
|
-- Autostart
|
|
--
|
|
-- `exec-once` no longer exists in 0.56; startup is an event handler.
|
|
--
|
|
-- Session: Fedora installs two sessions, "Hyprland" and "Hyprland
|
|
-- (uwsm-managed)". Panama targets the uwsm one, because
|
|
-- xdg-desktop-portal-hyprland's systemd unit requires graphical-session.target
|
|
-- and Fedora ships no hyprland-session.target -- without uwsm, screen sharing
|
|
-- in OBS/Sunshine/Zoom silently fails. The target is started explicitly below
|
|
-- so the plain session works too.
|
|
--
|
|
-- Anything that ships a systemd user unit is started as a unit rather than as
|
|
-- a compositor child. That is not cosmetic: under uwsm, units get the correct
|
|
-- activation environment (see uwsm/env), they restart on failure, and they
|
|
-- shut down in order. Panama's `change-settings` script enables them once;
|
|
-- the explicit starts here make a fresh clone work before that has run.
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
hl.on("hyprland.start", function()
|
|
-- Publish the session environment to systemd and D-Bus so user units and
|
|
-- the portals can see WAYLAND_DISPLAY. Cheap, and essential without uwsm.
|
|
hl.exec_cmd("dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=Hyprland")
|
|
hl.exec_cmd("systemctl --user start hyprland-session.target")
|
|
|
|
-- Units: polkit prompts, wallpaper, launcher daemon, idle/lock. All four
|
|
-- carry `ConditionEnvironment=WAYLAND_DISPLAY`, and hl.exec_cmd fires
|
|
-- commands without waiting for them to finish, so the dbus-update call
|
|
-- above racing this one is not safe to assume complete -- a lost race
|
|
-- leaves the Condition unmet and the unit silently never starts (exit 0,
|
|
-- no error). hypridle is the only listener for the logind Lock signal,
|
|
-- so that failure mode is "lock-session goes to nobody". Re-import
|
|
-- synchronously in the same shell invocation first so the Condition
|
|
-- always sees it, regardless of how the dbus-update call above scheduled.
|
|
-- panama-polkit-agent replaces hyprpolkitagent, whose prompt is compiled
|
|
-- into its binary and cannot be themed. Only one agent may register per
|
|
-- session, so they must not both start. hyprpolkitagent stays INSTALLED as
|
|
-- the fallback: `systemctl --user start hyprpolkitagent` restores the stock
|
|
-- prompt if Panama's ever fails to come up.
|
|
hl.exec_cmd("systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP && systemctl --user start panama-polkit-agent.service hyprpaper.service vicinae.service hypridle.service")
|
|
|
|
-- Pending repairs for this machine, if any. Oneshot: it checks, tells the
|
|
-- user when there is something to tell, and exits. Started here rather
|
|
-- than enabled so it belongs to the Hyprland session; see the unit.
|
|
hl.exec_cmd("systemctl --user start panama-migrate-notify.service")
|
|
|
|
-- Text expansion. change-settings runs `espanso service register`, which
|
|
-- writes and enables espanso's own user unit; the explicit start makes the
|
|
-- first Hyprland login after a fresh install work rather than the second.
|
|
-- Started on its own line: unlike the units above it carries no
|
|
-- ConditionEnvironment, and a missing unit (espanso not yet registered)
|
|
-- must not muddy the start of the four that lock and wallpaper depend on.
|
|
hl.exec_cmd("systemctl --user start espanso.service")
|
|
|
|
-- The shell: bar, dock, overview, quick settings, notifications, capture.
|
|
-- No systemd unit ships with quickshell, so it runs as a compositor child.
|
|
hl.exec_cmd("quickshell --daemonize")
|
|
|
|
-- Removable-media automounting. GNOME did this invisibly via gvfs+udisks;
|
|
-- outside GNOME something has to ask udisks to mount. No tray icon: the
|
|
-- Quickshell bar already has a tray, and udiskie's own icon would be
|
|
-- redundant. -f opens Nautilus when you click the mount notification.
|
|
hl.exec_cmd("udiskie --automount --notify --no-tray --file-manager nautilus")
|
|
|
|
-- Keyring unlock, for Nextcloud and Bitwarden credential storage.
|
|
hl.exec_cmd("/usr/bin/gnome-keyring-daemon --start --components=secrets,ssh,pkcs11")
|
|
|
|
-- Nextcloud and Bitwarden are NOT started here. Both ship a
|
|
-- ~/.config/autostart/*.desktop entry, and systemd's own
|
|
-- systemd-xdg-autostart-generator turns every such entry into a
|
|
-- `PartOf=graphical-session.target` unit (`[email protected]`,
|
|
-- `[email protected]`) that fires once the uwsm
|
|
-- session brings up graphical-session.target -- confirmed live via
|
|
-- `systemctl --user list-units 'app-*@autostart.service'`. An explicit
|
|
-- second launch here used to duplicate that: for Bitwarden specifically,
|
|
-- each `flatpak run` gets its own sandbox instance, so the two starts
|
|
-- didn't just race, they left two competing processes fighting over the
|
|
-- app's single-instance lock, with neither reliably owning a usable
|
|
-- window. Trust the generator instead of re-launching.
|
|
--
|
|
-- RustDesk is deliberately absent too: it ships an enabled *system*
|
|
-- service (`rustdesk --service`) that spawns --server and --tray for the
|
|
-- session on its own. Starting it here as well would give you two trays.
|
|
end)
|
|
|
|
hl.on("hyprland.shutdown", function()
|
|
hl.exec_cmd("systemctl --user stop hyprland-session.target")
|
|
end)
|
|
|
|
return true
|