`sibling` asks for a git URL, clones the repo into ~/.local/share/siblings/<name>, runs its install.sh and confirms the install. `sibling list` and `sibling remove` manage installed siblings. updates.d/71-siblings.sh pulls every sibling during `update` and runs its update.sh, reporting failures in the update summary. Document the sibling contract in skills/sibling-project-format.md and update the custom-hook guide to match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
123 lines
3.2 KiB
Bash
Executable File
123 lines
3.2 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Purpose : Install and manage CoreDesktop 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).
|
|
|
|
SIBLINGS="$HOME/.local/share/siblings"
|
|
|
|
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
|
|
|
|
# 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"
|
|
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
|