Files
coredesktop/sibling
T
davidandClaude Opus 5.5 76fceee115 Add sibling command for installing and updating sibling projects
`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>
2026-10-19 16:23:58 -06:00

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