From db6bdd8495dd5e3f503c16b73eaac3fe3754c2f5 Mon Sep 17 00:00:00 2001 From: Bernardo Magri Date: Mon, 6 Apr 2026 21:37:24 +0100 Subject: [PATCH] refactor: move state migration to pre-flight, fix XDG hardcoding, and prevent Nix store bloat --- bin/system/nomarchy-preflight-migration | 83 +++++++++++++++++++++++++ modules/home/default.nix | 10 +-- modules/home/state.nix | 77 ----------------------- modules/home/theme-files.nix | 17 ++++- modules/home/theme-switcher.nix | 17 +++-- 5 files changed, 114 insertions(+), 90 deletions(-) create mode 100644 bin/system/nomarchy-preflight-migration diff --git a/bin/system/nomarchy-preflight-migration b/bin/system/nomarchy-preflight-migration new file mode 100644 index 0000000..9a9fca1 --- /dev/null +++ b/bin/system/nomarchy-preflight-migration @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# Nomarchy Pre-flight State Migration +# Migrates legacy state files into the unified state.json before Nix evaluation + +STATE_DIR="$HOME/.config/home-manager" +OLD_TOGGLES_DIR="$HOME/.local/state/nomarchy/toggles" +IDLE_STATE_FILE="$STATE_DIR/idle-state.json" +NIGHTLIGHT_STATE_FILE="$STATE_DIR/hyprsunset-state.json" +HYPRLAND_STATE_FILE="$STATE_DIR/hyprland-state.json" +THEME_STATE_FILE="$STATE_DIR/theme-state.nix" +WALLPAPER_STATE_FILE="$STATE_DIR/wallpaper-state.nix" +FONT_STATE_FILE="$STATE_DIR/font-state.nix" +NEW_STATE_FILE="$STATE_DIR/state.json" + +# We expect jq to be in PATH (it's a dependency of nomarchy-scripts) +JQ="jq" + +mkdir -p "$(dirname "$NEW_STATE_FILE")" +[[ ! -f $NEW_STATE_FILE ]] && echo "{}" > "$NEW_STATE_FILE" + +# 1. Migrate .local/state/nomarchy/toggles +if [[ -d $OLD_TOGGLES_DIR ]]; then + for file in "$OLD_TOGGLES_DIR"/*; do + [[ -e "$file" ]] || continue + filename=$(basename "$file") + case "$filename" in + suspend-off) + $JQ '.suspend = false' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + ;; + screensaver-off) + $JQ '.screensaver = false' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + ;; + skip-vscode-theme-changes) + $JQ '.skipVsCodeTheme = true' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + ;; + esac + rm "$file" + done + rmdir "$OLD_TOGGLES_DIR" 2>/dev/null || true +fi + +# 2. Migrate existing JSON state files +if [[ -f $IDLE_STATE_FILE ]]; then + ENABLED=$($JQ '.enabled' "$IDLE_STATE_FILE") + if [[ "$ENABLED" == "true" || "$ENABLED" == "false" ]]; then + $JQ ".idle = $ENABLED" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + fi + rm "$IDLE_STATE_FILE" +fi + +if [[ -f $NIGHTLIGHT_STATE_FILE ]]; then + ENABLED=$($JQ '.enabled' "$NIGHTLIGHT_STATE_FILE") + TEMP=$($JQ '.temperature' "$NIGHTLIGHT_STATE_FILE") + $JQ ".nightlight = $ENABLED | .nightlightTemperature = $TEMP" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + rm "$NIGHTLIGHT_STATE_FILE" +fi + +if [[ -f $HYPRLAND_STATE_FILE ]]; then + GAPS_OUT=$($JQ '.gaps_out' "$HYPRLAND_STATE_FILE") + GAPS_IN=$($JQ '.gaps_in' "$HYPRLAND_STATE_FILE") + BORDER_SIZE=$($JQ '.border_size' "$HYPRLAND_STATE_FILE") + $JQ ".hyprland = {\"gaps_out\": $GAPS_OUT, \"gaps_in\": $GAPS_IN, \"border_size\": $BORDER_SIZE}" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + rm "$HYPRLAND_STATE_FILE" +fi + +# 3. Migrate plaintext string state files +if [[ -f $THEME_STATE_FILE ]]; then + THEME=$(cat "$THEME_STATE_FILE" | tr -d '\n') + $JQ ".theme = \"$THEME\"" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + rm "$THEME_STATE_FILE" +fi + +if [[ -f $WALLPAPER_STATE_FILE ]]; then + WALLPAPER=$(cat "$WALLPAPER_STATE_FILE" | tr -d '\n') + $JQ ".wallpaper = \"$WALLPAPER\"" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + rm "$WALLPAPER_STATE_FILE" +fi + +if [[ -f $FONT_STATE_FILE ]]; then + FONT=$(cat "$FONT_STATE_FILE" | tr -d '\n') + $JQ ".font = \"$FONT\"" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" + rm "$FONT_STATE_FILE" +fi diff --git a/modules/home/default.nix b/modules/home/default.nix index aac255c..e57f3f8 100644 --- a/modules/home/default.nix +++ b/modules/home/default.nix @@ -32,9 +32,9 @@ in ./swayosd.nix ]; - colorScheme = palettes.${config.nomarchy.theme} or palettes.nord; + colorScheme = lib.mkDefault (palettes.${config.nomarchy.theme} or palettes.nord); - home.packages = with pkgs; [ + home.packages = lib.mkDefault (with pkgs; [ firefox xfce.thunar imv @@ -55,10 +55,10 @@ in nerd-fonts.roboto-mono nerd-fonts.fira-code nerd-fonts.ubuntu-mono - ] ++ userPackages; + ] ++ userPackages); - home.shellAliases = { + home.shellAliases = lib.mkDefault { sys-update = "sudo nixos-rebuild switch --flake /etc/nixos#default --impure"; - env-update = "home-manager switch --flake /etc/nixos#default --impure"; + env-update = "nomarchy-preflight-migration && home-manager switch --flake /etc/nixos#default --impure"; }; } diff --git a/modules/home/state.nix b/modules/home/state.nix index 6805e65..cdf8c51 100644 --- a/modules/home/state.nix +++ b/modules/home/state.nix @@ -22,83 +22,6 @@ let in { config = { - home.activation.migrateNomarchyState = lib.hm.dag.entryAfter ["writeBoundary"] '' - OLD_TOGGLES_DIR="${config.home.homeDirectory}/.local/state/nomarchy/toggles" - IDLE_STATE_FILE="${stateDir}/idle-state.json" - NIGHTLIGHT_STATE_FILE="${stateDir}/hyprsunset-state.json" - HYPRLAND_STATE_FILE="${stateDir}/hyprland-state.json" - THEME_STATE_FILE="${stateDir}/theme-state.nix" - WALLPAPER_STATE_FILE="${stateDir}/wallpaper-state.nix" - FONT_STATE_FILE="${stateDir}/font-state.nix" - NEW_STATE_FILE="${stateDir}/state.json" - JQ="${pkgs.jq}/bin/jq" - - mkdir -p "$(dirname "$NEW_STATE_FILE")" - [[ ! -f $NEW_STATE_FILE ]] && echo "{}" > "$NEW_STATE_FILE" - - # 1. Migrate .local/state/nomarchy/toggles (empty 'off' flag files) - if [[ -d $OLD_TOGGLES_DIR ]]; then - for file in "$OLD_TOGGLES_DIR"/*; do - [[ -e "$file" ]] || continue - filename=$(basename "$file") - case "$filename" in - suspend-off) - $JQ '.suspend = false' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - ;; - screensaver-off) - $JQ '.screensaver = false' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - ;; - skip-vscode-theme-changes) - $JQ '.skipVsCodeTheme = true' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - ;; - esac - rm "$file" - done - rmdir "$OLD_TOGGLES_DIR" 2>/dev/null || true - fi - - # 2. Migrate existing JSON state files - if [[ -f $IDLE_STATE_FILE ]]; then - ENABLED=$($JQ '.enabled' "$IDLE_STATE_FILE") - $JQ ".idle = $ENABLED" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - rm "$IDLE_STATE_FILE" - fi - - if [[ -f $NIGHTLIGHT_STATE_FILE ]]; then - ENABLED=$($JQ '.enabled' "$NIGHTLIGHT_STATE_FILE") - TEMP=$($JQ '.temperature' "$NIGHTLIGHT_STATE_FILE") - $JQ ".nightlight = $ENABLED | .nightlightTemperature = $TEMP" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - rm "$NIGHTLIGHT_STATE_FILE" - fi - - if [[ -f $HYPRLAND_STATE_FILE ]]; then - GAPS_OUT=$($JQ '.gaps_out' "$HYPRLAND_STATE_FILE") - GAPS_IN=$($JQ '.gaps_in' "$HYPRLAND_STATE_FILE") - BORDER_SIZE=$($JQ '.border_size' "$HYPRLAND_STATE_FILE") - $JQ ".hyprland = {\"gaps_out\": $GAPS_OUT, \"gaps_in\": $GAPS_IN, \"border_size\": $BORDER_SIZE}" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - rm "$HYPRLAND_STATE_FILE" - fi - - # 3. Migrate plaintext string state files - if [[ -f $THEME_STATE_FILE ]]; then - THEME=$(cat "$THEME_STATE_FILE" | tr -d '\n') - $JQ ".theme = \"$THEME\"" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - rm "$THEME_STATE_FILE" - fi - - if [[ -f $WALLPAPER_STATE_FILE ]]; then - WALLPAPER=$(cat "$WALLPAPER_STATE_FILE" | tr -d '\n') - $JQ ".wallpaper = \"$WALLPAPER\"" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - rm "$WALLPAPER_STATE_FILE" - fi - - if [[ -f $FONT_STATE_FILE ]]; then - FONT=$(cat "$FONT_STATE_FILE" | tr -d '\n') - $JQ ".font = \"$FONT\"" "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - rm "$FONT_STATE_FILE" - fi - ''; - nomarchy = { toggles = { suspend = togglesState.suspend or true; diff --git a/modules/home/theme-files.nix b/modules/home/theme-files.nix index 92b526e..db32b07 100644 --- a/modules/home/theme-files.nix +++ b/modules/home/theme-files.nix @@ -1,4 +1,4 @@ -{ config, ... }: +{ config, lib, ... }: { xdg.configFile."nomarchy/current/theme" = { @@ -16,7 +16,20 @@ xdg.configFile."nomarchy/branding/icon.txt".source = ../../assets/branding/icon.txt; # Expose all themes to the system via local share for script accessibility - xdg.dataFile."nomarchy/themes".source = ../../assets/themes; + # We filter out images to prevent Nix Store bloat + xdg.dataFile."nomarchy/themes".source = builtins.path { + name = "nomarchy-themes-no-images"; + path = ../../assets/themes; + filter = path: type: + let + baseName = baseNameOf path; + in + ! (type == "regular" && ( + lib.hasSuffix ".jpg" baseName || + lib.hasSuffix ".png" baseName || + lib.hasSuffix ".jpeg" baseName + )); + }; # Nautilus python extensions xdg.dataFile."nautilus-python/extensions/localsend.py".source = ../../assets/nautilus-python/extensions/localsend.py; diff --git a/modules/home/theme-switcher.nix b/modules/home/theme-switcher.nix index 3fb88e7..0ae37db 100644 --- a/modules/home/theme-switcher.nix +++ b/modules/home/theme-switcher.nix @@ -13,7 +13,7 @@ let fi ''; nomarchy-font-selector = pkgs.writeShellScriptBin "nomarchy-font-selector" '' - STATE_DIR="$HOME/.config/home-manager" + STATE_DIR="${config.home.homeDirectory}/.config/home-manager" STATE_FILE="$STATE_DIR/state.json" mkdir -p "$STATE_DIR" @@ -30,18 +30,23 @@ let fi ''; nomarchy-wallpaper-selector = pkgs.writeShellScriptBin "nomarchy-wallpaper-selector" '' - STATE_DIR="$HOME/.config/home-manager" + STATE_DIR="${config.home.homeDirectory}/.config/home-manager" STATE_FILE="$STATE_DIR/state.json" - THEMES_DIR="$HOME/.local/share/nomarchy/themes" + THEMES_DIR="${config.xdg.dataHome}/nomarchy/themes" mkdir -p "$STATE_DIR" [[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE" # List all images in all themes backgrounds - WALLPAPERS=$(find "$THEMES_DIR" -type f \( -name "*.jpg" -o -name "*.png" \)) + # We search in the system-wide flake source for distro wallpapers to avoid Nix Store bloat + WALLPAPERS=$(find "${config.xdg.dataHome}/nomarchy/themes" -type f \( -name "*.jpg" -o -name "*.png" \) 2>/dev/null) + DISTRO_THEMES="/etc/nixos/nomarchy/assets/themes" + if [ -d "$DISTRO_THEMES" ]; then + WALLPAPERS="$WALLPAPERS\n$(find "$DISTRO_THEMES" -type f \( -name "*.jpg" -o -name "*.png" \))" + fi # Include user themes if they exist - if [ -d "$HOME/.config/nomarchy/themes" ]; then - WALLPAPERS="$WALLPAPERS\n$(find "$HOME/.config/nomarchy/themes" -type f \( -name "*.jpg" -o -name "*.png" \))" + if [ -d "${config.home.homeDirectory}/.config/nomarchy/themes" ]; then + WALLPAPERS="$WALLPAPERS\n$(find "${config.home.homeDirectory}/.config/nomarchy/themes" -type f \( -name "*.jpg" -o -name "*.png" \))" fi SELECTED_WP=$(echo -e "$WALLPAPERS" | walker --dmenu)