initial commit
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
{...}: {
|
||||
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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user