mirror of https://codeberg.org/glenneth/stash.git
feat: Add GNU Stow-like functionality to modular stash
- Add deploy mode (-d) for batch deployment from dotfiles repo - Add dot syntax support (stash .) for reverse symlinking - Add interactive mode improvements - Add package-specific deployment (stash package-name) - Update help module with new functionality - Create wrapper script that sets proper GUILE_LOAD_PATH - Maintain clean modular architecture - All original stash functionality preserved Usage: ./stash -d # Deploy all packages ./stash shell # Deploy shell package ./stash . # Dot syntax symlinking ./stash -s ~/.zshrc -t ~/.dotfiles/shell/zshrc # Traditional stashing
This commit is contained in:
parent
9613bcd154
commit
47b78215d2
|
|
@ -9,35 +9,43 @@
|
||||||
(define (display-help)
|
(define (display-help)
|
||||||
"Display help message explaining how to use the program."
|
"Display help message explaining how to use the program."
|
||||||
(display "\
|
(display "\
|
||||||
Usage: stash.scm [OPTION...] [.]
|
Usage: stash [OPTION...] [PACKAGE|.]
|
||||||
|
|
||||||
Stash is a symlink management utility that helps organize your files by moving them
|
Enhanced Stash with GNU Stow-like functionality for dotfiles management.
|
||||||
to a target location and creating symbolic links in their original location.
|
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-s, --source=DIR Source directory to stash
|
-s, --source=DIR Source directory to stash
|
||||||
-t, --target=DIR Target directory where files will be stashed
|
-t, --target=DIR Target directory where files will be stashed
|
||||||
-r, --recursive Recursively process directories under source
|
-r, --recursive Recursively process directories under source
|
||||||
-i, --interactive Interactively prompt for target directory
|
-i, --interactive Interactively prompt for target directory
|
||||||
|
-d, --deploy Deploy mode: create symlinks from dotfiles repo
|
||||||
-h, --help Display this help
|
-h, --help Display this help
|
||||||
-v, --version Display version information
|
-v, --version Display version information
|
||||||
|
|
||||||
Examples:
|
Stashing Examples (moving files to storage):
|
||||||
# Using dot syntax (after files are stashed):
|
|
||||||
cd ~/.dotfiles/config/nvim
|
|
||||||
stash.scm . # Creates symlink in ~/.config/nvim
|
|
||||||
|
|
||||||
# Stash a single directory:
|
# 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 with interactive target selection:
|
||||||
stash.scm -s ~/Pictures -i # Will prompt for target directory
|
stash -s ~/Pictures -i
|
||||||
|
|
||||||
# Recursively stash an entire directory:
|
# 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:
|
Deployment Examples (GNU Stow-like functionality):
|
||||||
stash.scm -s ~/projects/web -t ~/backup/code/web # Not limited to dotfiles
|
# 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.
|
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
|
You can use it to organize any files by moving them to a backup/storage
|
||||||
|
|
|
||||||
|
|
@ -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" "$@"
|
||||||
123
stash.scm
123
stash.scm
|
|
@ -20,6 +20,7 @@
|
||||||
(source (value #t) (single-char #\s))
|
(source (value #t) (single-char #\s))
|
||||||
(recursive (value #f) (single-char #\r))
|
(recursive (value #f) (single-char #\r))
|
||||||
(interactive (value #f) (single-char #\i))
|
(interactive (value #f) (single-char #\i))
|
||||||
|
(deploy (value #f) (single-char #\d))
|
||||||
(help (value #f) (single-char #\h))
|
(help (value #f) (single-char #\h))
|
||||||
(version (value #f) (single-char #\v))))
|
(version (value #f) (single-char #\v))))
|
||||||
|
|
||||||
|
|
@ -29,17 +30,38 @@
|
||||||
|
|
||||||
(let* ((options (getopt-long args %options))
|
(let* ((options (getopt-long args %options))
|
||||||
(help-wanted? (option-ref options 'help #f))
|
(help-wanted? (option-ref options 'help #f))
|
||||||
|
(version-wanted? (option-ref options 'version #f))
|
||||||
(source (option-ref options 'source #f))
|
(source (option-ref options 'source #f))
|
||||||
(target (option-ref options 'target #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
|
(cond
|
||||||
(help-wanted?
|
(help-wanted?
|
||||||
(display-help)
|
(display-help)
|
||||||
(exit 0))
|
(exit 0))
|
||||||
|
(version-wanted?
|
||||||
|
(display-version)
|
||||||
|
(exit 0))
|
||||||
|
(deploy?
|
||||||
|
(handle-deploy-mode remaining-args)
|
||||||
|
#t)
|
||||||
((and source target)
|
((and source target)
|
||||||
(handle-explicit-stash source target recursive?)
|
(handle-explicit-stash source target recursive?)
|
||||||
#t)
|
#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
|
(else
|
||||||
(display-help)
|
(display-help)
|
||||||
(exit 1)))))
|
(exit 1)))))
|
||||||
|
|
@ -71,6 +93,105 @@
|
||||||
(execute-operations operations)
|
(execute-operations operations)
|
||||||
#t))
|
#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
|
;; Entry point for stash
|
||||||
(let ((result (main (command-line))))
|
(let ((result (main (command-line))))
|
||||||
(if result
|
(if result
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue