upd: avoid pulling the same repo twice, support arguments

This commit is contained in:
PowerUser64 2025-03-24 23:26:11 -07:00
parent 3eaa2828ba
commit 94dfd777bc

View file

@ -1,33 +1,23 @@
#!/bin/bash
# updates some important repositories in my home folder (and elsewhere) that exist on multiple devices
#!/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
# Check for git
if ! command -v git > /dev/null; then
echo "git is not installed, please install git and try again"
exit 1
fi
#
# Setup functions and variables
#
# 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=""
GREEN=""
NC="(B"
fi
# global name of script
script="${0##*/}"
# Avoiding copy pasting this over and over
g() { git -C "$REPO" "$@"; }
cleanup() {
exit
}
REPOS_TO_UPDATE="${REPOS_TO_UPDATE:-}"
# path_append from https://superuser.com/questions/39751/add-directory-to-path-if-its-not-already-there
# 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
path_append() {
update_list_append() {
if [ -d "$1" ]; then
# shellcheck disable=SC3010
if command '[[' > /dev/null 2>&1 && [[ ":$REPOS_TO_UPDATE:" != *":$1:"* ]]; then
@ -38,31 +28,162 @@ path_append() {
fi
}
# shellcheck disable=SC2016
REPO_ABBR='echo ${REPO/"$HOME"/"~"}'
# Do the arguments contain a search?
# Usage: contains search "${array[@]}"
contains() {
search="$1"; shift
split="$1"; shift
array="$1"; shift
path_append "${DOCS_DIR:-}"
path_append "$HOME/bin"
path_append "${NIXOS_DIR:-}"
path_append "$HOME/code/faust-ideas"
path_append "$HOME/code/nixvim-config"
path_append "$HOME/.config/nvim"
ifs="$IFS"
IFS="$split"
for str in $array; do
if [ "$str" = "$search" ]; then
IFS="$ifs"
return 0
fi
done
IFS="$ifs"
return 1
}
# `git pull` everything mentioned in the REPOS_TO_UPDATE variable
IFS=: read -ra REPOS_TO_UPDATE_ARR <<< "${REPOS_TO_UPDATE:-}"
for REPO in "${REPOS_TO_UPDATE_ARR[@]}"; do
if [ -n "$REPO" ]; then
if g rev-parse > /dev/null 2>&1; then
echo " ${GREEN}Pulling $(eval "$REPO_ABBR")‥${NC}"
g pull
g diff --quiet || echo "${YELLOW}Warning: Working tree for $(eval "$REPO_ABBR") is dirty${NC}"
# 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_STUFF=true
done
# check for dotfiles updates
${UPDATED_STUFF:-false} && echo
echo " ${GREEN}Updating dotfiles…${NC}"
dotfiles pull
dotfiles diff --quiet || echo "${YELLOW}Warning: Working tree for dotfiles is dirty${NC}"
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=""
green=""
nc="(B"
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 "$@"