diff --git a/config/dot/hypr/autostart.lua b/config/dot/hypr/autostart.lua index 27c99c9..af3fc6e 100644 --- a/config/dot/hypr/autostart.lua +++ b/config/dot/hypr/autostart.lua @@ -23,8 +23,16 @@ hl.on("hyprland.start", function() 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. - hl.exec_cmd("systemctl --user start hyprpolkitagent.service hyprpaper.service vicinae.service hypridle.service") + -- 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. + hl.exec_cmd("systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP && systemctl --user start hyprpolkitagent.service hyprpaper.service vicinae.service hypridle.service") -- The shell: bar, dock, overview, quick settings, notifications, capture. -- No systemd unit ships with quickshell, so it runs as a compositor child. diff --git a/config/dot/quickshell/scripts/panama-idle b/config/dot/quickshell/scripts/panama-idle index 1f7bf8a..cc69614 100755 --- a/config/dot/quickshell/scripts/panama-idle +++ b/config/dot/quickshell/scripts/panama-idle @@ -25,6 +25,14 @@ generated="$state_dir/hypridle.conf" dropin_dir="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/hypridle.service.d" dropin="$dropin_dir/panama.conf" +# Set by generate() to the mktemp path it is currently writing, so concurrent +# invocations (e.g. rapid settings changes each spawning `apply`) never share +# a tmp file and interleave writes into a corrupt hypridle.conf. Cleared once +# the atomic mv below lands, so this is a no-op on a normal exit. +generated_tmp="" +cleanup() { rm -f "$generated_tmp" 2>/dev/null || true; } +trap cleanup EXIT + read_setting() { local key="$1" fallback="$2" [[ -r "$settings" ]] || { printf '%s' "$fallback"; return; } @@ -53,6 +61,10 @@ generate() { load mkdir -p "$state_dir" + # Unique per invocation, in the same directory as the destination so the + # final mv is an atomic same-filesystem rename rather than a copy. + generated_tmp="$(mktemp "$generated.XXXXXX")" + { printf '# Generated by panama-idle from %s\n' "$settings" printf '# Do not edit: it is rewritten whenever the idle settings change.\n' @@ -91,9 +103,10 @@ generate() { printf ' on-timeout = systemctl suspend\n' printf '}\n' fi - } >"$generated.tmp" + } >"$generated_tmp" - mv "$generated.tmp" "$generated" + mv "$generated_tmp" "$generated" + generated_tmp="" } install_dropin() { diff --git a/install b/install index 2438c27..220322a 100755 --- a/install +++ b/install @@ -49,10 +49,16 @@ gsettings set org.gnome.desktop.session idle-delay 0 2>/dev/null || true # still run: a missing optional package should not stop the dotfiles being # linked. The summary at the end is what decides whether the install worked, # because a failure scrolled past twenty minutes ago is a failure nobody saw. +# +# Explicit order, not glob order: change-settings runs `vicinae theme set`, +# which needs both vicinae itself (installed by install-packages) and the +# theme files it selects among (symlinked into place by link-dotfiles). New +# scripts must be added here explicitly, or they will not run at all. +STAGES=(install-packages link-dotfiles change-settings link-vicinae-scripts) failed=() -for script in "$PANAMA_PATH"/setup/scripts/*; do +for stage in "${STAGES[@]}"; do + script="$PANAMA_PATH/setup/scripts/$stage" [[ -x "$script" ]] || continue - stage="$(basename "$script")" printf '\n=== %s ===\n' "$stage" if ! "$script"; then failed+=("$stage") diff --git a/setup/packages/desktop-packages b/setup/packages/desktop-packages index 072bdf1..564aa2b 100644 --- a/setup/packages/desktop-packages +++ b/setup/packages/desktop-packages @@ -32,6 +32,7 @@ mokutil mozilla-openh264 nautilus-extensions nautilus-python +nextcloud-client openssl-devel opus-devel python3-dnf-plugin-versionlock diff --git a/setup/packages/flatpak-packages b/setup/packages/flatpak-packages index c207c2d..9e26c1a 100644 --- a/setup/packages/flatpak-packages +++ b/setup/packages/flatpak-packages @@ -2,3 +2,5 @@ org.gtk.Gtk3theme.adw-gtk3-dark io.github.Foldex.AdwSteamGtk com.bitwarden.desktop app.bluebubbles.BlueBubbles +org.mozilla.thunderbird_esr +io.missioncenter.MissionCenter diff --git a/setup/packages/hyprland-packages b/setup/packages/hyprland-packages index bd9f678..7be730b 100644 --- a/setup/packages/hyprland-packages +++ b/setup/packages/hyprland-packages @@ -8,6 +8,7 @@ gpu-screen-recorder grim grimblast gtk-update-icon-cache +helium-browser-bin hypridle hyprland hyprland-guiutils diff --git a/setup/scripts/install-packages b/setup/scripts/install-packages index bb2f3a0..4ed8cd7 100755 --- a/setup/scripts/install-packages +++ b/setup/scripts/install-packages @@ -1,5 +1,7 @@ #!/usr/bin/env bash +set -euo pipefail + # --- Helper functions --- log() { echo -e "\033[1;34m[INFO]\033[0m $*"; } exists() { command -v "$1" >/dev/null 2>&1; } @@ -10,33 +12,37 @@ LOCAL_BIN_PATH="$HOME/.local/bin" echo -e "\n--- Installing Repositories ---" log "Installing RPM Fusion Free and Nonfree Repositories" -sudo dnf install -y https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm > /dev/null 2>&1 +sudo dnf install -y https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm > /dev/null log "Enabling Fedora Cisco OpenH264 Repository" sudo dnf config-manager setopt fedora-cisco-openh264.enabled=1 log "Installing RPM Fusion AppStream Metadata" -sudo dnf update @core -y > /dev/null 2>&1 -sudo dnf install -y rpmfusion-\*-appstream-data > /dev/null 2>&1 +sudo dnf update @core -y > /dev/null +sudo dnf install -y rpmfusion-\*-appstream-data > /dev/null log "Installing Terra Repository" -sudo dnf install -y --nogpgcheck --repofrompath 'terra,https://repos.fyralabs.com/terra$releasever' terra-release > /dev/null 2>&1 +sudo dnf install -y --nogpgcheck --repofrompath 'terra,https://repos.fyralabs.com/terra$releasever' terra-release > /dev/null echo -e "\n--- Installing relevant packages ---" log "Updating all packages. This may take a while" -sudo dnf update -y --refresh > /dev/null 2>&1 +sudo dnf update -y --refresh > /dev/null log "Updating core, multimedia, and sound-and-video groups" +# A trailing `&& sync` here previously meant a failing groupupdate was exempt +# from set -e (bash does not apply -e to the left side of a && list), so the +# failure went unreported. Sync unconditionally on its own line instead. sudo dnf4 groupupdate -y 'core' 'multimedia' 'sound-and-video' \ --setop='install_weak_deps=False' \ --exclude='PackageKit-gstreamer-plugin' \ - --allowerasing && sync > /dev/null 2>&1 + --allowerasing > /dev/null +sync log "Swapping ffmpeg-free for ffmpeg" -sudo dnf swap -y 'ffmpeg-free' 'ffmpeg' --allowerasing > /dev/null 2>&1 +sudo dnf swap -y 'ffmpeg-free' 'ffmpeg' --allowerasing > /dev/null log "Swapping mesa-va-drivers for mesa-va-drivers-freeworld" -sudo dnf swap -y mesa-va-drivers mesa-va-drivers-freeworld > /dev/null 2>&1 +sudo dnf swap -y mesa-va-drivers mesa-va-drivers-freeworld > /dev/null log "Upgrading Multimedia group with optional packages" -sudo dnf4 group upgrade -y --with-optional Multimedia > /dev/null 2>&1 +sudo dnf4 group upgrade -y --with-optional Multimedia > /dev/null log "Installing GStreamer plugins (bad, good, base)" sudo dnf install -y gstreamer1-plugins-{bad-\*,good-\*,base} \ - --exclude=gstreamer1-plugins-bad-free-devel > /dev/null 2>&1 + --exclude=gstreamer1-plugins-bad-free-devel > /dev/null # --- Install all initial packages --- PACKAGES_FILE="$PANAMA_PATH/setup/packages/initial-packages" @@ -45,12 +51,25 @@ if [[ -f "$PACKAGES_FILE" ]]; then log "Installing Initial Packages" echo -e "Includes the following packages:" echo -e "$(<"$PACKAGES_FILE")" - sudo dnf install -y "$INITIAL_PACKAGES" > /dev/null 2>&1 + sudo dnf install -y $INITIAL_PACKAGES > /dev/null log "Initial packages installed!" else log "Package list was not in specified path: $PACKAGES_FILE" fi +# --- Install Desktop Packages --- +DESKTOP_FILE="$PANAMA_PATH/setup/packages/desktop-packages" +if [[ -f "$DESKTOP_FILE" ]]; then + DESKTOP_PACKAGES=$(tr "\n" " " <"$DESKTOP_FILE") + log "Installing Desktop Packages" + echo -e "Includes the following packages:" + echo -e "$(<"$DESKTOP_FILE")" + sudo dnf install -y $DESKTOP_PACKAGES > /dev/null + log "Desktop packages installed!" +else + log "Package list was not in specified path: $DESKTOP_FILE" +fi + # --- Install oh-my-posh if not already installed. --- mkdir -p "$LOCAL_BIN_PATH" if [[ -x "$LOCAL_BIN_PATH/oh-my-posh" ]]; then @@ -67,7 +86,7 @@ if [[ -f "$DEV_FILE" ]]; then log "Installing Development Packages. Mostly for Neovim." echo -e "Includes the following packages:" echo -e "$(<"$DEV_FILE")" - sudo dnf install -y $DEV_PACKAGES > /dev/null 2>&1 + sudo dnf install -y $DEV_PACKAGES > /dev/null log "Development packages installed!" else log "Package list was not in specified path: $DEV_FILE" @@ -78,12 +97,12 @@ fi HYPR_FILE="$PANAMA_PATH/setup/packages/hyprland-packages" if [[ -f "$HYPR_FILE" ]]; then log "Enabling Hyprland COPR" - sudo dnf copr enable -y lionheartp/Hyprland > /dev/null 2>&1 + sudo dnf copr enable -y lionheartp/Hyprland > /dev/null HYPR_PACKAGES=$(tr "\n" " " <"$HYPR_FILE") log "Installing Hyprland desktop packages" echo -e "Includes the following packages:" echo -e "$(<"$HYPR_FILE")" - sudo dnf install -y --setopt=install_weak_deps=False $HYPR_PACKAGES > /dev/null 2>&1 + sudo dnf install -y --setopt=install_weak_deps=False $HYPR_PACKAGES > /dev/null log "Hyprland packages installed!" else log "Package list was not in specified path: $HYPR_FILE" @@ -96,3 +115,18 @@ else log "Installing Bun via curl..." curl -fsSL https://bun.sh/install | bash > /dev/null 2>&1 fi + +# --- Install Flatpak Packages --- +FLATPAK_FILE="$PANAMA_PATH/setup/packages/flatpak-packages" +if [[ -f "$FLATPAK_FILE" ]]; then + FLATPAK_PACKAGES=$(tr "\n" " " <"$FLATPAK_FILE") + log "Adding Flathub remote" + sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo > /dev/null + log "Installing Flatpak Packages" + echo -e "Includes the following packages:" + echo -e "$(<"$FLATPAK_FILE")" + sudo flatpak install -y flathub $FLATPAK_PACKAGES > /dev/null + log "Flatpak packages installed!" +else + log "Package list was not in specified path: $FLATPAK_FILE" +fi diff --git a/setup/scripts/link-dotfiles b/setup/scripts/link-dotfiles index a562f38..9dec120 100755 --- a/setup/scripts/link-dotfiles +++ b/setup/scripts/link-dotfiles @@ -63,6 +63,12 @@ for dir in "${dirs[@]}"; do if [ -d "$CONFIG/$dir" ]; then mv "$CONFIG/$dir" "$PANAMA_OLD/$dir" log "Moved existing $dir config to $PANAMA_OLD/$dir" + # A regular file at this path (not a directory, not a symlink) is missed by + # the guards above, so `ln -s` below would fail with "File exists" -- back + # it up the same way. + elif [ -f "$CONFIG/$dir" ]; then + mv "$CONFIG/$dir" "$PANAMA_OLD/$dir" + log "Moved existing $dir file to $PANAMA_OLD/$dir" fi # Create symlink