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:
@@ -10,10 +10,21 @@ fzf-tab, k), plus eza, zoxide, fzf, and Nerd Fonts.
|
||||
curl -fsSL https://git.miomio.moe/mio/easyzsh/raw/branch/master/install.sh | bash
|
||||
```
|
||||
|
||||
Options: `--cp-hist` / `-c` imports bash history; `--non-interactive` / `-n`
|
||||
skips the default-shell change.
|
||||
Options:
|
||||
|
||||
From a git checkout:
|
||||
- `--cp-hist` / `-c` — import bash history
|
||||
- `--non-interactive` / `-n` — skip default-shell change and optional prompts
|
||||
- `--with <names>` — install optional add-ons (comma-separated, repeatable).
|
||||
Each name runs `install_<name>.sh` (currently: `fnm`, `pyenv`, `nvm`).
|
||||
Interactive installs (without `-n` and without `--with`) prompt for the same list.
|
||||
|
||||
```bash
|
||||
./install.sh --with fnm
|
||||
./install.sh --with fnm,pyenv -n
|
||||
curl -fsSL …/install.sh | bash -s -- --with fnm
|
||||
```
|
||||
|
||||
From a git checkout (shell stubs only):
|
||||
|
||||
```bash
|
||||
./deploy.sh
|
||||
|
||||
+137
-5
@@ -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)"
|
||||
|
||||
@@ -99,6 +99,15 @@ grep -Fq '"$ZSH_CONFIGS_DIR"/*(DN)' "$ROOT/.zshrc" || fail "existing extensionle
|
||||
grep -Fq '_resolve_trash_cmd' "$ROOT/.zshrc" || fail "trash-backed rm helper is missing"
|
||||
grep -Fq 'libglib2.0-bin' "$ROOT/install.sh" || fail "Ubuntu Trash support is not installed"
|
||||
grep -Fq 'deploy.sh' "$ROOT/install.sh" || fail "install.sh does not invoke deploy.sh"
|
||||
grep -Fq 'OPTIONAL_ADDONS=' "$ROOT/install.sh" || fail "install.sh missing OPTIONAL_ADDONS catalog"
|
||||
grep -Fq -- '--with' "$ROOT/install.sh" || fail "install.sh missing --with option"
|
||||
# --with parses known add-ons and rejects unknown ones without network.
|
||||
if HOME="$TEMP_DIR" PATH="/usr/bin:/bin" bash "$ROOT/install.sh" --with nosuch >/dev/null 2>&1; then
|
||||
fail "install.sh accepted an unknown --with add-on"
|
||||
fi
|
||||
if ! HOME="$TEMP_DIR" PATH="/usr/bin:/bin" bash "$ROOT/install.sh" --help >/dev/null; then
|
||||
fail "install.sh --help failed"
|
||||
fi
|
||||
grep -Fq 'brew shellenv' "$ROOT/.zshenv" || fail "Homebrew is not initialized in .zshenv"
|
||||
grep -Fq '/usr/local/bin/brew' "$ROOT/.zshenv" || fail "Intel Homebrew path is missing from .zshenv"
|
||||
grep -Fq '$HOME/.local/bin' "$ROOT/.zshenv" || fail "~/.local/bin is not added in .zshenv"
|
||||
|
||||
Reference in New Issue
Block a user