Files

138 lines
3.9 KiB
Bash
Executable File

#!/bin/bash
#
# Purpose : Install and manage MiniServer sibling projects
#
# A sibling is a git repository cloned into ~/.local/share/siblings/<name>.
# If it has an install.sh, that runs once when the sibling is added.
# Every `update` pulls each sibling and runs its update.sh, if it has one
# (see updates.d/71-siblings.sh). Ports listed in a sibling's firewall.conf
# are opened when it's added and closed when it's removed.
SIBLINGS="$HOME/.local/share/siblings"
# Provides setup_firewall
source "$HOME/.local/share/MiniServer/utils.sh"
usage() {
cat << EOF
Usage: sibling [command]
add [url] Install a sibling project from a git repository (default)
list Show installed sibling projects
remove <name> Uninstall a sibling project
EOF
}
# Derive a directory name from a git URL, e.g.
# https://host/user/my-project.git -> my-project
sibling_name() {
local name="${1%/}"
name="${name##*/}"
name="${name##*:}"
echo "${name%.git}"
}
add_sibling() {
local url="$1" name dir
if [ -z "$url" ]; then
read -rp "Git repository URL for the sibling project: " url
fi
if [ -z "$url" ]; then
echo "No URL given."
return 1
fi
name="$(sibling_name "$url")"
if [[ -z "$name" || "$name" == .* ]]; then
echo "Could not work out a project name from: $url"
return 1
fi
dir="$SIBLINGS/$name"
mkdir -p "$SIBLINGS"
if [ -d "$dir/.git" ]; then
local existing
existing="$(git -C "$dir" remote get-url origin 2>/dev/null)"
if [ "$existing" != "$url" ]; then
echo "A different sibling named '$name' is already installed from: $existing"
return 1
fi
echo "$name is already installed; pulling the latest version..."
git -C "$dir" pull --ff-only || return 1
else
echo "Cloning $name..."
git clone "$url" "$dir" || return 1
fi
if [ -f "$dir/install.sh" ]; then
echo "Running $name's install.sh..."
if ! (cd "$dir" && bash install.sh); then
echo "$name's install.sh failed. Fix the problem and run 'sibling add $url' again."
return 1
fi
fi
# Open the ports listed in the sibling's firewall.conf
if [ -f "$dir/firewall.conf" ]; then
echo "Applying $name's firewall.conf..."
if ! setup_firewall; then
echo "Some firewall rules could not be applied, see above. Fix the problem and run 'sibling add $url' again."
return 1
fi
fi
# Confirm the install
if [ -d "$dir/.git" ] && git -C "$dir" rev-parse --verify --quiet HEAD > /dev/null; then
echo "Installed $name at $dir ($(git -C "$dir" log -1 --format='%h %s'))."
echo "'update' will keep it up to date."
else
echo "$name does not look installed correctly at $dir."
return 1
fi
}
list_siblings() {
local dir found=0
for dir in "$SIBLINGS"/*/; do
[ -d "$dir/.git" ] || continue
found=1
printf '%-24s %s (%s)\n' "$(basename "$dir")" \
"$(git -C "$dir" remote get-url origin 2>/dev/null)" \
"$(git -C "$dir" log -1 --format='%h, %cr' 2>/dev/null)"
done
[ $found -eq 1 ] || echo "No sibling projects installed."
}
remove_sibling() {
local name="$1"
if [[ -z "$name" || "$name" == */* || "$name" == .* ]]; then
usage
return 1
fi
if [ ! -d "$SIBLINGS/$name" ]; then
echo "No sibling named '$name' is installed."
return 1
fi
read -rp "Remove $SIBLINGS/$name? [y/N] " response
case "$response" in
[yY][eE][sS]|[yY])
rm -rf "${SIBLINGS:?}/$name"
# Close the ports only this sibling's firewall.conf asked for
setup_firewall || echo "Some firewall rules could not be updated, see above. 'update' will retry."
echo "Removed $name. Anything its install.sh set up outside that folder is still there."
;;
*)
echo "Nothing removed."
;;
esac
}
case "$1" in
""|add) add_sibling "$2" ;;
list|ls) list_siblings ;;
remove|rm) remove_sibling "$2" ;;
-h|--help|help) usage ;;
*) usage; exit 1 ;;
esac