Files

116 lines
3.7 KiB
Nix
Raw Permalink Normal View History

2026-08-05 18:57:23 +01:00
{...}: {
flake.nixosModules.stow = {
config,
pkgs,
lib,
...
}: let
cfg = config.ahmza.stow;
stowScript = pkgs.writeShellApplication {
name = "ahmza-stow";
runtimeInputs = [pkgs.stow pkgs.coreutils pkgs.findutils pkgs.gnused];
text = ''
set -euo pipefail
REPO="${cfg.repoPath}"
TARGET="$HOME"
if [ ! -d "$REPO" ]; then
echo "ahmza-stow: dotfiles repo not found at $REPO; skipping." >&2
echo " (clone it with: git clone ${cfg.repoUrl} $REPO)" >&2
exit 0
fi
packages=(${lib.escapeShellArgs cfg.packages})
if [ ''${#packages[@]} -eq 0 ]; then
echo "ahmza-stow: no packages configured; nothing to do."
exit 0
fi
cd "$REPO"
# Back up any existing non-symlink targets that would block stowing $pkg.
# Mirrors the dotfiles repo's own install script: list the files/links the
# package provides (relative to the package dir) and back up only real
# (non-symlink) targets. Skipping symlinks means we never write through an
# already-stowed folded directory symlink into the repo itself.
# Backups go to <file>.pre-stow.bak.
backup_conflicts() {
local pkg="$1" rel target
while IFS= read -r rel; do
target="$HOME/$rel"
if [ -e "$target" ] && [ ! -L "$target" ]; then
echo "ahmza-stow: backing up existing $target -> $target.pre-stow.bak"
mv "$target" "$target.pre-stow.bak"
fi
done < <(cd "$REPO/$pkg" && find . \( -type f -o -type l \) | sed 's|^\./||')
}
for pkg in "''${packages[@]}"; do
if [ ! -d "$pkg" ]; then
echo "ahmza-stow: package '$pkg' not present in repo; skipping." >&2
continue
fi
echo "ahmza-stow: stow -R $pkg"
# Try to stow first; only back up (and retry) if there's a real conflict.
if ! stow -R --target="$TARGET" "$pkg" 2>/dev/null; then
echo "ahmza-stow: conflicts detected for '$pkg'; backing up and retrying."
backup_conflicts "$pkg"
if ! stow -R --target="$TARGET" "$pkg"; then
echo "ahmza-stow: failed to stow '$pkg' — resolve manually." >&2
fi
fi
done
echo "ahmza-stow: done."
'';
};
in {
options.ahmza.stow = {
enable = lib.mkEnableOption "automatic GNU Stow of dotfiles on login";
user = lib.mkOption {
type = lib.types.str;
description = "User whose dotfiles should be stowed.";
};
repoPath = lib.mkOption {
type = lib.types.str;
default = "/home/${cfg.user}/dot";
defaultText = "/home/\${config.ahmza.stow.user}/dot";
description = "Absolute path to the stow dotfiles repository.";
};
repoUrl = lib.mkOption {
type = lib.types.str;
default = "https://github.com/ahmzaa/dot.git";
description = "URL of the dotfiles repo (used only in help messages).";
};
packages = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = ''
Stow package names (top-level dirs in the dotfiles repo) to link.
Feature modules append to this via lib.mkAfter / list concatenation.
'';
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [pkgs.stow stowScript];
systemd.user.services.ahmza-stow = {
description = "Stow dotfiles for ${cfg.user}";
wantedBy = ["default.target"];
serviceConfig = {
Type = "oneshot";
ExecStart = lib.getExe stowScript;
};
};
};
};
}