install.sh: add --only and --skip flags for subset installs

This commit is contained in:
Joakim Persson
2026-05-05 22:50:27 +02:00
parent 6307072b21
commit dee755e291
2 changed files with 69 additions and 15 deletions
+10
View File
@@ -19,6 +19,16 @@ chmod +x install.sh
Each `.ts` file in `extensions/` is symlinked into `~/.pi/agent/extensions/`. Existing real files are backed up with a timestamp. Re-runs are idempotent. Each `.ts` file in `extensions/` is symlinked into `~/.pi/agent/extensions/`. Existing real files are backed up with a timestamp. Re-runs are idempotent.
**Install a subset:**
```bash
./install.sh --only ssh-controlmaster # just this one
./install.sh --only "ssh-controlmaster,other" # explicit list
./install.sh --skip "git-checkpoint" # all except these
```
`--only` and `--skip` accept comma-separated names without the `.ts` suffix. `--only` takes precedence if both are given.
### Alternative: pi install (local path) ### Alternative: pi install (local path)
Because `package.json` declares a `pi` manifest, you can also register this repo as a pi package: Because `package.json` declares a `pi` manifest, you can also register this repo as a pi package:
+56 -12
View File
@@ -5,9 +5,12 @@
# loads them automatically on every session. Idempotent and non-destructive. # loads them automatically on every session. Idempotent and non-destructive.
# #
# Usage: # Usage:
# ./install.sh # install (interactive confirm) # ./install.sh install all extensions
# ./install.sh --yes # install without prompt # ./install.sh --only ssh-controlmaster install one extension
# ./install.sh --uninstall # remove symlinks that point into this repo # ./install.sh --only "ext1,ext2" install a subset
# ./install.sh --skip "ext1,ext2" install all except these
# ./install.sh --yes skip confirmation prompt
# ./install.sh --uninstall remove symlinks that point into this repo
set -euo pipefail set -euo pipefail
@@ -49,22 +52,34 @@ require_pi_installed() {
# ── args ───────────────────────────────────────────── # ── args ─────────────────────────────────────────────
ACTION="install" ACTION="install"
ASSUME_YES="no" ASSUME_YES="no"
ONLY="" # comma-separated names to include (empty = all)
SKIP="" # comma-separated names to exclude (empty = none)
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
--uninstall) ACTION="uninstall"; shift ;; --uninstall) ACTION="uninstall"; shift ;;
-y|--yes) ASSUME_YES="yes"; shift ;; -y|--yes) ASSUME_YES="yes"; shift ;;
--only) ONLY="$2"; shift 2 ;;
--only=*) ONLY="${1#--only=}"; shift ;;
--skip) SKIP="$2"; shift 2 ;;
--skip=*) SKIP="${1#--skip=}"; shift ;;
-h|--help) -h|--help)
cat <<EOF cat <<EOF
install.sh — install pi-extensions install.sh — install pi-extensions
Usage: Usage:
./install.sh install (interactive confirm) ./install.sh install all extensions
./install.sh --yes install without prompt ./install.sh --only ssh-controlmaster install one extension
./install.sh --only "ext1,ext2" install a subset
./install.sh --skip "ext1,ext2" install all except these
./install.sh --yes skip confirmation prompt
./install.sh --uninstall remove symlinks that point into this repo ./install.sh --uninstall remove symlinks that point into this repo
--only and --skip accept comma-separated extension names (without .ts suffix).
--only takes precedence over --skip if both are given.
Each .ts file in extensions/ is symlinked into ~/.pi/agent/extensions/. Each .ts file in extensions/ is symlinked into ~/.pi/agent/extensions/.
Existing files that are already symlinked into this repo are left alone. Existing files already symlinked into this repo are left alone.
Existing real files or foreign symlinks are backed up with a timestamp. Existing real files or foreign symlinks are backed up with a timestamp.
EOF EOF
exit 0 ;; exit 0 ;;
@@ -72,6 +87,31 @@ EOF
esac esac
done done
# ── build install set ─────────────────────────────────
# Populates INSTALL_SET associative array: keys are bare names (no .ts suffix).
declare -A INSTALL_SET
build_install_set() {
if [[ -n "$ONLY" ]]; then
# Explicit allowlist — only install what's named
IFS=',' read -ra names <<< "$ONLY"
for n in "${names[@]}"; do
INSTALL_SET["${n%.ts}"]=1
done
else
# Start with everything present on disk, then remove --skip entries
for f in "${EXTENSIONS_SRC}"/*.ts; do
[[ -e "$f" ]] && INSTALL_SET["$(basename "$f" .ts)"]=1
done
if [[ -n "$SKIP" ]]; then
IFS=',' read -ra names <<< "$SKIP"
for n in "${names[@]}"; do
unset "INSTALL_SET[${n%.ts}]" 2>/dev/null || true
done
fi
fi
}
# ── install ────────────────────────────────────────── # ── install ──────────────────────────────────────────
do_install() { do_install() {
echo echo
@@ -80,24 +120,28 @@ do_install() {
echo echo
require_pi_installed require_pi_installed
build_install_set
local extensions=("${EXTENSIONS_SRC}"/*.ts) if [[ ${#INSTALL_SET[@]} -eq 0 ]]; then
if [[ ! -e "${extensions[0]}" ]]; then warn "No extensions selected — nothing to install."
warn "No .ts files found in ${EXTENSIONS_SRC}/ — nothing to install."
exit 0 exit 0
fi fi
echo "==> Extensions to symlink into ${EXTENSIONS_DEST}/:" echo "==> Extensions to symlink into ${EXTENSIONS_DEST}/:"
for src in "${extensions[@]}"; do for n in "${!INSTALL_SET[@]}"; do
printf ' %s\n' "$(basename "$src")" printf ' %s.ts\n' "$n"
done done
echo echo
confirm || { echo "Aborted."; exit 0; } confirm || { echo "Aborted."; exit 0; }
echo echo
for src in "${extensions[@]}"; do for src in "${EXTENSIONS_SRC}"/*.ts; do
[[ -e "$src" ]] || continue
local name local name
name="$(basename "$src")" name="$(basename "$src")"
local bare="${name%.ts}"
[[ -n "${INSTALL_SET[$bare]:-}" ]] || continue
local dest="${EXTENSIONS_DEST}/${name}" local dest="${EXTENSIONS_DEST}/${name}"
note "Linking ${name}" note "Linking ${name}"