189 lines
4.7 KiB
Bash
Executable file
189 lines
4.7 KiB
Bash
Executable file
#!/bin/sh
|
||
# Update dotfiles and some other common repositories.
|
||
# This script accepts no arguments, but accepts additional repositories through the REPOS_TO_UPDATE environment variable
|
||
|
||
set -eu
|
||
|
||
#
|
||
# Setup functions and variables
|
||
#
|
||
|
||
# global name of script
|
||
script="${0##*/}"
|
||
|
||
cleanup() {
|
||
exit
|
||
}
|
||
|
||
# update_list_append from https://superuser.com/questions/39751/add-directory-to-path-if-its-not-already-there
|
||
# checks if a path is already in the PATH and if it exists
|
||
update_list_append() {
|
||
if [ -d "$1" ]; then
|
||
# shellcheck disable=SC3010
|
||
if command '[[' > /dev/null 2>&1 && [[ ":$REPOS_TO_UPDATE:" != *":$1:"* ]]; then
|
||
REPOS_TO_UPDATE="${REPOS_TO_UPDATE:+"$REPOS_TO_UPDATE:"}$1"
|
||
else
|
||
REPOS_TO_UPDATE="$REPOS_TO_UPDATE:$1"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# Do the arguments contain a search?
|
||
# Usage: contains search "${array[@]}"
|
||
contains() {
|
||
search="$1"; shift
|
||
split="$1"; shift
|
||
array="$1"; shift
|
||
|
||
ifs="$IFS"
|
||
IFS="$split"
|
||
for str in $array; do
|
||
if [ "$str" = "$search" ]; then
|
||
IFS="$ifs"
|
||
return 0
|
||
fi
|
||
done
|
||
IFS="$ifs"
|
||
return 1
|
||
}
|
||
|
||
# Shorten the repo name to use a ~ for the home dir instead of the absolute path
|
||
pretty_path() {
|
||
arg="$1"
|
||
|
||
case "$arg" in
|
||
# If the arg starts with our home dir,
|
||
"$HOME"*)
|
||
# replace the home dir part with a ~
|
||
printf '~%s' "$(printf '%s' "$arg" | sed -E 's/^.{'"${#HOME}"'}//')"
|
||
;;
|
||
|
||
# Otherwise,
|
||
*)
|
||
# Just print the arg
|
||
printf '%s' "${arg}"
|
||
;;
|
||
esac | # Pass through some other filters
|
||
sed -E 's_/+$__' | # Remove trailing slash
|
||
tr -s '/' # Remove duplicate slashes
|
||
}
|
||
|
||
# Warn about incomplete changes to a repository
|
||
do_checks() {
|
||
repo_pretty="$1"
|
||
shift
|
||
|
||
# Uncommitted changes
|
||
if ! "$@" diff --quiet; then
|
||
echo "${yellow}Warning: Working tree for $repo_pretty is dirty${nc}"
|
||
fi
|
||
|
||
# Unpushed changes
|
||
unpushed="$("$@" for-each-ref --format=" %(refname:short) %(push:track)" refs/heads)"
|
||
if echo "$unpushed" | grep -Fq ahead; then
|
||
printf '%s' "${yellow}Warning: Unpushed commits in $repo_pretty on branches:"
|
||
printf '%s' "$unpushed" | tr '\n' ','
|
||
echo "${nc}"
|
||
fi
|
||
}
|
||
|
||
do_pull() {
|
||
update_dotfiles="${update_dotfiles:-false}"
|
||
|
||
# Which repos have we updated so far
|
||
updated=''
|
||
|
||
#
|
||
# Main loop: `git pull` everything mentioned in the REPOS_TO_UPDATE variable
|
||
#
|
||
|
||
repo=''
|
||
for repo in "$@"; do
|
||
if [ -n "$repo" ]; then
|
||
repo_pretty="$(pretty_path "$repo")"
|
||
repo="$(realpath "$repo" 2>/dev/null)"
|
||
repo="$(git -C "$repo" rev-parse --show-toplevel 2> /dev/null || true)"
|
||
if [ -n "$repo" ]; then
|
||
if ! contains "$repo" ':' "$updated"; then
|
||
# Pull the repository
|
||
echo " Pulling ${green}$repo_pretty${nc}‥"
|
||
if ! timeout 10 git -C "$repo" pull; then
|
||
exit=$#
|
||
if [ "$ignore_git_errors" != true ]; then
|
||
return "$exit"
|
||
fi
|
||
fi
|
||
|
||
updated="$repo:$updated"
|
||
|
||
# Warn about incomplete changes to a repository
|
||
do_checks "$repo_pretty" git -C "$repo"
|
||
did_updates=true
|
||
if [ "$update_dotfiles" = 'false' ]; then
|
||
echo
|
||
fi
|
||
fi
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# update dotfiles repo separately bc it's a bare repo
|
||
if [ "$update_dotfiles" = true ]; then
|
||
# check for dotfiles updates
|
||
${did_updates:-false} && echo
|
||
echo " Updating ${green}dotfiles${nc}…"
|
||
dotfiles pull
|
||
do_checks "$(dotfiles rev-parse --git-dir)" dotfiles
|
||
fi
|
||
}
|
||
|
||
main() {
|
||
trap cleanup INT
|
||
|
||
# Check for git
|
||
if ! command -v git > /dev/null; then
|
||
echo "$script: git is not installed, please install git and try again" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# Set colors
|
||
if command -v tput > /dev/null; then
|
||
yellow="$(tput setaf 3)"
|
||
green="$(tput setaf 2)"
|
||
nc="$(tput sgr0)"
|
||
elif [ -n "$TERMUX_VERSION" ]; then
|
||
yellow="[33m"
|
||
green="[32m"
|
||
nc="(B[m"
|
||
fi
|
||
|
||
# Default repos to update
|
||
ignore_git_errors=false
|
||
if [ $# = 0 ]; then
|
||
# Default paths to check
|
||
REPOS_TO_UPDATE="${REPOS_TO_UPDATE:-}"
|
||
|
||
set -- "$@" \
|
||
"${DOCS_DIR:-}" \
|
||
"$HOME/bin" \
|
||
"${NIXOS_DIR:-}" \
|
||
"$HOME/code/faust-ideas" \
|
||
"$HOME/code/nixvim-config" \
|
||
"$HOME/.config/nvim"
|
||
|
||
split="$IFS"
|
||
IFS=':'
|
||
for dir in $REPOS_TO_UPDATE; do
|
||
set -- "$dir" "$@"
|
||
done
|
||
IFS="$split"
|
||
|
||
update_dotfiles=true
|
||
else
|
||
ignore_git_errors="${IGNORE_GIT_ERRORS:-true}"
|
||
fi
|
||
|
||
do_pull "$@"
|
||
}
|
||
|
||
main "$@"
|