Compare commits
5
Commits
8320546ad4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad0f8291b7
|
||
|
|
dafbfef5cd
|
||
|
|
4fa5bc7d60
|
||
|
|
a7bf7e5330
|
||
|
|
ec5c598829
|
@@ -0,0 +1,16 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
## Environment & Constraints
|
||||||
|
* **Shell**: All scripts are written in `bash` and must be executed using `bash`. Do not attempt to run or source these scripts using `fish`, `zsh`, or other shells.
|
||||||
|
* **Platforms**: Primarily targeted at **Arch Linux**. Partial compatibility exists for Debian-based systems (specifically `zsh`, `bat`, `LazyVim` install scripts).
|
||||||
|
* **UI**: Expect interactive prompts using [`gum`](https://github.com/charmbracelet/gum) for selection menus.
|
||||||
|
|
||||||
|
## Repository Structure
|
||||||
|
* **`install.sh`**: The main entrypoint.
|
||||||
|
* **`zsh.sh`, `bat.sh`, `LazyVim.sh`, etc.**: Modular installation scripts for specific tools.
|
||||||
|
* **`configs/`**: Contains configuration files applied by the install scripts (e.g., `shelly-*.toml`).
|
||||||
|
|
||||||
|
## Developer Notes
|
||||||
|
* **Package Management**: Uses `shelly-cli` (Seafoam-Labs/Shelly-ALPM) for Arch Linux.
|
||||||
|
* **Debian Prerequisite**: On Debian-based systems, install scripts may trigger `apt full-upgrade -y`.
|
||||||
|
* **Testing**: If modifying scripts, verify behavior locally by sourcing or running in `bash`, ensuring `gum` is available for menu testing.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
skip_global_compinit=1
|
||||||
+3
-2
@@ -18,6 +18,8 @@ fi
|
|||||||
# Path to your oh-my-zsh installation.
|
# Path to your oh-my-zsh installation.
|
||||||
export ZSH=~/.oh-my-zsh
|
export ZSH=~/.oh-my-zsh
|
||||||
|
|
||||||
|
source $ZSH/custom/plugins/zsh-autocomplete/zsh-autocomplete.plugin.zsh
|
||||||
|
|
||||||
# Set the name of the theme to load. If you set this to "random"
|
# Set the name of the theme to load. If you set this to "random"
|
||||||
# It'll load a random theme each time that oh-my-zsh is loaded.
|
# It'll load a random theme each time that oh-my-zsh is loaded.
|
||||||
# See https://github.com/robbyrussell/oh-my-zsh/wiki/Themes
|
# See https://github.com/robbyrussell/oh-my-zsh/wiki/Themes
|
||||||
@@ -79,11 +81,10 @@ plugins=(
|
|||||||
git
|
git
|
||||||
zsh-history-substring-search
|
zsh-history-substring-search
|
||||||
zsh-autosuggestions
|
zsh-autosuggestions
|
||||||
zsh-autocomplete
|
|
||||||
# zsh-eza
|
# zsh-eza
|
||||||
fzf-zsh-plugin
|
fzf-zsh-plugin
|
||||||
zsh-syntax-highlighting
|
zsh-syntax-highlighting
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
source $ZSH/oh-my-zsh.sh
|
source $ZSH/oh-my-zsh.sh
|
||||||
|
|||||||
+227
-137
@@ -2,31 +2,14 @@
|
|||||||
|
|
||||||
# Strict mode from the very start: every tolerated failure in this script is
|
# Strict mode from the very start: every tolerated failure in this script is
|
||||||
# an explicit guard (if ! / || record_failure), so preamble steps are checked
|
# an explicit guard (if ! / || record_failure), so preamble steps are checked
|
||||||
# too. -E (errtrace) makes the ERR trap fire inside helper functions as well.
|
# too. Anything unguarded aborts the run via errexit.
|
||||||
set -Eeuo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
skip_watermark=false
|
skip_watermark=false
|
||||||
if [ "${1:-}" = "--skip-watermark" ]; then
|
if [ "${1:-}" = "--skip-watermark" ]; then
|
||||||
skip_watermark=true
|
skip_watermark=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Detect fish shell and warn user to run with bash instead (bash-only syntax)
|
|
||||||
if [ -n "${FISH_VERSION:-}" ] || case "${SHELL:-}" in *fish*) ;; *) false ;; esac; then
|
|
||||||
if command -v gum >/dev/null; then
|
|
||||||
if ! gum confirm "$(printf 'Your default shell appears to be fish.\n\nThis script must be run with bash. It uses bash-only syntax and will fail under fish.\n\nRun it with:\n bash installbeta.sh\n\nContinue anyway?')"; then
|
|
||||||
echo "Aborted. Please run this script with bash, not fish. e.g.: bash installbeta.sh"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Warning: fish shell detected."
|
|
||||||
echo "This script must be run with bash, not fish (bash-only syntax)."
|
|
||||||
echo "Please run it with bash, e.g.: bash installbeta.sh"
|
|
||||||
if [ -t 0 ] && [ -e /dev/tty ]; then
|
|
||||||
read -r -p "Press Enter to continue anyway, or Ctrl+C to cancel..." </dev/tty || true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$skip_watermark" = false ]; then
|
if [ "$skip_watermark" = false ]; then
|
||||||
clear 2>/dev/null || true
|
clear 2>/dev/null || true
|
||||||
echo '
|
echo '
|
||||||
@@ -44,20 +27,6 @@ See the LICENSE for more detail
|
|||||||
'
|
'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Report errors with context (line + failing command), and only once strict
|
|
||||||
# mode is active so preamble failures abort instead of printing and continuing
|
|
||||||
function error_handler() {
|
|
||||||
local rc="$1" line="$2" cmd="$3"
|
|
||||||
set +x
|
|
||||||
echo ""
|
|
||||||
echo "An error occurred (exit status $rc) at line $line while running:"
|
|
||||||
echo " $cmd"
|
|
||||||
echo "Please check the output above for details."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Trap errors (after set -euo pipefail above)
|
|
||||||
trap 'error_handler "$?" "$LINENO" "$BASH_COMMAND"' ERR
|
|
||||||
|
|
||||||
# --- Failure handling & full console log ------------------------------------
|
# --- Failure handling & full console log ------------------------------------
|
||||||
# Everything from here on is teed to a log file in /tmp, and Arch-only step
|
# Everything from here on is teed to a log file in /tmp, and Arch-only step
|
||||||
# failures are recorded instead of aborting the rest of the installation.
|
# failures are recorded instead of aborting the rest of the installation.
|
||||||
@@ -83,17 +52,106 @@ gum_tty() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Run a long non-interactive step behind a spinner when gum exists, plain
|
||||||
|
# otherwise: the shelly bootstrap below must work on a system that has no gum
|
||||||
|
# yet. Routed through gum_tty so spinner escape codes stay out of $LOG.
|
||||||
|
run_step() {
|
||||||
|
local title="$1" out rc=0
|
||||||
|
shift
|
||||||
|
if ! command -v gum >/dev/null; then
|
||||||
|
"$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
# gum spin --show-error would print the step's errors on gum's stderr, which
|
||||||
|
# gum_tty sends to the terminal only. Capture the output here instead and
|
||||||
|
# replay it on failure through stdout, so the cause also lands in $LOG.
|
||||||
|
out="$(mktemp "$WORK_DIR/step-XXXXXX.out")"
|
||||||
|
gum_tty spin -s dot --title "$title" -- bash -c '"$@" >"$0" 2>&1' "$out" "$@" || rc=$?
|
||||||
|
if [ "$rc" -ne 0 ]; then
|
||||||
|
cat "$out"
|
||||||
|
fi
|
||||||
|
rm -f "$out"
|
||||||
|
return "$rc"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Errors through gum log (level + colour + timestamp) once gum exists; plain
|
||||||
|
# echo before that, since the first record_failure can precede the bootstrap.
|
||||||
|
log_error() {
|
||||||
|
if command -v gum >/dev/null; then
|
||||||
|
gum log --level error --time kitchen -- "$*"
|
||||||
|
else
|
||||||
|
echo "!! $*"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Same, for the failures that abort the run. `|| true` because gum exits 1
|
||||||
|
# on level fatal, which errexit would otherwise trip over mid-message.
|
||||||
|
log_fatal() {
|
||||||
|
if command -v gum >/dev/null; then
|
||||||
|
gum log --level fatal --time kitchen -- "$*" || true
|
||||||
|
else
|
||||||
|
echo "FATAL: $*"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Record a failed step: remember it, print it now, keep installing
|
# Record a failed step: remember it, print it now, keep installing
|
||||||
record_failure() {
|
record_failure() {
|
||||||
FAILED+=("$1 — $2")
|
FAILED+=("$1 — $2")
|
||||||
echo "!! FAILED: $1 — $2 (continuing with the rest of the installation)"
|
log_error "$1 — $2 (continuing with the rest of the installation)"
|
||||||
|
# Plain line, no colour codes: this is what gum pager shows at the end
|
||||||
echo "$(date '+%F %T') $1 — $2" >>"$REPORT"
|
echo "$(date '+%F %T') $1 — $2" >>"$REPORT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Run a command; if it fails, print that exact command as a retry hint and
|
||||||
|
# record the failure. The hint is built from the arguments, so it cannot drift
|
||||||
|
# from what actually ran. Pass --spin TITLE first to run it via run_step.
|
||||||
|
# Usage: try_step STEP REASON MESSAGE [--spin TITLE] CMD...
|
||||||
|
try_step() {
|
||||||
|
local step="$1" reason="$2" message="$3" title="" hint
|
||||||
|
shift 3
|
||||||
|
if [ "$1" = "--spin" ]; then
|
||||||
|
title="$2"
|
||||||
|
shift 2
|
||||||
|
fi
|
||||||
|
if [ -n "$title" ]; then
|
||||||
|
run_step "$title" "$@" && return 0
|
||||||
|
else
|
||||||
|
"$@" && return 0
|
||||||
|
fi
|
||||||
|
printf -v hint '%q ' "$@"
|
||||||
|
echo "--------------------------------------------------------------------"
|
||||||
|
log_error "$message You can try running it manually:"
|
||||||
|
echo "${hint% }"
|
||||||
|
echo "--------------------------------------------------------------------"
|
||||||
|
record_failure "$step" "$reason"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Installed-package count for the final summary. apt is deliberately not
|
||||||
|
# reported: on apt-only systems this prints nothing and no count line shows.
|
||||||
|
snapshot_pkg_count() {
|
||||||
|
if command -v pacman >/dev/null; then
|
||||||
|
pacman -Qq 2>/dev/null | wc -l
|
||||||
|
elif command -v rpm >/dev/null; then
|
||||||
|
rpm -qa --qf '%{NAME}\n' 2>/dev/null | wc -l
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Private temp workspace for downloads and clones. Predictable names in /tmp
|
# Private temp workspace for downloads and clones. Predictable names in /tmp
|
||||||
# are a symlink attack vector on multi-user systems; the trap cleans up too.
|
# are a symlink attack vector on multi-user systems; the trap cleans up too.
|
||||||
WORK_DIR="$(mktemp -d)"
|
WORK_DIR="$(mktemp -d)"
|
||||||
trap 'rm -rf "$WORK_DIR"' EXIT
|
|
||||||
|
# Also stops the sudo keep-alive loop below when the script exits (any path:
|
||||||
|
# normal end, menu cancel, errexit, signal). SUDO_LOOP_PID is unset if the
|
||||||
|
# script bails out before the loop starts, hence the default value.
|
||||||
|
cleanup() {
|
||||||
|
if [ -n "${SUDO_LOOP_PID:-}" ]; then
|
||||||
|
# The loop's TERM trap also stops its current `sleep` (see below)
|
||||||
|
kill "$SUDO_LOOP_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
# Resolve where this script lives so local script/config fallbacks do not
|
# Resolve where this script lives so local script/config fallbacks do not
|
||||||
# depend on the caller's current directory. Empty when piped (curl | bash).
|
# depend on the caller's current directory. Empty when piped (curl | bash).
|
||||||
@@ -103,31 +161,69 @@ else
|
|||||||
SCRIPT_DIR=""
|
SCRIPT_DIR=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install shelly as a dependency if not present (Arch-based)
|
# Ask for sudo upfront so the user is prompted only once. A cancelled prompt or
|
||||||
|
# a system without sudo aborts here with a clear message instead of an
|
||||||
|
# unexplained errexit exit later on.
|
||||||
|
if ! sudo -v; then
|
||||||
|
log_fatal "sudo is required to run this installer (password prompt cancelled, or sudo is not available)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sudo keep-alive loop
|
||||||
|
sudo_keep_alive() {
|
||||||
|
local sleep_pid=""
|
||||||
|
# On TERM (from cleanup) stop the current sleep before exiting, otherwise it
|
||||||
|
# lingers orphaned for up to 250s. Sleeping in the background and waiting
|
||||||
|
# lets the trap fire immediately, and nothing else runs after it.
|
||||||
|
trap 'kill "$sleep_pid" 2>/dev/null; exit' TERM
|
||||||
|
while true; do
|
||||||
|
sleep 250 &
|
||||||
|
sleep_pid=$!
|
||||||
|
wait "$sleep_pid"
|
||||||
|
sudo -n true 2>/dev/null || true
|
||||||
|
# $$ is the main script's PID even in this background subshell: exit as
|
||||||
|
# soon as the installer is gone, as a backstop for the EXIT trap.
|
||||||
|
kill -0 "$$" 2>/dev/null || exit
|
||||||
|
done
|
||||||
|
}
|
||||||
|
# stderr silenced: no "Terminated" report from the loop's killed `sleep` may
|
||||||
|
# land in the tee'd log.
|
||||||
|
sudo_keep_alive 2>/dev/null &
|
||||||
|
SUDO_LOOP_PID=$!
|
||||||
|
echo "Keeping sudo alive on PID $SUDO_LOOP_PID - refreshing every 250s"
|
||||||
|
|
||||||
|
# Baseline for the package count in the final summary (empty on apt-only
|
||||||
|
# systems, where no count is reported). Taken before the first install step,
|
||||||
|
# which is the shelly bootstrap right below.
|
||||||
|
PKGS_BEFORE="$(snapshot_pkg_count)"
|
||||||
|
|
||||||
|
# Install shelly as a dependency if not present (Arch-based). This runs before
|
||||||
|
# any gum use on purpose: on a fresh Arch box gum comes from pacman here, so
|
||||||
|
# the bootstrap never depends on gum already being installed (and gum itself
|
||||||
|
# cannot be installed without shelly or apt).
|
||||||
if command -v pacman >/dev/null; then
|
if command -v pacman >/dev/null; then
|
||||||
# Install build prerequisites for shelly
|
# Install build prerequisites for shelly. Plain command, not run_step: gum
|
||||||
if ! sudo pacman -S --needed --noconfirm base-devel git; then
|
# may not exist yet at this point.
|
||||||
echo "--------------------------------------------------------------------"
|
try_step "Shelly bootstrap (Arch-only)" "failed to install base-devel/git prerequisites" \
|
||||||
echo "Failed to install prerequisite packages for Shelly. You can try running it manually:"
|
"Failed to install prerequisite packages for Shelly." \
|
||||||
echo "sudo pacman -S --needed --noconfirm base-devel git"
|
sudo pacman -S --needed --noconfirm base-devel git || true
|
||||||
echo "--------------------------------------------------------------------"
|
# gum from [extra], so the shelly build below gets a spinner and the rest of
|
||||||
record_failure "Shelly bootstrap (Arch-only)" "failed to install base-devel/git prerequisites"
|
# the script has gum even if that build fails. Its own transaction and
|
||||||
|
# non-fatal: a repo set without gum, or a stale sync db pointing at a removed
|
||||||
|
# version, must not take base-devel/git down with it. No -Sy here (that
|
||||||
|
# would be a partial upgrade); the safety net below retries via shelly.
|
||||||
|
if ! command -v gum >/dev/null && ! sudo pacman -S --needed --noconfirm gum; then
|
||||||
|
echo "Could not install gum with pacman - retrying via shelly after the bootstrap."
|
||||||
fi
|
fi
|
||||||
# Check if shelly binary exists
|
# Check if shelly binary exists
|
||||||
if ! command -v shelly >/dev/null; then
|
if ! command -v shelly >/dev/null; then
|
||||||
echo "shelly is NOT installed. Running installation commands..."
|
echo "shelly is NOT installed. Running installation commands..."
|
||||||
if ! git clone https://aur.archlinux.org/shelly.git "$WORK_DIR/shelly"; then
|
if try_step "Shelly bootstrap (Arch-only)" "failed to clone the shelly AUR repository" \
|
||||||
echo "--------------------------------------------------------------------"
|
"Failed to clone shelly repository." --spin "Clone Shelly from AUR" \
|
||||||
echo "Failed to clone shelly repository. You can try running it manually:"
|
git clone https://aur.archlinux.org/shelly.git "$WORK_DIR/shelly"; then
|
||||||
echo "git clone https://aur.archlinux.org/shelly.git \"$WORK_DIR/shelly\""
|
try_step "Shelly bootstrap (Arch-only)" "failed to build shelly with makepkg" \
|
||||||
echo "--------------------------------------------------------------------"
|
"Failed to build and install shelly." --spin "Install Shelly" \
|
||||||
record_failure "Shelly bootstrap (Arch-only)" "failed to clone the shelly AUR repository"
|
makepkg -D "$WORK_DIR/shelly" -si --noconfirm || true
|
||||||
elif ! (cd "$WORK_DIR/shelly" && makepkg -si --noconfirm); then
|
|
||||||
echo "--------------------------------------------------------------------"
|
|
||||||
echo "Failed to build and install shelly. You can try running it manually:"
|
|
||||||
echo "cd \"$WORK_DIR/shelly\" && makepkg -si --noconfirm"
|
|
||||||
echo "--------------------------------------------------------------------"
|
|
||||||
record_failure "Shelly bootstrap (Arch-only)" "failed to build shelly with makepkg"
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "Shelly is already installed."
|
echo "Shelly is already installed."
|
||||||
@@ -137,7 +233,8 @@ fi
|
|||||||
# Install prerequisites
|
# Install prerequisites
|
||||||
if command -v shelly >/dev/null; then
|
if command -v shelly >/dev/null; then
|
||||||
echo "Shelly detected. Installing prerequisites"
|
echo "Shelly detected. Installing prerequisites"
|
||||||
shelly install standard git zsh curl wget libnewt rsync gum --upgrade --no-confirm ||
|
# No gum here: pacman above provides it, the safety net below is the fallback
|
||||||
|
shelly install standard base-devel git zsh curl wget libnewt rsync --upgrade --no-confirm ||
|
||||||
record_failure "Prerequisites (shelly)" "installing base packages via shelly failed"
|
record_failure "Prerequisites (shelly)" "installing base packages via shelly failed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -145,18 +242,20 @@ if command -v apt >/dev/null; then
|
|||||||
echo "apt detected. Installing prerequisites"
|
echo "apt detected. Installing prerequisites"
|
||||||
sudo apt update || record_failure "Prerequisites (apt)" "apt update failed"
|
sudo apt update || record_failure "Prerequisites (apt)" "apt update failed"
|
||||||
sudo apt full-upgrade -y || record_failure "Prerequisites (apt)" "apt full-upgrade failed"
|
sudo apt full-upgrade -y || record_failure "Prerequisites (apt)" "apt full-upgrade failed"
|
||||||
|
sudo -v
|
||||||
sudo apt install git zsh curl wget rsync gum -y ||
|
sudo apt install git zsh curl wget rsync gum -y ||
|
||||||
record_failure "Prerequisites (apt)" "apt install of base packages failed"
|
record_failure "Prerequisites (apt)" "apt install of base packages failed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure gum is installed
|
# Ensure gum is installed (safety net: pacman/apt above should already have
|
||||||
|
# provided it, but nothing below can run without it)
|
||||||
if ! command -v gum >/dev/null; then
|
if ! command -v gum >/dev/null; then
|
||||||
echo "gum is not installed. Installing it now..."
|
echo "gum is not installed. Installing it now..."
|
||||||
|
|
||||||
if command -v shelly >/dev/null; then
|
if command -v shelly >/dev/null; then
|
||||||
echo "shelly detected. Installing gum"
|
echo "shelly detected. Installing gum"
|
||||||
shelly install standard gum --no-confirm || {
|
shelly install standard gum --no-confirm || {
|
||||||
echo "Failed to install gum. Exiting."
|
log_fatal "Failed to install gum. Exiting."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
@@ -164,11 +263,28 @@ if ! command -v gum >/dev/null; then
|
|||||||
if command -v apt >/dev/null; then
|
if command -v apt >/dev/null; then
|
||||||
echo "apt detected. Installing gum"
|
echo "apt detected. Installing gum"
|
||||||
sudo apt install gum -y || {
|
sudo apt install gum -y || {
|
||||||
echo "Failed to install gum. Exiting."
|
log_fatal "Failed to install gum. Exiting."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# gum drives every interactive dialog in this script and there is no fallback
|
||||||
|
# menu, so a system that ended up without it must abort here.
|
||||||
|
if ! command -v gum >/dev/null; then
|
||||||
|
log_fatal "gum is required but could not be installed on this system (no shelly/apt found)."
|
||||||
|
echo "Install it manually: https://github.com/charmbracelet/gum" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect fish shell and warn user to run with bash instead (bash-only syntax).
|
||||||
|
# Placed after the gum bootstrap above so the dialog can always use gum confirm
|
||||||
|
# instead of falling back to a plain text prompt.
|
||||||
|
if [ -n "${FISH_VERSION:-}" ] || case "${SHELL:-}" in *fish*) ;; *) false ;; esac then
|
||||||
|
if ! gum_tty confirm "$(printf 'Your default shell appears to be fish.\n\nThis script must be run with bash. It uses bash-only syntax and will fail under fish.\n\nRun it with:\n bash installbeta.sh\n\nContinue anyway?')"; then
|
||||||
|
log_fatal "Aborted. Please run this script with bash, not fish. e.g.: bash installbeta.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Options for the gum menu
|
# Options for the gum menu
|
||||||
@@ -183,58 +299,19 @@ OPTIONS=(
|
|||||||
"Run bat setup script"
|
"Run bat setup script"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Present the menu: gum when available, plain numbered fallback otherwise
|
# Present the menu with gum (gum is guaranteed by the bootstrap above)
|
||||||
# (systems with neither shelly nor apt never get gum installed - the menu
|
if ! CHOICE=$(gum_tty choose --no-limit --height 14 \
|
||||||
# must still work there instead of failing with a misleading message)
|
--header "Tab = select, Enter = confirm:" \
|
||||||
select_options() {
|
--selected "Run fish setup script,Install Standard Packages (Shelly),Install AUR Packages (Shelly),Run bat setup script" \
|
||||||
if command -v gum >/dev/null; then
|
"${OPTIONS[@]}"); then
|
||||||
gum_tty choose --no-limit --height 14 \
|
echo "No options selected (menu cancelled). Nothing was installed."
|
||||||
--header "Tab = select, Enter = confirm:" \
|
exit 1
|
||||||
--selected "Run fish setup script,Install Standard Packages (Shelly),Install AUR Packages (Shelly),Run bat setup script" \
|
fi
|
||||||
"${OPTIONS[@]}"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "gum is not installed and could not be installed on this system." >&2
|
# gum exits 0 with empty output when nothing is selected - treat that as a
|
||||||
echo "Falling back to a plain numbered menu." >&2
|
# cancel too, otherwise the run below would silently install nothing.
|
||||||
local i=1 opt
|
if [ -z "$CHOICE" ]; then
|
||||||
for opt in "${OPTIONS[@]}"; do
|
echo "No options selected. Nothing was installed."
|
||||||
printf ' %d) %s\n' "$i" "$opt"
|
|
||||||
i=$((i + 1))
|
|
||||||
done
|
|
||||||
|
|
||||||
local reply=""
|
|
||||||
if [ -e /dev/tty ]; then
|
|
||||||
read -r -p "Enter numbers separated by spaces (Enter = cancel): " reply </dev/tty || reply=""
|
|
||||||
else
|
|
||||||
echo "No interactive terminal available, cannot show a menu." >&2
|
|
||||||
return 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ -n "$reply" ] || return 1
|
|
||||||
|
|
||||||
local -a picked=()
|
|
||||||
local n
|
|
||||||
for n in $reply; do
|
|
||||||
case "$n" in
|
|
||||||
'' | *[!0-9]*)
|
|
||||||
echo "Invalid selection: $n" >&2
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
if [ "$n" -ge 1 ] && [ "$n" -le "${#OPTIONS[@]}" ]; then
|
|
||||||
picked+=("${OPTIONS[$((n - 1))]}")
|
|
||||||
else
|
|
||||||
echo "Selection out of range: $n" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
[ "${#picked[@]}" -gt 0 ] || return 1
|
|
||||||
printf '%s\n' "${picked[@]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
if ! CHOICE="$(select_options)"; then
|
|
||||||
echo "No options selected (menu cancelled or unavailable). Nothing was installed."
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -248,6 +325,7 @@ while IFS= read -r selection; do
|
|||||||
case "$selection" in
|
case "$selection" in
|
||||||
"Run zsh setup script")
|
"Run zsh setup script")
|
||||||
echo "Running zsh setup script..."
|
echo "Running zsh setup script..."
|
||||||
|
sudo -v
|
||||||
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
||||||
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/zsh.sh" ]; then
|
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/zsh.sh" ]; then
|
||||||
echo "Found local script, running..."
|
echo "Found local script, running..."
|
||||||
@@ -264,6 +342,7 @@ while IFS= read -r selection; do
|
|||||||
;;
|
;;
|
||||||
"Run fish setup script")
|
"Run fish setup script")
|
||||||
echo "Running fish setup script..."
|
echo "Running fish setup script..."
|
||||||
|
sudo -v
|
||||||
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
||||||
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/fish.sh" ]; then
|
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/fish.sh" ]; then
|
||||||
echo "Found local script, running..."
|
echo "Found local script, running..."
|
||||||
@@ -272,6 +351,8 @@ while IFS= read -r selection; do
|
|||||||
record_failure "Run fish setup script" "fish.sh exited with status $rc"
|
record_failure "Run fish setup script" "fish.sh exited with status $rc"
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
# Top-level pipe (like the zsh branch) so set -o pipefail catches a
|
||||||
|
# failed download instead of reporting an empty script as success.
|
||||||
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/fish.sh | bash -s -- --skip-watermark || {
|
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/fish.sh | bash -s -- --skip-watermark || {
|
||||||
rc=$?
|
rc=$?
|
||||||
record_failure "Run fish setup script" "downloaded fish.sh exited with status $rc"
|
record_failure "Run fish setup script" "downloaded fish.sh exited with status $rc"
|
||||||
@@ -280,6 +361,7 @@ while IFS= read -r selection; do
|
|||||||
;;
|
;;
|
||||||
"Run LazyVim setup script")
|
"Run LazyVim setup script")
|
||||||
echo "Running LazyVim setup script..."
|
echo "Running LazyVim setup script..."
|
||||||
|
sudo -v
|
||||||
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
||||||
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/LazyVim.sh" ]; then
|
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/LazyVim.sh" ]; then
|
||||||
echo "Found local script, running..."
|
echo "Found local script, running..."
|
||||||
@@ -296,6 +378,7 @@ while IFS= read -r selection; do
|
|||||||
;;
|
;;
|
||||||
"Install Docker")
|
"Install Docker")
|
||||||
echo "Installing Docker..."
|
echo "Installing Docker..."
|
||||||
|
sudo -v
|
||||||
if ! command -v docker >/dev/null; then
|
if ! command -v docker >/dev/null; then
|
||||||
echo "docker is NOT installed. Installing..."
|
echo "docker is NOT installed. Installing..."
|
||||||
if curl -fsSL https://get.docker.com -o "$WORK_DIR/get-docker.sh" &&
|
if curl -fsSL https://get.docker.com -o "$WORK_DIR/get-docker.sh" &&
|
||||||
@@ -316,6 +399,7 @@ while IFS= read -r selection; do
|
|||||||
;;
|
;;
|
||||||
"Install Standard Packages (Shelly)")
|
"Install Standard Packages (Shelly)")
|
||||||
echo "Installing Standard Packages via Shelly backup..."
|
echo "Installing Standard Packages via Shelly backup..."
|
||||||
|
sudo -v
|
||||||
# Check if shelly binary is installed
|
# Check if shelly binary is installed
|
||||||
if ! command -v shelly >/dev/null; then
|
if ! command -v shelly >/dev/null; then
|
||||||
echo "Cannot proceed: shelly binary not found (Arch-only)"
|
echo "Cannot proceed: shelly binary not found (Arch-only)"
|
||||||
@@ -328,22 +412,17 @@ while IFS= read -r selection; do
|
|||||||
rc=$?
|
rc=$?
|
||||||
record_failure "Install Standard Packages (Shelly)" "shelly backup import exited with status $rc"
|
record_failure "Install Standard Packages (Shelly)" "shelly backup import exited with status $rc"
|
||||||
}
|
}
|
||||||
elif ! curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/shelly-standard.toml -o "$WORK_DIR/shelly-standard.toml"; then
|
elif try_step "Install Standard Packages (Shelly)" "failed to download shelly-standard.toml" \
|
||||||
echo "--------------------------------------------------------------------"
|
"Failed to download shelly backup." \
|
||||||
echo "Failed to download shelly backup. You can try running it manually:"
|
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/shelly-standard.toml -o "$WORK_DIR/shelly-standard.toml"; then
|
||||||
echo "curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/shelly-standard.toml -o \"$WORK_DIR/shelly-standard.toml\""
|
try_step "Install Standard Packages (Shelly)" "shelly backup import failed" \
|
||||||
echo "--------------------------------------------------------------------"
|
"Failed to install Standard packages." \
|
||||||
record_failure "Install Standard Packages (Shelly)" "failed to download shelly-standard.toml"
|
shelly backup --import --name shelly-standard --directory "$WORK_DIR" --no-confirm || true
|
||||||
elif ! shelly backup --import --name shelly-standard --directory "$WORK_DIR" --no-confirm; then
|
|
||||||
echo "--------------------------------------------------------------------"
|
|
||||||
echo "Failed to install Standard packages. You can try running it manually:"
|
|
||||||
echo "shelly backup --import --name shelly-standard --directory \"$WORK_DIR\" --no-confirm"
|
|
||||||
echo "--------------------------------------------------------------------"
|
|
||||||
record_failure "Install Standard Packages (Shelly)" "shelly backup import failed"
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"Install AUR Packages (Shelly)")
|
"Install AUR Packages (Shelly)")
|
||||||
echo "Installing AUR Packages via Shelly backup..."
|
echo "Installing AUR Packages via Shelly backup..."
|
||||||
|
sudo -v
|
||||||
# Check for shelly before installing AUR packages
|
# Check for shelly before installing AUR packages
|
||||||
if ! command -v shelly >/dev/null; then
|
if ! command -v shelly >/dev/null; then
|
||||||
echo "Cannot proceed: shelly binary not found (Arch-only)"
|
echo "Cannot proceed: shelly binary not found (Arch-only)"
|
||||||
@@ -356,22 +435,17 @@ while IFS= read -r selection; do
|
|||||||
rc=$?
|
rc=$?
|
||||||
record_failure "Install AUR Packages (Shelly)" "shelly backup import exited with status $rc"
|
record_failure "Install AUR Packages (Shelly)" "shelly backup import exited with status $rc"
|
||||||
}
|
}
|
||||||
elif ! curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/shelly-aur.toml -o "$WORK_DIR/shelly-aur.toml"; then
|
elif try_step "Install AUR Packages (Shelly)" "failed to download shelly-aur.toml" \
|
||||||
echo "--------------------------------------------------------------------"
|
"Failed to download shelly backup." \
|
||||||
echo "Failed to download shelly backup. You can try running it manually:"
|
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/shelly-aur.toml -o "$WORK_DIR/shelly-aur.toml"; then
|
||||||
echo "curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/shelly-aur.toml -o \"$WORK_DIR/shelly-aur.toml\""
|
try_step "Install AUR Packages (Shelly)" "shelly backup import failed" \
|
||||||
echo "--------------------------------------------------------------------"
|
"Failed to install AUR packages." \
|
||||||
record_failure "Install AUR Packages (Shelly)" "failed to download shelly-aur.toml"
|
shelly backup --import --name shelly-aur --directory "$WORK_DIR" --no-confirm || true
|
||||||
elif ! shelly backup --import --name shelly-aur --directory "$WORK_DIR" --no-confirm; then
|
|
||||||
echo "--------------------------------------------------------------------"
|
|
||||||
echo "Failed to install AUR packages. You can try running it manually:"
|
|
||||||
echo "shelly backup --import --name shelly-aur --directory \"$WORK_DIR\" --no-confirm"
|
|
||||||
echo "--------------------------------------------------------------------"
|
|
||||||
record_failure "Install AUR Packages (Shelly)" "shelly backup import failed"
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"Install affinity with GUI")
|
"Install affinity with GUI")
|
||||||
echo "Installing GUI dependencies"
|
echo "Installing GUI dependencies"
|
||||||
|
sudo -v
|
||||||
if command -v shelly >/dev/null; then
|
if command -v shelly >/dev/null; then
|
||||||
echo "shelly found"
|
echo "shelly found"
|
||||||
shelly install standard python-pyqt6 --no-confirm ||
|
shelly install standard python-pyqt6 --no-confirm ||
|
||||||
@@ -416,7 +490,7 @@ while IFS= read -r selection; do
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Invalid option selected: $selection"
|
log_error "Invalid option selected: $selection"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done < <(printf '%s\n' "${OPTIONS[@]}")
|
done < <(printf '%s\n' "${OPTIONS[@]}")
|
||||||
@@ -437,12 +511,28 @@ if [ "${#FAILED[@]}" -gt 0 ]; then
|
|||||||
} | tee -a "$REPORT"
|
} | tee -a "$REPORT"
|
||||||
|
|
||||||
if [ "$TTY_OK" = true ] && command -v gum >/dev/null; then
|
if [ "$TTY_OK" = true ] && command -v gum >/dev/null; then
|
||||||
gum_tty pager < "$REPORT" || true
|
gum_tty pager <"$REPORT" || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Non-zero so callers and CI can detect the failed run
|
# Non-zero so callers and CI can detect the failed run
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "All steps completed with no failures."
|
echo "================ INSTALL SUMMARY ================"
|
||||||
|
echo "installbeta.sh finished at $(date '+%F %T') - no failures."
|
||||||
|
if [ -n "$PKGS_BEFORE" ]; then
|
||||||
|
PKG_DELTA=$(($(snapshot_pkg_count) - PKGS_BEFORE))
|
||||||
|
if [ "$PKG_DELTA" -gt 0 ]; then
|
||||||
|
echo "Packages added: $PKG_DELTA"
|
||||||
|
elif [ "$PKG_DELTA" -lt 0 ]; then
|
||||||
|
echo "Packages removed: $((-PKG_DELTA))"
|
||||||
|
else
|
||||||
|
echo "Packages added: 0 (no net change)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo "Full console log: $LOG"
|
||||||
|
echo "Failure report: $REPORT"
|
||||||
|
echo "==================================================="
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -80,16 +80,19 @@ fi
|
|||||||
# Do not print commands
|
# Do not print commands
|
||||||
set +x
|
set +x
|
||||||
|
|
||||||
# Backup the user's current config before oh-my-zsh overwrites it.
|
# Backup the user's current configs before oh-my-zsh / our downloads overwrite them.
|
||||||
# Timestamped so re-running the script never overwrites the previous backup.
|
# Timestamped so re-running the script never overwrites the previous backup.
|
||||||
if [ -f "$HOME/.zshrc" ]; then
|
BACKUP_STAMP="$(date +%Y%m%d-%H%M%S)"
|
||||||
ZSHRC_BACKUP="$HOME/.zshrc.bak-$(date +%Y%m%d-%H%M%S)"
|
for cfg in .zshrc .zshenv; do
|
||||||
if ! cp "$HOME/.zshrc" "$ZSHRC_BACKUP"; then
|
if [ -f "$HOME/$cfg" ]; then
|
||||||
echo "error: failed to back up $HOME/.zshrc to $ZSHRC_BACKUP" >&2
|
cfg_backup="$HOME/$cfg.bak-$BACKUP_STAMP"
|
||||||
exit 1
|
if ! cp "$HOME/$cfg" "$cfg_backup"; then
|
||||||
|
echo "error: failed to back up $HOME/$cfg to $cfg_backup" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Backed up $HOME/$cfg to $cfg_backup"
|
||||||
fi
|
fi
|
||||||
echo "Backed up $HOME/.zshrc to $ZSHRC_BACKUP"
|
done
|
||||||
fi
|
|
||||||
|
|
||||||
# Install oh-my-zsh without entering zsh
|
# Install oh-my-zsh without entering zsh
|
||||||
if ! omz_installer=$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh); then
|
if ! omz_installer=$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh); then
|
||||||
@@ -121,10 +124,12 @@ clone_plugin "$ZSH_CUSTOM_DIR/plugins/zsh-autocomplete" --depth=1 https://github
|
|||||||
|
|
||||||
|
|
||||||
# Download and replace config file
|
# Download and replace config file
|
||||||
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/.zshrc -o ~/.zshrc
|
CONFIG_BASE_URL="https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs"
|
||||||
|
curl -fsSL "$CONFIG_BASE_URL/.zshrc" -o ~/.zshrc
|
||||||
|
curl -fsSL "$CONFIG_BASE_URL/.zshenv" -o ~/.zshenv
|
||||||
|
|
||||||
# Download generic fzf configuration
|
# Download generic fzf configuration
|
||||||
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/.fzf.zsh -o ~/.fzf.zsh
|
curl -fsSL "$CONFIG_BASE_URL/.fzf.zsh" -o ~/.fzf.zsh
|
||||||
|
|
||||||
# Setup fzf: the oh-my-zsh fzf-zsh-plugin provides the integration.
|
# Setup fzf: the oh-my-zsh fzf-zsh-plugin provides the integration.
|
||||||
# (Previously an empty ~/.fzf/shell/key-bindings.zsh placeholder was created
|
# (Previously an empty ~/.fzf/shell/key-bindings.zsh placeholder was created
|
||||||
|
|||||||
Reference in New Issue
Block a user