feat(install): optional add-ons via --with and interactive prompt

Wire install_<name>.sh into the main installer through a shared catalog
so fnm/pyenv/nvm (and future tools) share one CLI and prompt path.
This commit is contained in:
Jeffrey
2026-08-07 14:23:30 +08:00
parent 42c8305871
commit bff5ceddf0
3 changed files with 160 additions and 8 deletions
+137 -5
View File
@@ -5,27 +5,120 @@ set -euo pipefail
# Save the original working directory
ORIGINAL_DIR="$(pwd)"
# Optional add-ons: each name maps to install_<name>.sh in this repo.
# Append here when adding a new optional installer.
OPTIONAL_ADDONS=(fnm pyenv nvm)
# When run from a git checkout, prefer local install_*.sh; curl|bash uses EASYZSH_RAW_BASE.
SCRIPT_DIR=""
if [[ -n "${BASH_SOURCE[0]:-}" && "${BASH_SOURCE[0]}" != "bash" && -f "${BASH_SOURCE[0]}" ]]; then
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
fi
# Flags to determine if the arguments were passed
cp_hist_flag=false
noninteractive_flag=false
with_flag_set=false
selected_addons=()
# Loop through all arguments
for arg in "$@"
do
case $arg in
usage() {
cat <<'EOF'
Usage: install.sh [options]
Options:
-c, --cp-hist Copy bash history into zsh history
-n, --non-interactive Skip default-shell change and optional prompts
--with <names> Install optional add-ons (comma-separated).
Repeatable. Names match install_<name>.sh
(currently: fnm, pyenv, nvm).
-h, --help Show this help
Examples:
./install.sh
./install.sh --with fnm
./install.sh --with fnm,pyenv -n
curl -fsSL …/install.sh | bash -s -- --with fnm
EOF
}
is_known_addon() {
local name="$1"
local known
for known in "${OPTIONAL_ADDONS[@]}"; do
[[ "$known" == "$name" ]] && return 0
done
return 1
}
append_addons_csv() {
local csv="$1"
local part
local IFS=','
# shellcheck disable=SC2086
for part in $csv; do
part="${part#"${part%%[![:space:]]*}"}"
part="${part%"${part##*[![:space:]]}"}"
[[ -z "$part" ]] && continue
if ! is_known_addon "$part"; then
echo "Unknown optional add-on: $part (known: ${OPTIONAL_ADDONS[*]})" >&2
exit 2
fi
selected_addons+=("$part")
done
}
while [[ $# -gt 0 ]]; do
case $1 in
--cp-hist|-c)
cp_hist_flag=true
shift
;;
--non-interactive|-n)
noninteractive_flag=true
shift
;;
--with)
with_flag_set=true
shift
if [[ $# -eq 0 || "$1" == -* ]]; then
echo "--with requires a comma-separated list of add-on names" >&2
exit 2
fi
append_addons_csv "$1"
shift
;;
--with=*)
with_flag_set=true
append_addons_csv "${1#--with=}"
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $arg" >&2
echo "Unknown option: $1" >&2
usage >&2
exit 2
;;
esac
done
dedupe_selected_addons() {
local _deduped=()
local _name _d _seen
for _name in "${selected_addons[@]+"${selected_addons[@]}"}"; do
_seen=false
for _d in "${_deduped[@]+"${_deduped[@]}"}"; do
[[ "$_d" == "$_name" ]] && _seen=true && break
done
[[ "$_seen" == false ]] && _deduped+=("$_name")
done
selected_addons=("${_deduped[@]+"${_deduped[@]}"}")
}
dedupe_selected_addons
if [ -f ~/.gitconfig ]; then
cp ~/.gitconfig ~/.gitconfig.backup
echo "Backup of .gitconfig created."
@@ -424,6 +517,45 @@ else
echo -e "\nNot copying bash_history to zsh_history, as --cp-hist or -c is not supplied\n"
fi
# Optional add-ons (install_<name>.sh). After shell stubs so patches land correctly.
run_optional_addon() {
local name="$1"
local script="install_${name}.sh"
echo -e "\n=== Optional add-on: ${name} ===\n"
if [[ -n "$SCRIPT_DIR" && -f "$SCRIPT_DIR/$script" ]]; then
bash "$SCRIPT_DIR/$script"
else
curl -fsSL "${EASYZSH_RAW_BASE}/${script}" | bash
fi
}
prompt_optional_addons() {
# Only when interactive and user did not pass --with.
echo -e "\nOptional add-ons (each is install_<name>.sh):"
local name
for name in "${OPTIONAL_ADDONS[@]}"; do
echo " - $name"
done
echo -n "Install which? Comma-separated names, or Enter to skip: "
local reply
read -r reply || reply=""
[[ -z "${reply//[[:space:]]/}" ]] && return 0
append_addons_csv "$reply"
}
if [[ "$with_flag_set" == false && "$noninteractive_flag" == false ]]; then
prompt_optional_addons
dedupe_selected_addons
fi
if ((${#selected_addons[@]})); then
for name in "${selected_addons[@]}"; do
run_optional_addon "$name"
done
elif [[ "$with_flag_set" == true ]]; then
echo -e "\n--with given but no add-ons selected; skipping optionals\n"
fi
if [ "$noninteractive_flag" = true ]; then
echo -e "Installation complete, exit terminal and enter a new zsh session\n"
echo -e "Make sure to change zsh to default shell by running: chsh -s $(command -v zsh)"