Skip to content

Instantly share code, notes, and snippets.

@glesica
Created April 4, 2012 21:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save glesica/2305700 to your computer and use it in GitHub Desktop.
Save glesica/2305700 to your computer and use it in GitHub Desktop.
A script for linking dot files into a home directory.
#!/bin/bash
# install.sh
# Author: George Lesica <glesica@gmail.com>
# Description: Deploy script for my dotfiles repo.
# License: Public Domain (where it exists), any OSI-approved
# license elsewhere.
#
# Configuration
#
repo="`pwd`"
src="${repo}/home"
manifest="${repo}/manifest"
target="$HOME"
#
# Defaults
#
do_backup=1 # Back up existing files. Default: true.
do_copy=0 # Copy instead of symlinking files. Default: false.
do_continue=0 # Continue on error instead of exit
do_help=0 # Display the help message. Default: false.
do_silent=0 # Suppress all output to stdout. Default: false.
do_test=0 # Testing mode, don't make changes. Default: false.
do_verbose=0 # Produce extra output. Default: false.
do_yes=0 # Assume "yes" to all queries. Default: false.
#
# Constants
#
PROGNAME=$(basename $0)
#
# Helper Functions
#
# Error printer
function error_echo() {
echo "${PROGNAME}: ${1:-"Unknown error"}" 1>&2
}
# Error output function
function error_exit() {
error_echo "$1"
exit 1
}
# Conditional echo that checks $do_verbose.
function verbose_echo() {
if (( $do_verbose )); then
echo $1
fi
}
# Conditional echo that checks $do_silent.
function silent_echo() {
if (( ! $do_silent )); then
echo $1
fi
}
# Back up an existing dot file.
# Return codes:
# 0 - successfully backed up file/dir
# 1 - received a blank argument
# 2 - move command failed
function backup() {
# Exit on blank argument.
if [ -z "$1" ]; then
error_echo "Empty backup target given"
return 1
fi
# Announce
verbose_echo "Backing up ${target}/.${1}"
# Return successfully if file/dir does not exist.
if [ ! -e "${target}/.${1}" ]; then
silent_echo "File ${target}/.${1} does not exist, no backup created"
return 0
fi
# Safely rename the file as a backup.
local i=0
while [ -e "${target}/.${1}.backup${i}" ]; do
i=$[$i+1]
done
# Move the file to its backup location.
silent_echo "File ${target}/.${1} exists. Backing up as ${target}/.${1}.backup${i}"
if (( ! $do_test )); then
mv "${target}/.${1}" "${target}/.${1}.backup${i}"
if [ "$?" -ne 0 ]; then
return 2
fi
else
silent_echo "Test mode, no action taken"
fi
return 0
}
# Link or copy a file or directory into the target location.
# Return codes:
# 0 - successfully handled file/dir
# 1 - received a blank argument
# 2 - source file does not exist
# 3 - backup failed
# 4 - copy or link command failed
function install() {
# Exit on a blank argument.
if [ -z "$1" ]; then
error_echo "Empty install target given"
return 1
fi
# Announce ourselves.
verbose_echo "Installing ${1}"
# Check if file exists in source.
if [ ! -e "${src}/${1}" ]; then
error_echo "File ${src}/${1} does not exist"
return 2
fi
# Determine whether we should handle the file/dir.
local proceed="Yes"
if (( ! $do_silent )) && (( ! $do_yes )); then
echo "Install file ${1}?"
select proceed in "Yes" "No"; do
break;
done
fi
# Return successfully if we were instructed to skip the file/dir.
if [ "$proceed" != "Yes" ]; then
verbose_echo "Skipping ${1} per user request"
return 0
fi
# Back up the existing version of the file/dir.
if (( $do_backup )); then
backup "$1"
if [ "$?" -ne 0 ]; then
error_echo "Backup of ${1} failed"
return 3
fi
fi
# Copy or symlink the file/dir into the target.
if (( $do_copy )); then
silent_echo "Copying file ${src}/${1} to ${target}/.${1}"
if (( ! $do_test )); then
cp -R "${src}/${1}" "${target}/.${1}";
if [ "$?" -ne 0 ]; then
error_echo "Copying ${1} failed"
return 4
fi
else
silent_echo "Test mode, no action taken"
fi
else
silent_echo "Symlinking file ${src}/${1} to ${target}/.${1}"
if (( ! $do_test )); then
ln -sf "${src}/${1}" "${target}/.${1}";
if [ "$?" -ne 0 ]; then
error_echo "Symlinking ${1} failed"
return 4
fi
else
silent_echo "Test mode, no action taken"
fi
fi
return 0
}
# Install a list of files.
function install_all() {
# Make sure the manifest file exists.
if [ ! -f ${manifest} ]; then
error_exit "Invalid source location. No manifest found"
fi
# Install each item in the manifest.
for f in `cat ${manifest}`; do
install "${f}"
if [ "$?" -ne 0 ]; then
local msg="Error processing ${f}"
if (( ! $do_continue )); then
error_exit "$msg"
else
error_echo "$msg"
fi
fi
done
return 0
}
# Print help information.
function help() {
echo "Usage: ${PROGNAME} -[bchstvy] [-m <manifest>] [-r <src>] [-d <destination>]"
echo "A script to install dot files into /home (or elsewhere) from a common location based on a manifest file."
echo " -b : Skip creating backups of existing files"
echo " -c : Copy instead of symlinking new files"
echo " -e : Continue processing manifest even if errors occur"
echo " -h : Display this help message and exit"
echo " -s : Suppress all non-error output"
echo " -t : Test run, do not modify files or directories"
echo " -v : Be extra verbose, lots of output"
echo " -y : Automatically answer 'yes' to all queries"
echo " -m <manifest> : Read list of dot files from <manifest>"
echo " -r <src> : Directory to fetch dot files from"
echo " -d <destination> : Target directory for dot files"
}
#
# Get to it!
#
# Gather arguments and such together.
while getopts "bcd:ehm:r:stvy" opt; do
case $opt in
b ) do_backup=0;;
c ) do_copy=1;;
e ) do_continue=1;;
h ) do_help=1;;
s ) do_silent=1;;
t ) do_test=1;;
v ) do_verbose=1;;
y ) do_yes=1;;
# Arguments
d ) destination="${OPTARG}";;
m ) manifest="${OPTARG}";;
r ) src="${OPTARG}";;
# If we got an invalid argument, show help text.
* ) do_help=1;;
esac
done
# Print help if requested, then exit.
if (( $do_help )); then
help
exit 0
fi
# Run the installation, then exit.
install_all
exit $?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment