diff --git a/modules/stash/help.scm b/modules/stash/help.scm index d447b59..e14128d 100644 --- a/modules/stash/help.scm +++ b/modules/stash/help.scm @@ -9,35 +9,43 @@ (define (display-help) "Display help message explaining how to use the program." (display "\ -Usage: stash.scm [OPTION...] [.] +Usage: stash [OPTION...] [PACKAGE|.] -Stash is a symlink management utility that helps organize your files by moving them -to a target location and creating symbolic links in their original location. +Enhanced Stash with GNU Stow-like functionality for dotfiles management. Options: -s, --source=DIR Source directory to stash -t, --target=DIR Target directory where files will be stashed -r, --recursive Recursively process directories under source -i, --interactive Interactively prompt for target directory + -d, --deploy Deploy mode: create symlinks from dotfiles repo -h, --help Display this help -v, --version Display version information -Examples: - # Using dot syntax (after files are stashed): - cd ~/.dotfiles/config/nvim - stash.scm . # Creates symlink in ~/.config/nvim - +Stashing Examples (moving files to storage): # Stash a single directory: - stash.scm -s ~/Documents/notes -t ~/backup/notes # Move notes and create symlink - + stash -s ~/Documents/notes -t ~/backup/notes + # Stash with interactive target selection: - stash.scm -s ~/Pictures -i # Will prompt for target directory + stash -s ~/Pictures -i # Recursively stash an entire directory: - stash.scm -s ~/.config -t ~/.dotfiles/config -r # Stash all config files + stash -s ~/.config -t ~/.dotfiles/config -r - # Stash any directory to any location: - stash.scm -s ~/projects/web -t ~/backup/code/web # Not limited to dotfiles +Deployment Examples (GNU Stow-like functionality): + # Deploy all packages from dotfiles directory: + cd ~/.dotfiles && stash -d + + # Deploy specific package: + cd ~/.dotfiles && stash shell + + # Using dot syntax (create symlink from current dir to home): + cd ~/.dotfiles/shell && stash . + +Dotfiles Workflow: + 1. Collect dotfiles: ~/.files/collect-dotfiles.sh + 2. Stash to dotfiles repo: stash -s ~/.zshrc -t ~/.dotfiles/shell/zshrc + 3. On new machine: cd ~/.dotfiles && stash -d Note: Stash works with any directories, not just config files or dotfiles. You can use it to organize any files by moving them to a backup/storage diff --git a/stash b/stash new file mode 100755 index 0000000..d89a8d7 --- /dev/null +++ b/stash @@ -0,0 +1,14 @@ +#!/bin/bash + +# Stash - Enhanced symlink management utility +# Wrapper script that sets up the proper Guile load path + +# Get the directory where this script is located +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Set up Guile load path to include both project root and modules directory +export GUILE_LOAD_PATH="$SCRIPT_DIR:$SCRIPT_DIR/modules:$GUILE_LOAD_PATH" +export GUILE_AUTO_COMPILE=0 + +# Run the modular stash.scm with proper module loading +exec guile "$SCRIPT_DIR/stash.scm" "$@" diff --git a/stash.scm b/stash.scm index be7aece..df31b55 100644 --- a/stash.scm +++ b/stash.scm @@ -20,6 +20,7 @@ (source (value #t) (single-char #\s)) (recursive (value #f) (single-char #\r)) (interactive (value #f) (single-char #\i)) + (deploy (value #f) (single-char #\d)) (help (value #f) (single-char #\h)) (version (value #f) (single-char #\v)))) @@ -29,17 +30,38 @@ (let* ((options (getopt-long args %options)) (help-wanted? (option-ref options 'help #f)) + (version-wanted? (option-ref options 'version #f)) (source (option-ref options 'source #f)) (target (option-ref options 'target #f)) - (recursive? (option-ref options 'recursive #f))) + (recursive? (option-ref options 'recursive #f)) + (interactive? (option-ref options 'interactive #f)) + (deploy? (option-ref options 'deploy #f)) + (remaining-args (option-ref options '() '()))) (cond (help-wanted? (display-help) (exit 0)) + (version-wanted? + (display-version) + (exit 0)) + (deploy? + (handle-deploy-mode remaining-args) + #t) ((and source target) (handle-explicit-stash source target recursive?) #t) + ((and source interactive?) + (handle-interactive-stash source recursive?) + #t) + ;; Handle dot syntax: stash . + ((and (= (length remaining-args) 1) (string=? (car remaining-args) ".")) + (handle-dot-syntax) + #t) + ;; Handle package deployment: stash package-name + ((and (= (length remaining-args) 1) (not (string=? (car remaining-args) "."))) + (handle-package-deploy (car remaining-args)) + #t) (else (display-help) (exit 1))))) @@ -71,6 +93,105 @@ (execute-operations operations) #t)) +;;; Enhanced handler functions for GNU Stow-like functionality + +(define (handle-interactive-stash source recursive?) + "Handle interactive stashing where user selects target directory." + (display "Interactive target selection:\n") + (display (format #f "Source: ~a\n" source)) + (display "Enter target directory: ") + (let ((target (read-line))) + (if (string-null? target) + (begin + (display "No target specified. Exiting.\n") + (exit 1)) + (handle-explicit-stash source target recursive?)))) + +(define (handle-dot-syntax) + "Handle dot syntax: create symlink from current directory to home." + (let* ((current-dir (getcwd)) + (home-dir (getenv "HOME")) + (relative-path (string-drop current-dir (+ (string-length home-dir) 1))) + (target-path (string-append home-dir "/" relative-path))) + (display (format #f "Creating symlink: ~a -> ~a\n" target-path current-dir)) + (when (file-exists? target-path) + (if (file-is-symlink? target-path) + (delete-file target-path) + (begin + (display (format #f "Error: ~a already exists and is not a symlink\n" target-path)) + (exit 1)))) + (let ((target-dir (dirname target-path))) + (when (not (file-exists? target-dir)) + (mkdir-p target-dir))) + (symlink current-dir target-path) + (display (format #f "Created symlink: ~a -> ~a\n" target-path current-dir)))) + +(define (handle-package-deploy package-name) + "Deploy a specific package from current directory." + (let* ((package-dir (string-append (getcwd) "/" package-name)) + (home-dir (getenv "HOME"))) + (if (file-exists? package-dir) + (deploy-package package-dir home-dir) + (begin + (display (format #f "Error: Package directory ~a not found\n" package-dir)) + (exit 1))))) + +(define (handle-deploy-mode args) + "Handle deploy mode: deploy all packages or specific package." + (let ((current-dir (getcwd)) + (home-dir (getenv "HOME"))) + (if (null? args) + ;; Deploy all packages in current directory + (deploy-all-packages current-dir home-dir) + ;; Deploy specific package + (handle-package-deploy (car args))))) + +(define (deploy-all-packages dotfiles-dir home-dir) + "Deploy all packages from dotfiles directory." + (display (format #f "Deploying all packages from ~a to ~a\n" dotfiles-dir home-dir)) + (let ((entries (scandir dotfiles-dir))) + (for-each + (lambda (entry) + (let ((entry-path (string-append dotfiles-dir "/" entry))) + (when (and (not (member entry '("." ".." ".git" "README.md" "collect-dotfiles.sh" "STASH_GUIDE.md"))) + (file-is-directory? entry-path)) + (display (format #f "Deploying package: ~a\n" entry)) + (deploy-package entry-path home-dir)))) + entries))) + +(define (deploy-package package-dir home-dir) + "Deploy a single package by creating symlinks." + (let ((package-name (basename package-dir))) + (display (format #f "Deploying package: ~a\n" package-name)) + (deploy-directory-contents package-dir home-dir))) + +(define (deploy-directory-contents source-dir target-base) + "Recursively deploy directory contents by creating symlinks." + (let ((entries (scandir source-dir))) + (for-each + (lambda (entry) + (when (not (member entry '("." ".."))) + (let* ((source-path (string-append source-dir "/" entry)) + (target-path (string-append target-base "/." entry))) + (cond + ((file-is-directory? source-path) + ;; For directories, create the directory and recurse + (when (not (file-exists? target-path)) + (mkdir target-path #o755)) + (deploy-directory-contents source-path (dirname target-path))) + (else + ;; For files, create symlink + (when (file-exists? target-path) + (if (file-is-symlink? target-path) + (delete-file target-path) + (display (format #f "Warning: ~a exists and is not a symlink, skipping\n" target-path)))) + (let ((target-dir (dirname target-path))) + (when (not (file-exists? target-dir)) + (mkdir-p target-dir))) + (symlink source-path target-path) + (display (format #f "Created symlink: ~a -> ~a\n" target-path source-path))))))) + entries))) + ;; Entry point for stash (let ((result (main (command-line)))) (if result