From 4020ad5878b42dc259dd712e868723b6d2da0ff1 Mon Sep 17 00:00:00 2001 From: Bernardo Magri Date: Sat, 4 Apr 2026 10:35:02 +0100 Subject: [PATCH] feat(core): professionalize state migration via Nix activation - Move state migration logic from shell scripts to Home Manager activation - Ensure migrations run automatically during rebuilds - Delete obsolete bin/nomarchy-migrate and migrations/ directory --- bin/nomarchy-dev-add-migration | 14 --- bin/nomarchy-migrate | 29 ----- migrations/20260404_migrate_state_to_json.sh | 89 -------------- modules/home/state.nix | 115 ++++++++++++++++--- 4 files changed, 97 insertions(+), 150 deletions(-) delete mode 100755 bin/nomarchy-dev-add-migration delete mode 100755 bin/nomarchy-migrate delete mode 100644 migrations/20260404_migrate_state_to_json.sh diff --git a/bin/nomarchy-dev-add-migration b/bin/nomarchy-dev-add-migration deleted file mode 100755 index 7780cbb..0000000 --- a/bin/nomarchy-dev-add-migration +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# Creates a new Nomarchy migration named after the unix timestamp of the last commit. -# Only intended for Nomarchy developers. - -cd ~/.local/share/nomarchy -migration_file="$HOME/.local/share/nomarchy/migrations/$(git log -1 --format=%cd --date=unix).sh" -touch $migration_file - -if [[ $1 != "--no-edit" ]]; then - nvim $migration_file -fi - -echo $migration_file diff --git a/bin/nomarchy-migrate b/bin/nomarchy-migrate deleted file mode 100755 index 5111bc7..0000000 --- a/bin/nomarchy-migrate +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -# Run all pending migrations to bring the system in line with the installed version. - -# Where we store an empty file for each migration that has already been performed. -STATE_DIR="$HOME/.local/state/nomarchy/migrations" -mkdir -p "$STATE_DIR" - -# Skipped migrations are tracked separately -mkdir -p "$STATE_DIR/skipped" - -# Run any pending migrations -for file in ~/.local/share/nomarchy/migrations/*.sh; do - filename=$(basename "$file") - - if [[ ! -f $STATE_DIR/$filename && ! -f $STATE_DIR/skipped/$filename ]]; then - echo -e "\e[32m\nRunning migration (${filename%.sh})\e[0m" - - if bash $file; then - touch "$STATE_DIR/$filename" - else - if gum confirm "Migration ${filename%.sh} failed. Skip and continue?"; then - touch "$STATE_DIR/skipped/$filename" - else - exit 1 - fi - fi - fi -done diff --git a/migrations/20260404_migrate_state_to_json.sh b/migrations/20260404_migrate_state_to_json.sh deleted file mode 100644 index 812ad08..0000000 --- a/migrations/20260404_migrate_state_to_json.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env bash - -# Migration: Migrate individual state files to unified state.json -# Version: 20260404 - -OLD_TOGGLES_DIR="$HOME/.local/state/nomarchy/toggles" -IDLE_STATE_FILE="$HOME/.config/home-manager/idle-state.json" -NIGHTLIGHT_STATE_FILE="$HOME/.config/home-manager/hyprsunset-state.json" -HYPRLAND_STATE_FILE="$HOME/.config/home-manager/hyprland-state.json" -THEME_STATE_FILE="$HOME/.config/home-manager/theme-state.nix" -WALLPAPER_STATE_FILE="$HOME/.config/home-manager/wallpaper-state.nix" -FONT_STATE_FILE="$HOME/.config/home-manager/font-state.nix" -NEW_STATE_FILE="$HOME/.config/home-manager/state.json" - -mkdir -p "$(dirname "$NEW_STATE_FILE")" -[[ ! -f $NEW_STATE_FILE ]] && echo "{}" > "$NEW_STATE_FILE" - -# 1. Migrate .local/state/nomarchy/toggles (these were 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) - echo "Migrating suspend-off -> suspend: false" - jq '.suspend = false' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - ;; - screensaver-off) - echo "Migrating screensaver-off -> screensaver: false" - jq '.screensaver = false' "$NEW_STATE_FILE" > "$NEW_STATE_FILE.tmp" && mv "$NEW_STATE_FILE.tmp" "$NEW_STATE_FILE" - ;; - skip-vscode-theme-changes) - echo "Migrating skip-vscode-theme-changes -> skipVsCodeTheme: true" - 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 - echo "Migrating idle-state.json -> state.json" - 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 - echo "Migrating hyprsunset-state.json -> state.json" - 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 - echo "Migrating hyprland-state.json -> state.json" - 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 - echo "Migrating theme-state.nix -> state.json" - 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 - echo "Migrating wallpaper-state.nix -> state.json" - 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 - echo "Migrating font-state.nix -> state.json" - 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 - -echo "State migration complete." diff --git a/modules/home/state.nix b/modules/home/state.nix index 3d788e8..31b0861 100644 --- a/modules/home/state.nix +++ b/modules/home/state.nix @@ -1,4 +1,4 @@ -{ config, lib, ... }: +{ config, lib, pkgs, ... }: let stateDir = "${config.home.homeDirectory}/.config/home-manager"; @@ -21,23 +21,102 @@ let togglesState = readState "state.json" {}; in { - config.nomarchy = { - toggles = { - suspend = togglesState.suspend or true; - screensaver = togglesState.screensaver or true; - idle = togglesState.idle or true; - nightlight = togglesState.nightlight or false; - waybar = togglesState.waybar or true; - skipVsCodeTheme = togglesState.skipVsCodeTheme or false; + 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; + screensaver = togglesState.screensaver or true; + idle = togglesState.idle or true; + nightlight = togglesState.nightlight or false; + waybar = togglesState.waybar or true; + skipVsCodeTheme = togglesState.skipVsCodeTheme or false; + }; + nightlightTemperature = togglesState.nightlightTemperature or 4000; + theme = togglesState.theme or "nord"; + wallpaper = togglesState.wallpaper or ""; + hyprland = { + gaps_in = togglesState.hyprland.gaps_in or 5; + gaps_out = togglesState.hyprland.gaps_out or 10; + border_size = togglesState.hyprland.border_size or 2; + }; + fonts.monospace = togglesState.font or "JetBrainsMono Nerd Font"; }; - nightlightTemperature = togglesState.nightlightTemperature or 4000; - theme = togglesState.theme or "nord"; - wallpaper = togglesState.wallpaper or ""; - hyprland = { - gaps_in = togglesState.hyprland.gaps_in or 5; - gaps_out = togglesState.hyprland.gaps_out or 10; - border_size = togglesState.hyprland.border_size or 2; - }; - fonts.monospace = togglesState.font or "JetBrainsMono Nerd Font"; }; }