Compare commits
2 Commits
779b8c6c9d
...
2c4341e5e8
Author | SHA1 | Date | |
---|---|---|---|
2c4341e5e8 | |||
2fb59d407e |
@ -35,5 +35,6 @@ Included within are also a handful of management scripts.
|
||||
|
||||
| script | description |
|
||||
| --- | --- |
|
||||
| `bootleg-stow` | USE AT YOUR OWN RISK. Tries its best to emulate Stow in an environment where it's not available. Should at the very least allow you to install and update these dotfiles without too much headache. |
|
||||
| `deploy-all.sh` | Deploys all packages. Note that I reserve the right to produce packages with conflicting contents, so in the future this may be refined to only deploy a specific subset of packages (base, desktop-common, etc.). |
|
||||
| `submodule-checkout-masters-and-update.sh` | For every submodule in the repo, checks out its master branch (instead of the exact commit specified in the base repo) and then pulls from upstream. Effectively, this updates any submodules in the repo, such as Vim plugins, themes, etc. |
|
||||
|
242
bootleg-stow
Executable file
242
bootleg-stow
Executable file
@ -0,0 +1,242 @@
|
||||
#! /bin/bash
|
||||
#
|
||||
# bootleg-stow
|
||||
# Copyright (C) 2021 Vintage Salt <rehashedsalt@cock.li>
|
||||
#
|
||||
# Distributed under terms of the MIT license.
|
||||
#
|
||||
set -e
|
||||
|
||||
# Read-only set-once variables
|
||||
declare -r _name="$(basename -- "$0")"
|
||||
declare -i _opthelp
|
||||
declare -i _optverbose
|
||||
# Modes
|
||||
declare -i _optstow=1
|
||||
declare -i _optunstow
|
||||
# Working variables
|
||||
declare -a _args
|
||||
declare _return
|
||||
declare _files
|
||||
declare _directories
|
||||
|
||||
# Helper functions
|
||||
log() {
|
||||
# Print a line to the terminal if _optverbose is greater than $2
|
||||
# $2 defaults to 0
|
||||
# loglevel 0: Daily-use messages
|
||||
# loglevel 1: Detailed but not quite debugging
|
||||
# loglevel 2: Definitely debugging
|
||||
[ -z "$1" ] && return 1
|
||||
if (( _optverbose >= ${2:-0} )); then
|
||||
printf "%s\\n" "$1"
|
||||
fi
|
||||
}
|
||||
warn() {
|
||||
# Print a yellow line to the terminal, respecting _optverbose
|
||||
[ -z "$1" ] && return 1
|
||||
if (( _optverbose >= ${2:-0} )); then
|
||||
if [ -t 1 ]; then
|
||||
printf "\\e[33m%s\\e[0m\\n" "$1"
|
||||
else
|
||||
printf "WARN: %s\\n" "$1"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
error() {
|
||||
# Print a red line to the terminal, exit if $2 is specified
|
||||
[ -z "$1" ] && return 1
|
||||
if [ -t 2 ]; then
|
||||
printf "\\e[31m%s\\e[0m\\n" "$1" 1>&2
|
||||
else
|
||||
printf "ERROR: %s\\n" "$1" 1>&2
|
||||
fi
|
||||
[ -z "$2" ] && return
|
||||
exit "${2:-1}"
|
||||
}
|
||||
has() {
|
||||
# Parse out all arguments and try to find them in path
|
||||
# If an argument cannot be found, set _return and fail
|
||||
for prog in "$@"; do
|
||||
if ! command -v "$prog" > /dev/null 2>&1; then
|
||||
_return="$prog"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
checkconflict() {
|
||||
# Take a directory as argument 1 and stow its contents in ..
|
||||
[ -z "$1" ] && return 1
|
||||
if ! [ -d "$1" ]; then
|
||||
error "Could not find directory: $1" 2
|
||||
fi
|
||||
if ! [ -r "$1" ]; then
|
||||
error "Could not read directory: $1" 2
|
||||
fi
|
||||
# Get our list of files
|
||||
local files="$(find "$1" -type f)"
|
||||
local directories="$(find "$1" -type d)"
|
||||
local -a conflict
|
||||
# Iterate over them
|
||||
for file in $files; do
|
||||
# Get the basename of the file
|
||||
filename="${file#"$1"/}"
|
||||
log "$filename" 2
|
||||
if [ -f ../"$filename" ]; then
|
||||
if [ -h ../"$filename" ]; then
|
||||
continue
|
||||
else
|
||||
conflict+=("$filename")
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ -n "${conflict[*]}" ]; then
|
||||
warn "The following non-symlinks would be touched by this operation:"
|
||||
for file in "${conflict[@]}"; do
|
||||
echo "$file"
|
||||
done
|
||||
error "Please resolve these conflicts manually." 3
|
||||
fi
|
||||
_files="$files"
|
||||
_directories="$directories"
|
||||
}
|
||||
stow() {
|
||||
# Stow all of _files and _directories in $1 as a package name
|
||||
# Note that you should checkconflict first
|
||||
[ -z "$1" ] && return 1
|
||||
stowdir="$(basename -- "$PWD")"
|
||||
log "Stow directory is: $stowdir" 2
|
||||
pushd .. > /dev/null 2>&1
|
||||
for dir in $_directories; do
|
||||
dirname="${dir#"$1"/}"
|
||||
if [ "$dir" == "$1" ]; then
|
||||
log "Skipping package directory: $dir" 2
|
||||
continue
|
||||
fi
|
||||
if ! [ -d "$dirname" ]; then
|
||||
log "Creating directory: $dirname" 2
|
||||
mkdir -p "$dirname"
|
||||
fi
|
||||
done
|
||||
for file in $_files; do
|
||||
filename="${file#"$1"/}"
|
||||
if has realpath dirname; then
|
||||
path="$(realpath --relative-to="$(dirname "$filename")" "$PWD/$stowdir/$1/$filename")"
|
||||
else
|
||||
path="$PWD/$stowdir/$1/$filename"
|
||||
fi
|
||||
log "Linking file: $filename to $path" 2
|
||||
if [ -h "$filename" ]; then
|
||||
rm "$filename"
|
||||
fi
|
||||
ln -s "$path" "$filename"
|
||||
done
|
||||
popd > /dev/null 2>&1
|
||||
}
|
||||
unstow() {
|
||||
# Unstow all of _files and _directories
|
||||
pushd .. > /dev/null 2>&1
|
||||
for file in $_files; do
|
||||
filename="${file#"$1"/}"
|
||||
if [ -h "$filename" ]; then
|
||||
rm "$filename"
|
||||
else
|
||||
warn "File is not a symlink: $filename"
|
||||
fi
|
||||
done
|
||||
_directories="$(echo "$_directories" | tac)"
|
||||
for dir in $_directories; do
|
||||
# We silently ignore errors here so that rmdiring a directory with stuff in
|
||||
# it doesn't break our loop.
|
||||
rmdir -p "$dir" > /dev/null 2>&1 || continue
|
||||
done
|
||||
popd > /dev/null 2>&1
|
||||
}
|
||||
|
||||
# Core program functions
|
||||
printhelp() {
|
||||
cat << EOF
|
||||
Usage: $_name [OPTION]... PACKAGE...
|
||||
Bootleg stow for the poor sods who can't get it.
|
||||
|
||||
-h Print this help text
|
||||
-R Unstow a directory.
|
||||
-S Stow a directory. Default operation.
|
||||
-v Print more status messages. Stacks
|
||||
|
||||
Copyright (c) 2021 rehashedsalt@cock.li
|
||||
Licensed under the MIT license
|
||||
EOF
|
||||
}
|
||||
|
||||
# Main
|
||||
main() {
|
||||
# Parse out arguments
|
||||
while [ -n "$1" ]; do
|
||||
# Parse out flags
|
||||
while getopts ":hRSv" opt; do
|
||||
case $opt in
|
||||
h)
|
||||
_opthelp=1
|
||||
;;
|
||||
R)
|
||||
_optunstow=1
|
||||
;;
|
||||
S)
|
||||
_optstow=1
|
||||
;;
|
||||
v)
|
||||
_optverbose+=1
|
||||
;;
|
||||
:)
|
||||
error "Option requires argument: -$OPTARG" 2
|
||||
;;
|
||||
*)
|
||||
error "Invalid option: -$OPTARG" 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# Store arguments
|
||||
shift $((OPTIND - 1))
|
||||
if [ -n "$1" ]; then
|
||||
_args+=("$1")
|
||||
shift
|
||||
fi
|
||||
unset OPTIND
|
||||
done
|
||||
# Early hook for help
|
||||
[ -n "$_opthelp" ] && printhelp && exit 0
|
||||
# Validate critical options
|
||||
# TODO: That
|
||||
# Validate core program dependencies
|
||||
log "Validating dependencies" 2
|
||||
if ! has basename find tac; then
|
||||
error "Failed to find program: $_return" 1
|
||||
fi
|
||||
|
||||
# Do the do
|
||||
if [ -n "$_optunstow" ]; then
|
||||
if [ -n "${_args[*]}" ]; then
|
||||
for package in "${_args[@]}"; do
|
||||
checkconflict "$package"
|
||||
unstow "$package"
|
||||
done
|
||||
else
|
||||
error "No package specified" 1
|
||||
fi
|
||||
elif [ -n "$_optstow" ]; then
|
||||
if [ -n "${_args[*]}" ]; then
|
||||
for package in "${_args[@]}"; do
|
||||
checkconflict "$package"
|
||||
stow "$package"
|
||||
done
|
||||
else
|
||||
error "No package specified" 1
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
Loading…
Reference in New Issue
Block a user