mirror of
https://github.com/Matir/skel.git
synced 2026-05-25 21:19:09 -07:00
276 lines
6.9 KiB
Bash
Executable File
276 lines
6.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# shellcheck disable=SC2155,SC2223
|
|
|
|
set -o nounset
|
|
set -o errexit
|
|
set -o shwordsplit 2>/dev/null || true # Make zsh behave like bash
|
|
|
|
HOME=${HOME:-$(cd ~ && pwd)}
|
|
|
|
|
|
|
|
have_command() {
|
|
command -v "${1}" >/dev/null 2>&1
|
|
}
|
|
|
|
|
|
|
|
link_directory_contents() {
|
|
local SRCDIR="${1}"
|
|
local DESTDIR="${2}"
|
|
local PREFIX="${3}"
|
|
local file
|
|
|
|
# shellcheck disable=SC2086
|
|
find "${SRCDIR}" \( -name .git -o \
|
|
-name install.sh -o \
|
|
-name README.md -o \
|
|
-name .gitignore \) \
|
|
-prune -o -type f -print | \
|
|
while read -r file ; do
|
|
local TARGET="${DESTDIR}/${PREFIX}${file#"${SRCDIR}"/}"
|
|
mkdir -p "$(dirname "${TARGET}")"
|
|
ln -s -f "${file}" "${TARGET}"
|
|
done
|
|
}
|
|
|
|
ssh_key_already_installed() {
|
|
# Return 1 if the key isn't already installed, 0 if it is
|
|
local AK="${HOME}/.ssh/authorized_keys"
|
|
if [[ ! -f "$AK" ]] ; then
|
|
return 1
|
|
fi
|
|
# Extract the key data (field 2) from the key file, ignoring comments
|
|
local key_data
|
|
key_data=$(awk '/^ssh-/ {print $2}' "$1")
|
|
if [[ -z "${key_data}" ]]; then
|
|
# Not a valid key file
|
|
return 1
|
|
fi
|
|
# Use grep with fixed-string matching to see if the key is present.
|
|
# The exit code of grep is 0 on match, 1 on no match, which is perfect.
|
|
grep -F -q -- "${key_data}" "${AK}"
|
|
}
|
|
|
|
install_ssh_keys() {
|
|
# Install SSH keys
|
|
verbose 'Installing SSH keys...'
|
|
local AK="${HOME}/.ssh/authorized_keys"
|
|
local key
|
|
local keydir
|
|
if [[ "${TRUST_ALL_KEYS}" = 1 ]] ; then
|
|
keydir="${BASEDIR}/keys/ssh"
|
|
else
|
|
keydir="${BASEDIR}/keys/ssh/trusted"
|
|
fi
|
|
for key in "${keydir}"/* ; do
|
|
if [[ ! -f "${key}" ]] ; then
|
|
continue
|
|
fi
|
|
if ssh_key_already_installed "${key}" ; then
|
|
verbose "Key $(basename "${key}") already installed..."
|
|
continue
|
|
fi
|
|
echo "# $(basename "${key}") added from skel on $(date +%Y-%m-%d)" >> "${AK}"
|
|
cat "${key}" >> "${AK}"
|
|
done
|
|
}
|
|
|
|
install_gpg_keys() {
|
|
have_command gpg || \
|
|
return 0
|
|
local key
|
|
for key in "${BASEDIR}"/keys/gpg/* ; do
|
|
gpg --import < "${key}" >/dev/null 2>&1
|
|
done
|
|
}
|
|
|
|
install_known_hosts() {
|
|
verbose 'Installing known hosts...' >&2
|
|
if [[ ! -f "${BASEDIR}/keys/known_hosts" ]] ; then
|
|
return 0
|
|
fi
|
|
mkdir -p "${HOME}/.ssh"
|
|
if [[ -f "${HOME}/.ssh/known_hosts" ]] ; then
|
|
local tmpf="$(mktemp)"
|
|
cat "${BASEDIR}"/keys/known_hosts "${HOME}"/.ssh/known_hosts \
|
|
| sort -u > "$tmpf"
|
|
mv "$tmpf" "${HOME}"/.ssh/known_hosts
|
|
else
|
|
cp "${BASEDIR}"/keys/known_hosts "${HOME}"/.ssh/known_hosts
|
|
fi
|
|
}
|
|
|
|
install_keys() {
|
|
install_ssh_keys
|
|
install_gpg_keys
|
|
install_known_hosts
|
|
}
|
|
|
|
setup_git_email() {
|
|
local gc_local="${HOME}/.gitconfig.local"
|
|
local current_email=""
|
|
|
|
if [[ -f "${gc_local}" ]]; then
|
|
current_email=$(git config -f "${gc_local}" user.email || true)
|
|
fi
|
|
|
|
if [[ -n "${GIT_EMAIL:-}" ]]; then
|
|
# Use environment variable
|
|
git config -f "${gc_local}" user.email "${GIT_EMAIL}"
|
|
elif [[ -n "${current_email}" ]]; then
|
|
# Already has an email set
|
|
GIT_EMAIL="${current_email}"
|
|
else
|
|
# Prompt the user
|
|
echo -n "Enter git email (leave blank to skip): " >&2
|
|
read -r GIT_EMAIL || true
|
|
if [[ -n "${GIT_EMAIL}" ]]; then
|
|
git config -f "${gc_local}" user.email "${GIT_EMAIL}"
|
|
fi
|
|
fi
|
|
export GIT_EMAIL
|
|
}
|
|
|
|
read_saved_prefs() {
|
|
# Can't use basedir here as we don't have it yet
|
|
local pref_file="$(dirname "$0")/.installed-prefs"
|
|
if [[ -f "${pref_file}" ]] ; then
|
|
verbose "Loading saved skel preferences from ${pref_file}"
|
|
# source is a bashism
|
|
# shellcheck disable=SC1090
|
|
. "${pref_file}"
|
|
fi
|
|
}
|
|
|
|
save_prefs() {
|
|
[[ "$SAVE" = 1 ]] || return 0
|
|
local pref_file=${BASEDIR}/.installed-prefs
|
|
{
|
|
echo "BASEDIR=\"${BASEDIR}\""
|
|
echo "MINIMAL=\"${MINIMAL}\""
|
|
echo "INSTALL_KEYS=\"${INSTALL_KEYS}\""
|
|
echo "TRUST_ALL_KEYS=\"${TRUST_ALL_KEYS}\""
|
|
echo "VERBOSE=\"${VERBOSE}\""
|
|
} > "$pref_file"
|
|
}
|
|
|
|
cleanup() {
|
|
if [[ -x "${BASEDIR}/bin/prune-broken-symlinks.sh" ]]; then
|
|
"${BASEDIR}/bin/prune-broken-symlinks.sh" -y "${HOME}/.zshrc.d"
|
|
"${BASEDIR}/bin/prune-broken-symlinks.sh" -y "${HOME}/bin"
|
|
fi
|
|
}
|
|
|
|
verbose() {
|
|
[[ "${VERBOSE:-0}" = 1 ]] && echo "$@" >&2 || return 0
|
|
}
|
|
|
|
# Operations
|
|
|
|
install_dotfiles() {
|
|
link_directory_contents "${BASEDIR}/dotfiles" "${HOME}" "."
|
|
if [[ -d "${BASEDIR}/local_dotfiles" ]] ; then
|
|
link_directory_contents "${BASEDIR}/local_dotfiles" "${HOME}" "."
|
|
fi
|
|
if [[ -d "${BASEDIR}/dotfile_overlays" ]] ; then
|
|
for dotfiledir in "${BASEDIR}/dotfile_overlays/"* ; do
|
|
if [[ -d "${dotfiledir}" ]] ; then
|
|
link_directory_contents "${dotfiledir}" "${HOME}" "."
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
install_main() {
|
|
if [[ -d "${BASEDIR}/.git" && have_command git ]] ; then
|
|
if [[ -z "$(git -C "${BASEDIR}" status --porcelain)" ]]; then
|
|
git -C "${BASEDIR}" pull --ff-only || true
|
|
else
|
|
echo "Skipping self-update: repository has local changes." >&2
|
|
fi
|
|
fi
|
|
[[ "$MINIMAL" = 1 ]] || {
|
|
|
|
# Install vim-plug if not already present
|
|
local VIM_PLUG_URL="https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim"
|
|
local VIM_AUTOLOAD_DIR="${HOME}/.vim/autoload"
|
|
local VIM_PLUG_FILE="${VIM_AUTOLOAD_DIR}/plug.vim"
|
|
|
|
if [[ ! -f "${VIM_PLUG_FILE}" ]]; then
|
|
verbose "Installing vim-plug..."
|
|
mkdir -p "${VIM_AUTOLOAD_DIR}"
|
|
if have_command curl; then
|
|
curl -fLo "${VIM_PLUG_FILE}" --create-dirs "${VIM_PLUG_URL}"
|
|
else
|
|
echo "Error: curl not found. Cannot install vim-plug." >&2
|
|
fi
|
|
fi
|
|
|
|
# Install TPM (Tmux Plugin Manager) if not already present
|
|
local TPM_DIR="${HOME}/.tmux/plugins/tpm"
|
|
local TPM_REPO="https://github.com/tmux-plugins/tpm"
|
|
|
|
if [[ ! -d "${TPM_DIR}" ]]; then
|
|
verbose "Installing TPM (Tmux Plugin Manager)..."
|
|
if have_command git; then
|
|
git clone --depth 1 "${TPM_REPO}" "${TPM_DIR}"
|
|
else
|
|
echo "Error: git not found. Cannot install TPM." >&2
|
|
fi
|
|
fi
|
|
|
|
# try to update dotfile overlays
|
|
if [[ -d "${BASEDIR}/dotfile_overlays" ]] ; then
|
|
for dotfiledir in "${BASEDIR}/dotfile_overlays/"* ; do
|
|
if [[ -d "${dotfiledir}/.git" ]] ; then
|
|
git -C "${dotfiledir}" pull --ff-only || true
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
install_dotfiles
|
|
link_directory_contents "${BASEDIR}/bin" "${HOME}/bin" ""
|
|
[[ "$INSTALL_KEYS" = 1 ]] && install_keys
|
|
save_prefs
|
|
setup_git_email
|
|
cleanup
|
|
}
|
|
|
|
|
|
# Setup variables
|
|
read_saved_prefs
|
|
|
|
# Defaults if not passed in or saved.
|
|
# TODO: use flags instead of environment variables.
|
|
: ${BASEDIR:=$HOME/.skel}
|
|
: ${MINIMAL:=0}
|
|
: ${INSTALL_KEYS:=1}
|
|
: ${TRUST_ALL_KEYS:=0}
|
|
: ${VERBOSE:=0}
|
|
: ${SAVE:=1}
|
|
|
|
# Check prerequisites
|
|
if [[ ! -d "$BASEDIR" ]] ; then
|
|
echo "Please install to $BASEDIR!" 1>&2
|
|
exit 1
|
|
fi
|
|
|
|
OPERATION=${1:-install}
|
|
|
|
case $OPERATION in
|
|
install)
|
|
install_main
|
|
;;
|
|
dotfiles)
|
|
install_dotfiles
|
|
;;
|
|
*)
|
|
echo "Unknown operation $OPERATION." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
echo "OK"
|