Compare commits
19
Commits
1d89333407
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad0f8291b7
|
||
|
|
dafbfef5cd
|
||
|
|
4fa5bc7d60
|
||
|
|
a7bf7e5330
|
||
|
|
ec5c598829
|
||
|
|
8320546ad4
|
||
|
|
f8bb57f666
|
||
|
|
bca2dd45ba
|
||
|
|
a012aeab9c
|
||
|
|
23915c301a
|
||
|
|
0d605940b4
|
||
|
|
696badb67a
|
||
|
|
99d94ab615
|
||
|
|
49e00110bf
|
||
|
|
e215b85d90
|
||
|
|
a38a007ce3
|
||
|
|
9e190e3e75
|
||
|
|
a9b7e698b9
|
||
|
|
410537e246
|
@@ -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.
|
||||||
+131
-94
@@ -1,94 +1,85 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# Detect fish shell and ask user to switch to bash (bash-only syntax)
|
# Detect fish shell and ask user to switch to bash (bash-only syntax)
|
||||||
if [ -n "${FISH_VERSION:-}" ] || case "$SHELL" in *fish*) ;; *) false ;; esac; then
|
if [ -n "${FISH_VERSION:-}" ] || case "${SHELL:-}" in *fish*) ;; *) false ;; esac; then
|
||||||
echo "Warning: fish shell detected."
|
echo "Warning: fish shell detected."
|
||||||
echo "This script is written for bash and may have syntax issues under fish."
|
echo "This script is written for bash and may have syntax issues under fish."
|
||||||
echo "Please switch to bash first and re-run it, e.g.: bash $0"
|
echo "Please switch to bash first and re-run it, e.g.: bash $0"
|
||||||
read -r -p "Press Enter to continue anyway, or Ctrl+C to cancel..." </dev/tty || true
|
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
|
||||||
|
|
||||||
# Install prerequisetes
|
# Install prerequisites
|
||||||
if command -v pacman >/dev/null; then
|
if command -v pacman >/dev/null; then
|
||||||
echo "pacman detected. Installing prerequisites"
|
echo "pacman detected. Installing prerequisites"
|
||||||
# sclip is not packaged for arch, tree-sitter-cli ships the tree-sitter binary
|
# sclip is not packaged for arch, tree-sitter-cli ships the tree-sitter binary
|
||||||
sudo pacman -S --needed --noconfirm git curl wget python3 python-pip neovim jdk-openjdk base-devel ripgrep fd lazygit tectonic tree-sitter-cli julia luarocks shfmt ast-grep nvm
|
if ! sudo pacman -S --needed --noconfirm git curl wget python3 python-pip neovim jdk-openjdk base-devel ripgrep fd lazygit tectonic tree-sitter-cli julia luarocks shfmt ast-grep nvm nodejs; then
|
||||||
|
echo "--------------------------------------------------------------------"
|
||||||
|
echo "Failed to install prerequisites. You can try running it manually:"
|
||||||
|
echo "sudo pacman -S --needed --noconfirm git curl wget python3 python-pip neovim jdk-openjdk base-devel ripgrep fd lazygit tectonic tree-sitter-cli julia luarocks shfmt ast-grep nvm nodejs"
|
||||||
|
echo "--------------------------------------------------------------------"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
elif command -v apt >/dev/null; then
|
elif command -v apt >/dev/null; then
|
||||||
echo "apt detected. Installing prerequisites"
|
echo "apt detected. Installing prerequisites"
|
||||||
# tectonic and sclip have no debian package, ast-grep comes from npm, node comes from nodejs
|
# tectonic and sclip have no debian package, ast-grep comes from npm, node comes from nodejs
|
||||||
sudo apt install git curl wget python3 python3-pip python3-pynvim neovim default-jdk build-essential ripgrep fd-find lazygit tree-sitter-cli luarocks shfmt nodejs npm -y
|
if ! sudo apt update || ! sudo apt install git curl wget python3 python3-pip python3-pynvim neovim default-jdk build-essential ripgrep fd-find luarocks shfmt nodejs npm -y; then
|
||||||
|
echo "--------------------------------------------------------------------"
|
||||||
|
echo "Failed to install prerequisites. You can try running it manually:"
|
||||||
|
echo "sudo apt install git curl wget python3 python3-pip python3-pynvim neovim default-jdk build-essential ripgrep fd-find luarocks shfmt nodejs npm -y"
|
||||||
|
echo "--------------------------------------------------------------------"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# julia has no package for every debian architecture (e.g. arm64)
|
# lazygit and tree-sitter-cli only exist on the newest releases, julia is missing on some architectures (e.g. arm64)
|
||||||
sudo apt install julia -y || echo "julia could not be installed, skipping"
|
for optional_pkg in lazygit tree-sitter-cli julia; do
|
||||||
|
sudo apt install "$optional_pkg" -y || echo "$optional_pkg could not be installed, skipping"
|
||||||
|
done
|
||||||
|
|
||||||
# Debian ships the fd binary as fdfind
|
# Debian ships the fd binary as fdfind
|
||||||
if ! command -v fd >/dev/null && command -v fdfind >/dev/null; then
|
if ! command -v fd >/dev/null && command -v fdfind >/dev/null; then
|
||||||
sudo ln -sf "$(command -v fdfind)" /usr/local/bin/fd
|
sudo ln -sf "$(command -v fdfind)" /usr/local/bin/fd
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v sg >/dev/null; then
|
if ! command -v ast-grep >/dev/null; then
|
||||||
sudo npm install -g @ast-grep/cli
|
sudo npm install -g @ast-grep/cli || echo "ast-grep could not be installed, skipping"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Skipped on debian (not packaged): tectonic, sclip"
|
echo "Skipped on debian (not packaged): tectonic, sclip"
|
||||||
else
|
else
|
||||||
echo "Cannot proceed: pacman or apt is required"
|
echo "Cannot proceed: pacman or apt is required"
|
||||||
echo "This script supports arch based and debian based systems"
|
echo "This script supports arch based and debian based systems"
|
||||||
echo "+ sleep 10" && sleep 10
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo 'This script will move your current nvim config to ~/.config/nvim.bak'
|
echo 'This script will back up your current nvim config with a timestamped name, e.g. ~/.config/nvim.bak-20261008-130000'
|
||||||
echo "Press CTRL+C within 10 secconds if you don't want these actions to be made"
|
if [ -t 0 ]; then
|
||||||
sleep 10
|
echo "Press CTRL+C within 10 seconds if you don't want these actions to be made"
|
||||||
|
sleep 10
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Remove old backups
|
# Back up current nvim directories with a timestamp so a re-run never
|
||||||
# Remove existing .bak directories first to ensure a clean slate
|
# destroys the user's original backup (old behaviour deleted *.bak first)
|
||||||
echo " Checking for and removing old Neovim backup directories..."
|
STAMP="$(date +%Y%m%d-%H%M%S)"
|
||||||
|
|
||||||
# Use $HOME for reliable path expansion
|
backup_if_exists() {
|
||||||
if [ -e "$HOME/.config/nvim.bak" ]; then
|
local src="$1"
|
||||||
echo "Path $HOME/.config/nvim.bak exists. Removing..."
|
if [ -e "$src" ]; then
|
||||||
rm -rf "$HOME/.config/nvim.bak"
|
echo " Backing up $src -> ${src}.bak-$STAMP"
|
||||||
fi
|
mv "$src" "${src}.bak-$STAMP"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
if [ -e "$HOME/.local/share/nvim.bak" ]; then
|
|
||||||
echo "Path $HOME/.local/share/nvim.bak exists. Removing..."
|
|
||||||
rm -rf "$HOME/.local/share/nvim.bak"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -e "$HOME/.local/state/nvim.bak" ]; then
|
|
||||||
echo "Path $HOME/.local/state/nvim.bak exists. Removing..."
|
|
||||||
rm -rf "$HOME/.local/state/nvim.bak"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -e "$HOME/.cache/nvim.bak" ]; then
|
|
||||||
echo "Path $HOME/.cache/nvim.bak exists. Removing..."
|
|
||||||
rm -rf "$HOME/.cache/nvim.bak"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Remove the current nvim config and create a new backup
|
|
||||||
echo ""
|
|
||||||
echo " Backing up current Neovim configuration..."
|
echo " Backing up current Neovim configuration..."
|
||||||
if [ -d "$HOME/.config/nvim" ]; then # Check if nvim config exists before moving
|
backup_if_exists "$HOME/.config/nvim"
|
||||||
mv "$HOME/.config/nvim" "$HOME/.config/nvim.bak"
|
backup_if_exists "$HOME/.local/share/nvim"
|
||||||
else
|
backup_if_exists "$HOME/.local/state/nvim"
|
||||||
echo "No existing ~/.config/nvim found to back up."
|
backup_if_exists "$HOME/.cache/nvim"
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo " Backing up Neovim share, state, and cache directories..."
|
|
||||||
if [ -d "$HOME/.local/share/nvim" ]; then
|
|
||||||
mv "$HOME/.local/share/nvim" "$HOME/.local/share/nvim.bak"
|
|
||||||
fi
|
|
||||||
if [ -d "$HOME/.local/state/nvim" ]; then
|
|
||||||
mv "$HOME/.local/state/nvim" "$HOME/.local/state/nvim.bak"
|
|
||||||
fi
|
|
||||||
if [ -d "$HOME/.cache/nvim" ]; then
|
|
||||||
mv "$HOME/.cache/nvim" "$HOME/.cache/nvim.bak"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Remove any leftover
|
# Remove any leftover
|
||||||
if [ -e "$HOME/.config/.nvim" ]; then
|
if [ -e "$HOME/.config/.nvim" ]; then
|
||||||
@@ -99,49 +90,95 @@ fi
|
|||||||
|
|
||||||
# Install LazyVim
|
# Install LazyVim
|
||||||
echo ""
|
echo ""
|
||||||
git clone https://github.com/alvinlollo/LazyVim ~/.config/nvim
|
if ! git clone https://github.com/alvinlollo/LazyVim "$HOME/.config/nvim"; then
|
||||||
|
echo "error: failed to clone https://github.com/alvinlollo/LazyVim into ~/.config/nvim" >&2
|
||||||
# Check if nvm is in shell path
|
echo "Your previous config is preserved in ~/.config/nvim.bak-$STAMP (if one existed)." >&2
|
||||||
if command -v nvm &>/dev/null; then
|
exit 1
|
||||||
# Get current shell name
|
|
||||||
CURRENT_SHELL=$(basename -- "$SHELL")
|
|
||||||
|
|
||||||
# 2. Source the correct configuration file safely
|
|
||||||
if [ "$CURRENT_SHELL" = "fish" ] && [ -f ~/.bashrc ]; then
|
|
||||||
|
|
||||||
# Check if fish is installed first, then check for fisher
|
|
||||||
if command -v fish &>/dev/null && fish -c "functions -q fisher" &>/dev/null; then
|
|
||||||
fisher install jorgebucaran/nvm.fish
|
|
||||||
source ~/.config/fish/config.fish
|
|
||||||
else
|
|
||||||
curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source
|
|
||||||
fisher install jorgebucaran/fisher
|
|
||||||
fisher install jorgebucaran/nvm.fish
|
|
||||||
source ~/.config/fish/config.fish
|
|
||||||
fi
|
|
||||||
|
|
||||||
elif [ "$CURRENT_SHELL" = "zsh" ] && [ -f ~/.zshrc ]; then
|
|
||||||
|
|
||||||
echo -e "export NVM_DIR=\"$HOME/.nvm\"\n[ -s \"/usr/share/nvm/nvm.sh\" ] && \. \"/usr/share/nvm/nvm.sh\"\n[ -s \"/usr/share/nvm/init-nvm.sh\" ] && \. \"/usr/share/nvm/init-nvm.sh\"" >>~/.zshrc
|
|
||||||
source ~/.zshrc
|
|
||||||
|
|
||||||
elif [ "$CURRENT_SHELL" = "bash" ] && [ -f ~/.config/fish/config.fish ]; then
|
|
||||||
|
|
||||||
echo "source /usr/share/nvm/init-nvm.sh" >>~/.bashrc
|
|
||||||
source ~/.bashrc
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "Unsupported or unreadable shell configuration: $CURRENT_SHELL"
|
|
||||||
echo "Failed to add NVM to shell path. Please add it manually to your shell configuration file."
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use NVM to install the latest LTS version of Node.js
|
# Load nvm into this shell if it exists. nvm is a shell function, so
|
||||||
if command -v nvm &>/dev/null; then
|
# 'command -v nvm' only works after its init script has been sourced.
|
||||||
|
NVM_LOADED=false
|
||||||
|
for nvm_init in "$HOME/.nvm/nvm.sh" /usr/share/nvm/nvm.sh /usr/share/nvm/init-nvm.sh; do
|
||||||
|
if [ -f "$nvm_init" ]; then
|
||||||
|
set +u # nvm's init script is not always nounset-safe
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
if ! . "$nvm_init"; then
|
||||||
|
set -u
|
||||||
|
continue # try the next candidate
|
||||||
|
fi
|
||||||
|
set -u
|
||||||
|
NVM_LOADED=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
CURRENT_SHELL=$(basename -- "${SHELL:-/bin/bash}")
|
||||||
|
|
||||||
|
case "$CURRENT_SHELL" in
|
||||||
|
fish)
|
||||||
|
# fish cannot source nvm.sh - it uses the nvm.fish plugin instead
|
||||||
|
if [ -f "$HOME/.config/fish/config.fish" ]; then
|
||||||
|
if ! fish -c 'type -q fisher' 2>/dev/null; then
|
||||||
|
fisher_src="$(mktemp)"
|
||||||
|
if ! curl -fsSL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish -o "$fisher_src"; then
|
||||||
|
echo "error: failed to download fisher.fish" >&2
|
||||||
|
rm -f "$fisher_src"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fish -c "source '$fisher_src' && fisher install jorgebucaran/fisher"
|
||||||
|
rm -f "$fisher_src"
|
||||||
|
fi
|
||||||
|
# fisher refuses to install over existing files - skip when already present
|
||||||
|
if fish -c 'type -q nvm' 2>/dev/null; then
|
||||||
|
echo "nvm.fish already available in fish, skipping install."
|
||||||
|
else
|
||||||
|
fish -c 'fisher install jorgebucaran/nvm.fish'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No ~/.config/fish/config.fish found, skipping fish plugin setup"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
zsh)
|
||||||
|
if [ -f "$HOME/.zshrc" ]; then
|
||||||
|
if ! grep -q 'nvm.sh' "$HOME/.zshrc"; then
|
||||||
|
printf '%s\n' \
|
||||||
|
'export NVM_DIR="$HOME/.nvm"' \
|
||||||
|
'[ -s "/usr/share/nvm/nvm.sh" ] && . "/usr/share/nvm/nvm.sh"' \
|
||||||
|
'[ -s "/usr/share/nvm/init-nvm.sh" ] && . "/usr/share/nvm/init-nvm.sh"' >>"$HOME/.zshrc"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No ~/.zshrc found, skipping nvm shell wiring"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
bash)
|
||||||
|
if [ -f "$HOME/.bashrc" ]; then
|
||||||
|
if ! grep -q 'nvm.sh\|init-nvm.sh' "$HOME/.bashrc"; then
|
||||||
|
printf '%s\n' \
|
||||||
|
'export NVM_DIR="$HOME/.nvm"' \
|
||||||
|
'[ -s "/usr/share/nvm/nvm.sh" ] && . "/usr/share/nvm/nvm.sh"' \
|
||||||
|
'[ -s "/usr/share/nvm/init-nvm.sh" ] && . "/usr/share/nvm/init-nvm.sh"' >>"$HOME/.bashrc"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No ~/.bashrc found, skipping nvm shell wiring"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported or unreadable shell configuration: $CURRENT_SHELL"
|
||||||
|
echo "Failed to add NVM to shell path. Please add it manually to your shell configuration file."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Use nvm (when loaded) to install the latest LTS version of Node.js
|
||||||
|
if [ "$NVM_LOADED" = true ]; then
|
||||||
nvm install lts
|
nvm install lts
|
||||||
nvm use lts
|
nvm use lts
|
||||||
elif command -v node >/dev/null; then
|
elif command -v node >/dev/null; then
|
||||||
echo "nvm not found, using system Node.js $(node --version)"
|
echo "nvm not available, using system Node.js $(node --version)"
|
||||||
else
|
else
|
||||||
echo "No Node.js installation found. Install it manually, then re-run this script."
|
echo "No Node.js installation found."
|
||||||
|
echo "Install nvm (Arch: pacman -S nvm) or nodejs, then re-run this script."
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "LazyVim setup complete."
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Single-install-script
|
# Single-install-script
|
||||||
|
|
||||||
This script automatically installs my favourite Linux applications I use every day such as:
|
This script automatically installs my favourite Linux applications I use every day such as:
|
||||||
[Docker](https://www.docker.com/), [zsh, oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh) and [neovim](https://neovim.io/). This uses the [shelly-cli](https://github.com/Seafoam-Labs/Shelly-ALPM) package manager. You can choose what sections to install with the whiptail menu.
|
[Docker](https://www.docker.com/), [zsh, oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh) and [neovim](https://neovim.io/). This uses the [shelly-cli](https://github.com/Seafoam-Labs/Shelly-ALPM) package manager. You can choose what sections to install with the [gum](https://github.com/charmbracelet/gum) menu.
|
||||||
|
|
||||||
This script will automatically install [shelly-cli](https://github.com/Seafoam-Labs/Shelly-ALPM) if it is not already present.
|
This script will automatically install [shelly-cli](https://github.com/Seafoam-Labs/Shelly-ALPM) if it is not already present.
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ Please consider staring this project. It helps me see how important this is for
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
> **Warning:** Run this script with **bash**, not fish (or another shell). It uses bash-only syntax and will fail if run under fish. Keep `bash` in the commands below — do not replace it with `fish` or `source` the script. If fish is your default shell, a warning (dialog if whiptail is installed, otherwise a text prompt) will remind you before anything is installed.
|
> **Warning:** Run this script with **bash**, not fish (or another shell). It uses bash-only syntax and will fail if run under fish. Keep `bash` in the commands below — do not replace it with `fish` or `source` the script. If fish is your default shell, a warning (a `gum confirm` dialog if gum is installed, otherwise a text prompt) will remind you before anything is installed.
|
||||||
|
|
||||||
To use this script paste the following in the Terminal:
|
To use this script paste the following in the Terminal:
|
||||||
|
|
||||||
@@ -29,6 +29,35 @@ For installing zsh and themes (also works on Debian based systems):
|
|||||||
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/zsh.sh | bash
|
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/zsh.sh | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Note:** on Debian-based systems the installer runs `apt full-upgrade -y` as part of its prerequisites. On Arch it installs prerequisites through shelly without upgrading the whole system.
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
|
||||||
|
| Script | Purpose |
|
||||||
|
| -------------- | ----------------------------------------------------------------------- |
|
||||||
|
| `install.sh` | Main menu (gum) that dispatches to the scripts below. |
|
||||||
|
| `installbeta.sh` | Same menu, but logs every step and records failures instead of aborting, then exits non-zero if anything failed. This is the next `install.sh` — once tested it replaces it. |
|
||||||
|
| `zsh.sh` | zsh + oh-my-zsh + plugins and my config (also works on Debian). |
|
||||||
|
| `fish.sh` | fish + Fisher plugins + end-4 dotfiles config. |
|
||||||
|
| `LazyVim.sh` | My LazyVim neovim config. |
|
||||||
|
| `bat.sh` | bat + Catppuccin Mocha theme + MANPAGER setup. |
|
||||||
|
| `SecureBoot.sh`| **Not in the menu** (intentional): manual UEFI-only Secure Boot setup with `sbctl`. It verifies every sign target and asks before enrolling keys — read it before running. |
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
Enable the pre-push gate once per clone (runs `bash -n` and `shellcheck` on every tracked `*.sh`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git config core.hooksPath hooks
|
||||||
|
```
|
||||||
|
|
||||||
|
There is also a manual end-to-end smoke test that runs the installers in a throwaway Arch container (needs docker or podman, network access; **not** part of the hook):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tests/smoke.sh # bat.sh, zsh.sh, fish.sh
|
||||||
|
tests/smoke.sh LazyVim.sh # opt in to the heavy LazyVim run
|
||||||
|
```
|
||||||
|
|
||||||
## Zsh configuration
|
## Zsh configuration
|
||||||
|
|
||||||
My Zsh configuration includes the following plugins run through [oh-my-zsh](https://ohmyz.sh/): git, [zsh-history-substring-search](https://github.com/zsh-users/zsh-history-substring-search), [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions), [zsh-autocomplete](https://github.com/marlonrichert/zsh-autocomplete), [fzf-zsh-plugin](https://github.com/unixorn/fzf-zsh-plugin) and [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting.git)
|
My Zsh configuration includes the following plugins run through [oh-my-zsh](https://ohmyz.sh/): git, [zsh-history-substring-search](https://github.com/zsh-users/zsh-history-substring-search), [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions), [zsh-autocomplete](https://github.com/marlonrichert/zsh-autocomplete), [fzf-zsh-plugin](https://github.com/unixorn/fzf-zsh-plugin) and [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting.git)
|
||||||
|
|||||||
+99
-13
@@ -1,25 +1,111 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# Detect fish shell and ask user to switch to bash (bash-only syntax)
|
# Detect fish shell and ask user to switch to bash (bash-only syntax)
|
||||||
if [ -n "${FISH_VERSION:-}" ] || case "$SHELL" in *fish*) ;; *) false ;; esac; then
|
if [ -n "${FISH_VERSION:-}" ] || case "${SHELL:-}" in *fish*) ;; *) false ;; esac; then
|
||||||
echo "Warning: fish shell detected."
|
echo "Warning: fish shell detected."
|
||||||
echo "This script is written for bash and may have syntax issues under fish."
|
echo "This script is written for bash and may have syntax issues under fish."
|
||||||
echo "Please switch to bash first and re-run it, e.g.: bash $0"
|
echo "Please switch to bash first and re-run it, e.g.: bash $0"
|
||||||
read -r -p "Press Enter to continue anyway, or Ctrl+C to cancel..." </dev/tty || true
|
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
|
||||||
|
|
||||||
# Install Prerequisites
|
# Secure Boot setup only applies to UEFI systems (meaningless on BIOS/CSM)
|
||||||
sudo pacman -S --needed -noconfirm efibootmgr sbsigntools mokutil sbctl grub
|
if [ ! -d /sys/firmware/efi ]; then
|
||||||
|
echo "error: this system is not booted in UEFI mode (/sys/firmware/efi is missing)." >&2
|
||||||
|
echo "Secure Boot setup does not apply to BIOS/CSM machines. Aborting." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v pacman >/dev/null; then
|
||||||
|
echo "error: pacman not found. This script only supports Arch-based systems." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install prerequisites
|
||||||
|
sudo pacman -S --needed --noconfirm efibootmgr sbsigntools mokutil sbctl grub
|
||||||
|
|
||||||
|
# Fail early if the toolchain is still missing (do not touch keys otherwise)
|
||||||
|
if ! command -v sbctl >/dev/null; then
|
||||||
|
echo "error: sbctl is not available after installation. Install it manually and re-run." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Candidate sign targets. GRUB's path and the kernel image name vary by
|
||||||
|
# machine, so every path is checked before signing instead of trusted blindly.
|
||||||
|
SIGN_TARGETS=(
|
||||||
|
/boot/EFI/BOOT/BOOTX64.EFI
|
||||||
|
/boot/grub/x86_64-efi/core.efi
|
||||||
|
/boot/grub/x86_64-efi/grub.efi
|
||||||
|
/boot/vmlinuz-linux
|
||||||
|
)
|
||||||
|
|
||||||
|
missing=0
|
||||||
|
for target in "${SIGN_TARGETS[@]}"; do
|
||||||
|
if [ ! -e "$target" ]; then
|
||||||
|
echo "warning: $target does not exist on this machine, skipping" >&2
|
||||||
|
missing=$((missing + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$missing" -eq "${#SIGN_TARGETS[@]}" ]; then
|
||||||
|
echo "error: none of the expected boot files exist; refusing to continue." >&2
|
||||||
|
echo "Adjust SIGN_TARGETS for this machine and re-run." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
sudo sbctl create-keys
|
sudo sbctl create-keys
|
||||||
sudo sbctl enroll-keys --microsoft
|
|
||||||
sudo sbctl verify
|
for target in "${SIGN_TARGETS[@]}"; do
|
||||||
# use sudo sbctl sign -s for every unsigned item
|
if [ -e "$target" ]; then
|
||||||
sudo sbctl sign -s /boot/EFI/BOOT/BOOTX64.EFI
|
sudo sbctl sign -s "$target"
|
||||||
sudo sbctl sign -s /boot/grub/x84_64-efi/core.efi
|
fi
|
||||||
sudo sbctl sign -s /boot/grub/x86_64-efi/grub.efi
|
done
|
||||||
sudo sbctl sign -s /boot/vmlinuz-linux
|
|
||||||
|
# Every file must verify BEFORE enrollment - enrollment is hard to undo
|
||||||
|
echo "Verifying signatures before enrollment..."
|
||||||
|
if ! sudo sbctl verify; then
|
||||||
|
echo "error: sbctl verify reports unsigned or badly signed files." >&2
|
||||||
|
echo "Fix these first (sudo sbctl sign -s <file>). Not enrolling." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "All boot files are signed and verified."
|
||||||
|
echo ""
|
||||||
|
echo "Before enrolling keys, be ready for what can go wrong:"
|
||||||
|
echo " - After enrollment, any file not signed with these keys will not boot."
|
||||||
|
echo " - To recover: enter BIOS and disable Secure Boot (or boot a USB stick),"
|
||||||
|
echo " then run 'sudo sbctl unenroll' or re-sign the missing files."
|
||||||
|
echo " - Keep a bootable USB / BIOS access available for the first reboot."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Only enroll when a human explicitly says so, and only when we have a terminal
|
||||||
|
enroll=""
|
||||||
|
if [ -t 0 ] && [ -e /dev/tty ]; then
|
||||||
|
read -r -p "Enroll Secure Boot keys now? [y/N] " enroll </dev/tty || enroll=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$enroll" in
|
||||||
|
y | Y | yes | YES)
|
||||||
|
sudo sbctl enroll-keys --microsoft
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Enrollment skipped. Nothing has been enrolled."
|
||||||
|
echo "When you are ready, run: sudo sbctl enroll-keys --microsoft"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
sudo sbctl status
|
sudo sbctl status
|
||||||
sudo sbctl verify
|
|
||||||
echo "Use sudo sbctl sign -s for every unsigned item"
|
if sudo sbctl verify; then
|
||||||
|
echo ""
|
||||||
|
echo "Success: every file is signed and verified."
|
||||||
|
echo "Reboot and confirm the machine boots with Secure Boot enabled."
|
||||||
|
else
|
||||||
|
echo "error: verification failed after signing. Do not reboot yet." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# Detect fish shell and ask user to switch to bash (bash-only syntax)
|
# Detect fish shell and ask user to switch to bash (bash-only syntax)
|
||||||
if [ -n "${FISH_VERSION:-}" ] || case "$SHELL" in *fish*) ;; *) false ;; esac; then
|
if [ -n "${FISH_VERSION:-}" ] || case "${SHELL:-}" in *fish*) ;; *) false ;; esac; then
|
||||||
echo "Warning: fish shell detected."
|
echo "Warning: fish shell detected."
|
||||||
echo "This script is written for bash and may have syntax issues under fish."
|
echo "This script is written for bash and may have syntax issues under fish."
|
||||||
echo "Please switch to bash first and re-run it, e.g.: bash $0"
|
echo "Please switch to bash first and re-run it, e.g.: bash $0"
|
||||||
read -r -p "Press Enter to continue anyway, or Ctrl+C to cancel..." </dev/tty || true
|
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
|
||||||
|
|
||||||
skip_watermark=false
|
skip_watermark=false
|
||||||
if [ "$1" = "--skip-watermark" ]; then
|
if [ "${1:-}" = "--skip-watermark" ]; then
|
||||||
skip_watermark=true
|
skip_watermark=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -23,8 +27,8 @@ if [ "$skip_watermark" = false ]; then
|
|||||||
|___/
|
|___/
|
||||||
|
|
||||||
--------------- bat Install Script ---------------
|
--------------- bat Install Script ---------------
|
||||||
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE UNDER THE GPL-2.0 LICENCE, THERE IS NO WARRANTY
|
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE UNDER THE GPL-2.0 LICENSE, THERE IS NO WARRANTY
|
||||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. See the LICENCE for more detail
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. See the LICENSE for more detail
|
||||||
'
|
'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -32,10 +36,16 @@ fi
|
|||||||
if ! command -v git >/dev/null || ! command -v curl >/dev/null || ! command -v wget >/dev/null; then
|
if ! command -v git >/dev/null || ! command -v curl >/dev/null || ! command -v wget >/dev/null; then
|
||||||
if command -v pacman >/dev/null; then
|
if command -v pacman >/dev/null; then
|
||||||
echo "pacman detected. Installing prerequisites"
|
echo "pacman detected. Installing prerequisites"
|
||||||
sudo pacman -S --needed --noconfirm git curl wget
|
if ! sudo pacman -S --needed --noconfirm git curl wget; then
|
||||||
|
echo "Failed to install prerequisites. Install git, curl and wget manually, then re-run this script."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
elif command -v apt >/dev/null; then
|
elif command -v apt >/dev/null; then
|
||||||
echo "apt detected. Installing prerequisites"
|
echo "apt detected. Installing prerequisites"
|
||||||
sudo apt install git curl wget -y
|
if ! sudo apt update || ! sudo apt install git curl wget -y; then
|
||||||
|
echo "Failed to install prerequisites. Install git, curl and wget manually, then re-run this script."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "No supported package manager found (pacman/apt)."
|
echo "No supported package manager found (pacman/apt)."
|
||||||
echo "Please install git, curl and wget manually, then re-run this script."
|
echo "Please install git, curl and wget manually, then re-run this script."
|
||||||
@@ -44,20 +54,24 @@ if ! command -v git >/dev/null || ! command -v curl >/dev/null || ! command -v w
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Install bat
|
# Install bat
|
||||||
fresh_bat_install=false
|
|
||||||
if ! command -v bat >/dev/null && ! command -v batcat >/dev/null; then
|
if ! command -v bat >/dev/null && ! command -v batcat >/dev/null; then
|
||||||
if command -v pacman >/dev/null; then
|
if command -v pacman >/dev/null; then
|
||||||
echo "pacman detected. Installing bat"
|
echo "pacman detected. Installing bat"
|
||||||
sudo pacman -S --needed --noconfirm bat bat-extras
|
if ! sudo pacman -S --needed --noconfirm bat bat-extras; then
|
||||||
|
echo "Failed to install bat. Install it manually, then re-run this script."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
elif command -v apt >/dev/null; then
|
elif command -v apt >/dev/null; then
|
||||||
echo "apt detected. Installing bat"
|
echo "apt detected. Installing bat"
|
||||||
sudo apt install bat -y
|
if ! sudo apt install bat -y; then
|
||||||
|
echo "Failed to install bat. Install it manually, then re-run this script."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "No supported package manager found (pacman/apt)."
|
echo "No supported package manager found (pacman/apt)."
|
||||||
echo "Please install bat manually, then re-run this script."
|
echo "Please install bat manually, then re-run this script."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fresh_bat_install=true
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Debian/Ubuntu ship the binary as batcat, provide bat for the commands below
|
# Debian/Ubuntu ship the binary as batcat, provide bat for the commands below
|
||||||
@@ -70,38 +84,45 @@ if ! command -v bat >/dev/null; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$fresh_bat_install" = true ]; then
|
# Install the Catppuccin Mocha theme (downloaded and configured names must match)
|
||||||
# 1. Detect the actual active shell name (not just the login default)
|
BAT_CONFIG_DIR="$(bat --config-dir)"
|
||||||
CURRENT_SHELL=$(basename -- "$SHELL")
|
mkdir -p "$BAT_CONFIG_DIR/themes"
|
||||||
|
if [ ! -f "$BAT_CONFIG_DIR/themes/Catppuccin Mocha.tmTheme" ]; then
|
||||||
# 2. Source the correct configuration file safely
|
if ! wget -q -O "$BAT_CONFIG_DIR/themes/Catppuccin Mocha.tmTheme" \
|
||||||
if [ "$CURRENT_SHELL" = "bash" ] && [ -f ~/.bashrc ]; then
|
"https://github.com/catppuccin/bat/raw/main/themes/Catppuccin%20Mocha.tmTheme"; then
|
||||||
source ~/.bashrc
|
echo "Failed to download the Catppuccin Mocha theme."
|
||||||
elif [ "$CURRENT_SHELL" = "zsh" ] && [ -f ~/.zshrc ]; then
|
exit 1
|
||||||
source ~/.zshrc
|
|
||||||
elif [ "$CURRENT_SHELL" = "fish" ] && [ -f ~/.config/fish/config.fish ]; then
|
|
||||||
source ~/.config/fish/config.fish
|
|
||||||
else
|
|
||||||
echo "Unsupported or unreadable shell configuration: $CURRENT_SHELL"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install Catppuccin themes
|
|
||||||
source ~/.bashrc
|
|
||||||
mkdir -p "$(bat --config-dir)/themes"
|
|
||||||
wget -P "$(bat --config-dir)/themes" https://github.com/catppuccin/bat/raw/main/themes/Catppuccin%20Macchiato.tmTheme
|
|
||||||
bat cache --build
|
bat cache --build
|
||||||
|
|
||||||
echo "--theme="Catppuccin Mocha"" >>"$(bat --config-dir)/themes"
|
# Set the default theme in bat's config file (the themes/ directory only holds themes)
|
||||||
|
if ! grep -qxF -- '--theme="Catppuccin Mocha"' "$BAT_CONFIG_DIR/config" 2>/dev/null; then
|
||||||
|
printf '%s\n' '--theme="Catppuccin Mocha"' >>"$BAT_CONFIG_DIR/config"
|
||||||
|
fi
|
||||||
|
|
||||||
# Detect shell name via POSIX standard
|
# Configure the MANPAGER for the user's actual login shell ($SHELL, not $0)
|
||||||
CURRENT_SHELL=$(basename "$0" 2>/dev/null || echo "$0")
|
CURRENT_SHELL=$(basename -- "${SHELL:-/bin/bash}")
|
||||||
CURRENT_SHELL="${CURRENT_SHELL#-}"
|
|
||||||
|
|
||||||
case "$CURRENT_SHELL" in
|
case "$CURRENT_SHELL" in
|
||||||
"bash") [ -f ~/.bashrc ] && echo "export MANPAGER="bat -plman"" >>~/.bashrc ;;
|
bash)
|
||||||
"zsh") [ -f ~/.zshrc ] && echo "export MANPAGER="bat -plman"" >>~/.zshrc ;;
|
if [ -f "$HOME/.bashrc" ] && ! grep -qxF 'export MANPAGER="bat -plman"' "$HOME/.bashrc"; then
|
||||||
"fish") exec fish -c "echo "export MANPAGER="bat -plman"" >> ~/.config/fish/config.fish" ;;
|
printf '%s\n' 'export MANPAGER="bat -plman"' >>"$HOME/.bashrc"
|
||||||
*) echo "Unsupported shell: $CURRENT_SHELL" ;;
|
fi
|
||||||
|
;;
|
||||||
|
zsh)
|
||||||
|
if [ -f "$HOME/.zshrc" ] && ! grep -qxF 'export MANPAGER="bat -plman"' "$HOME/.zshrc"; then
|
||||||
|
printf '%s\n' 'export MANPAGER="bat -plman"' >>"$HOME/.zshrc"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
fish)
|
||||||
|
if [ -f "$HOME/.config/fish/config.fish" ] && ! grep -qxF 'set -gx MANPAGER "bat -plman"' "$HOME/.config/fish/config.fish"; then
|
||||||
|
printf '%s\n' 'set -gx MANPAGER "bat -plman"' >>"$HOME/.config/fish/config.fish"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported shell: $CURRENT_SHELL (skipping MANPAGER setup)"
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
echo "bat configured with the Catppuccin Mocha theme."
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# Detect fish shell and ask user to switch to bash (bash-only syntax)
|
# Detect fish shell and ask user to switch to bash (bash-only syntax)
|
||||||
if [ -n "${FISH_VERSION:-}" ] || case "$SHELL" in *fish*) ;; *) false ;; esac; then
|
if [ -n "${FISH_VERSION:-}" ] || case "${SHELL:-}" in *fish*) ;; *) false ;; esac; then
|
||||||
echo "Warning: fish shell detected."
|
echo "Warning: fish shell detected."
|
||||||
echo "This script is written for bash and may have syntax issues under fish."
|
echo "This script is written for bash and may have syntax issues under fish."
|
||||||
echo "Please switch to bash first and re-run it, e.g.: bash $0"
|
echo "Please switch to bash first and re-run it, e.g.: bash $0"
|
||||||
read -r -p "Press Enter to continue anyway, or Ctrl+C to cancel..." </dev/tty || true
|
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
|
||||||
|
|
||||||
skip_watermark=false
|
skip_watermark=false
|
||||||
if [ "$1" = "--skip-watermark" ]; then
|
if [ "${1:-}" = "--skip-watermark" ]; then
|
||||||
skip_watermark=true
|
skip_watermark=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -23,94 +27,160 @@ if [ "$skip_watermark" = false ]; then
|
|||||||
|___/
|
|___/
|
||||||
|
|
||||||
--------------- FISH Install Script ---------------
|
--------------- FISH Install Script ---------------
|
||||||
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE UNDER THE GPL-2.0 LICENCE, THERE IS NO WARRANTY
|
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE UNDER THE GPL-2.0 LICENSE, THERE IS NO WARRANTY
|
||||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. See the LICENCE for more detail
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. See the LICENSE for more detail
|
||||||
'
|
'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Show disclaimer
|
# Show disclaimer
|
||||||
echo "This script will add fish functions and install fish"
|
echo "This script will add fish functions and install fish"
|
||||||
|
|
||||||
# Enable exit on error
|
# Resolve where this script (and its configs/) live. When the script arrives
|
||||||
set -eu
|
# over a pipe (curl | bash) there is no local checkout, so repo-config syncing
|
||||||
|
# is skipped and only the user's home directory is populated.
|
||||||
|
if [ -n "${BASH_SOURCE[0]:-}" ] && [ -f "${BASH_SOURCE[0]}" ]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
else
|
||||||
|
SCRIPT_DIR=""
|
||||||
|
fi
|
||||||
|
|
||||||
# Install prerequisites if installed skips
|
# Never clone or write into the caller's current directory
|
||||||
|
WORK_DIR="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||||
|
|
||||||
if ! command -v zsh >/dev/null && command -v git >/dev/null && command -v curl >/dev/null && command -v fzf >/dev/null; then
|
# Install prerequisites if any of them are missing
|
||||||
|
if ! command -v fish >/dev/null || ! command -v git >/dev/null ||
|
||||||
|
! command -v curl >/dev/null || ! command -v fzf >/dev/null; then
|
||||||
if command -v pacman >/dev/null; then
|
if command -v pacman >/dev/null; then
|
||||||
echo "pacman detected. Installing prerequisites"
|
echo "pacman detected. Installing prerequisites"
|
||||||
sudo pacman -S fish git curl fzf --noconfirm
|
if ! sudo pacman -S --needed --noconfirm fish git curl fzf; then
|
||||||
fi
|
echo "Failed to install prerequisites (fish, git, curl, fzf). Install them manually, then re-run this script."
|
||||||
|
exit 1
|
||||||
if command -v apt >/dev/null; then
|
fi
|
||||||
|
elif command -v apt >/dev/null; then
|
||||||
echo "apt detected. Installing prerequisites"
|
echo "apt detected. Installing prerequisites"
|
||||||
sudo apt install git curl fish fzf -y
|
if ! sudo apt update || ! sudo apt install git curl fish fzf -y; then
|
||||||
|
echo "Failed to install prerequisites (fish, git, curl, fzf). Install them manually, then re-run this script."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No supported package manager found (pacman/apt)."
|
||||||
|
echo "Please install fish, git, curl and fzf manually, then re-run this script."
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Clone or pull end-4 dotfiles repository
|
if ! command -v fish >/dev/null; then
|
||||||
if [ -d dots-hyprland ]; then
|
echo "error: fish could not be installed (no supported package manager, or install failed)." >&2
|
||||||
cd dots-hyprland && git pull && cd ..
|
echo "Install fish manually, then re-run this script." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clone the end-4 dotfiles repository into the temp workspace
|
||||||
|
if ! git clone --depth=1 https://github.com/end-4/dots-hyprland.git "$WORK_DIR/dots-hyprland"; then
|
||||||
|
echo "error: failed to clone https://github.com/end-4/dots-hyprland.git" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sync fresh end-4 files into this repo's configs/ (only when running from a
|
||||||
|
# local checkout, and only for files that do not exist yet)
|
||||||
|
if [ -n "$SCRIPT_DIR" ] && [ -d "$SCRIPT_DIR/configs" ]; then
|
||||||
|
for rel in \
|
||||||
|
fish/config.fish \
|
||||||
|
fish/auto-Hypr.fish \
|
||||||
|
fish/fish_variables \
|
||||||
|
hypr/hyprland/colors.lua \
|
||||||
|
hypr/hyprlock/colors.conf; do
|
||||||
|
if [ ! -f "$SCRIPT_DIR/configs/$rel" ] && [ -f "$WORK_DIR/dots-hyprland/dots/.config/$rel" ]; then
|
||||||
|
mkdir -p "$(dirname "$SCRIPT_DIR/configs/$rel")"
|
||||||
|
cp "$WORK_DIR/dots-hyprland/dots/.config/$rel" "$SCRIPT_DIR/configs/$rel"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run end-4 setup script with flags to skip unnecessary components.
|
||||||
|
# SKIP_END4_SETUP=1 is a test hook used by tests/smoke.sh to validate this
|
||||||
|
# script's own logic without running end-4's heavyweight installer.
|
||||||
|
echo "WARNING: the end-4 installer runs with --force and --skip-backup;"
|
||||||
|
echo "it may overwrite an existing Hyprland configuration without a backup."
|
||||||
|
if [ "${SKIP_END4_SETUP:-0}" = "1" ]; then
|
||||||
|
echo "(SKIP_END4_SETUP=1 - skipping the end-4 installer)"
|
||||||
else
|
else
|
||||||
git clone https://github.com/end-4/dots-hyprland.git
|
(
|
||||||
|
cd "$WORK_DIR/dots-hyprland"
|
||||||
|
./setup install --force --skip-plasmaintg --skip-backup --skip-quickshell --skip-hyprland --skip-hyprland-entry
|
||||||
|
)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copy fish config files from end-4 to git repo if they don't exist
|
# Resolve a config file: prefer this repo's copy, fall back to the end-4 clone
|
||||||
if [ ! -f configs/fish/config.fish ]; then
|
cfg_src() {
|
||||||
cp dots-hyprland/dots/.config/fish/config.fish configs/fish/config.fish
|
local rel="$1"
|
||||||
fi
|
if [ -n "$SCRIPT_DIR" ] && [ -e "$SCRIPT_DIR/configs/$rel" ]; then
|
||||||
|
printf '%s\n' "$SCRIPT_DIR/configs/$rel"
|
||||||
|
elif [ -e "$WORK_DIR/dots-hyprland/dots/.config/$rel" ]; then
|
||||||
|
printf '%s\n' "$WORK_DIR/dots-hyprland/dots/.config/$rel"
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
if [ ! -f configs/fish/auto-Hypr.fish ]; then
|
# Copy a config file into $HOME if it does not exist yet
|
||||||
cp dots-hyprland/dots/.config/fish/auto-Hypr.fish configs/fish/auto-Hypr.fish
|
install_file() {
|
||||||
fi
|
local rel="$1" dest="$2" src
|
||||||
|
if [ -e "$dest" ]; then
|
||||||
if [ ! -f configs/fish/fish_variables ]; then
|
return 0
|
||||||
cp dots-hyprland/dots/.config/fish/fish_variables configs/fish/fish_variables
|
fi
|
||||||
fi
|
if ! src="$(cfg_src "$rel")"; then
|
||||||
|
echo "warning: $rel not found in configs/ or the end-4 clone, skipping" >&2
|
||||||
# Copy color configs from end-4 to git repo if they don't exist
|
return 0
|
||||||
if [ ! -f configs/hypr/hyprland/colors.conf ]; then
|
fi
|
||||||
cp dots-hyprland/dots/.config/hypr/hyprland/colors.conf configs/hypr/hyprland/colors.conf
|
mkdir -p "$(dirname "$dest")"
|
||||||
fi
|
cp "$src" "$dest"
|
||||||
|
}
|
||||||
if [ ! -f configs/hypr/hyprlock/colors.conf ]; then
|
|
||||||
cp dots-hyprland/dots/.config/hypr/hyprlock/colors.conf configs/hypr/hyprlock/colors.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run end-4 setup script with flags to skip unnecessary components
|
|
||||||
dots-hyprland/setup install --force --skip-plasmaintg --skip-backup --skip-quickshell --skip-hyprland --skip-hyprland-entry
|
|
||||||
|
|
||||||
# Create fish config directory
|
|
||||||
mkdir -p "$HOME/.config/fish"
|
|
||||||
|
|
||||||
# Copy fish config files to user config if they don't exist
|
# Copy fish config files to user config if they don't exist
|
||||||
if [ ! -f "$HOME/.config/fish/config.fish" ]; then
|
install_file fish/config.fish "$HOME/.config/fish/config.fish"
|
||||||
cp configs/fish/config.fish "$HOME/.config/fish/config.fish"
|
install_file fish/auto-Hypr.fish "$HOME/.config/fish/auto-Hypr.fish"
|
||||||
|
|
||||||
|
if cfg_src fish/functions >/dev/null 2>&1 && [ ! -d "$HOME/.config/fish/functions" ]; then
|
||||||
|
mkdir -p "$HOME/.config/fish"
|
||||||
|
cp -r "$(cfg_src fish/functions)" "$HOME/.config/fish/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -d configs/fish/functions ] && [ ! -d "$HOME/.config/fish/functions" ]; then
|
if cfg_src fish/conf.d >/dev/null 2>&1 && [ ! -d "$HOME/.config/fish/conf.d" ]; then
|
||||||
cp -r configs/fish/functions "$HOME/.config/fish/"
|
mkdir -p "$HOME/.config/fish"
|
||||||
|
cp -r "$(cfg_src fish/conf.d)" "$HOME/.config/fish/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -d configs/fish/conf.d ] && [ ! -d "$HOME/.config/fish/conf.d" ]; then
|
# Copy color configs to user config if they don't exist.
|
||||||
cp -r configs/fish/conf.d "$HOME/.config/fish/"
|
# end-4 renamed hyprland's colors.conf to colors.lua and keeps hyprlock's
|
||||||
|
# colors under hypr/hyprlock/ - warn and skip if upstream moves again.
|
||||||
|
install_file hypr/hyprland/colors.lua "$HOME/.config/hypr/hyprland/colors.lua"
|
||||||
|
install_file hypr/hyprlock/colors.conf "$HOME/.config/hypr/hyprlock/colors.conf"
|
||||||
|
|
||||||
|
# Install the Fisher plugin manager and nvm.fish, running inside fish (this is
|
||||||
|
# bash). The repo configs may already ship these functions (copied above) -
|
||||||
|
# fisher refuses to install over existing files, so only bootstrap what is
|
||||||
|
# actually missing instead of forcing a re-install.
|
||||||
|
if fish -c 'type -q fisher' 2>/dev/null; then
|
||||||
|
echo "Fisher is already available in fish (from configs/), skipping bootstrap."
|
||||||
|
else
|
||||||
|
if ! curl -fsSL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish \
|
||||||
|
-o "$WORK_DIR/fisher.fish"; then
|
||||||
|
echo "error: failed to download the fisher.fish installer" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! fish -c "source '$WORK_DIR/fisher.fish' && fisher install jorgebucaran/fisher"; then
|
||||||
|
echo "error: failed to install Fisher into fish" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f "$HOME/.config/fish/auto-Hypr.fish" ]; then
|
if fish -c 'type -q nvm' 2>/dev/null; then
|
||||||
cp configs/fish/auto-Hypr.fish "$HOME/.config/fish/"
|
echo "nvm.fish is already available in fish, skipping install."
|
||||||
|
elif ! fish -c 'fisher install jorgebucaran/nvm.fish'; then
|
||||||
|
echo "error: failed to install nvm.fish into fish" >&2
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copy color configs to user config if they don't exist
|
echo "fish setup complete."
|
||||||
if [ ! -f "$HOME/.config/hypr/hyprland/colors.conf" ]; then
|
|
||||||
cp configs/hypr/hyprland/colors.conf "$HOME/.config/hypr/hyprland/colors.conf"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$HOME/.config/hypr/hyprlock/colors.conf" ]; then
|
|
||||||
cp configs/hypr/hyprlock/colors.conf "$HOME/.config/hypr/hyprlock/colors.conf"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install Fisher plugin manager for fish shell
|
|
||||||
curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher
|
|
||||||
|
|
||||||
# Install plugins using Fisher
|
|
||||||
fisher install jorgebucaran/nvm.fish
|
|
||||||
|
|||||||
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Pre-push gate: syntax-check and shellcheck every tracked shell script.
|
||||||
|
# Enable once per clone with: git config core.hooksPath hooks
|
||||||
|
# Bypass a single push with: git push --no-verify
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
|
mapfile -t scripts < <(git ls-files '*.sh')
|
||||||
|
if [ "${#scripts[@]}" -eq 0 ]; then
|
||||||
|
echo "pre-push: no tracked shell scripts found" >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
status=0
|
||||||
|
|
||||||
|
for f in "${scripts[@]}"; do
|
||||||
|
if ! bash -n "$f"; then
|
||||||
|
echo "pre-push: bash -n FAILED for $f" >&2
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! command -v shellcheck >/dev/null; then
|
||||||
|
echo "pre-push: shellcheck is not installed - the gate cannot run." >&2
|
||||||
|
echo " install it (e.g. 'pacman -S shellcheck' or 'apt install shellcheck')" >&2
|
||||||
|
echo " or bypass this once with: git push --no-verify" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# SC1090 (can't follow non-constant source) is excluded: every 'source ~/.bashrc'
|
||||||
|
# style call in these scripts triggers it and it is not actionable.
|
||||||
|
if ! shellcheck -S warning -e SC1090 "${scripts[@]}"; then
|
||||||
|
echo "pre-push: shellcheck reported problems (see above)" >&2
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$status" -eq 0 ]; then
|
||||||
|
echo "pre-push: all ${#scripts[@]} shell scripts pass bash -n + shellcheck"
|
||||||
|
fi
|
||||||
|
exit "$status"
|
||||||
+44
-45
@@ -8,9 +8,8 @@ fi
|
|||||||
|
|
||||||
# Detect fish shell and warn user to run with bash instead (bash-only syntax)
|
# 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 [ -n "${FISH_VERSION:-}" ] || case "$SHELL" in *fish*) ;; *) false ;; esac; then
|
||||||
if command -v whiptail >/dev/null; then
|
if command -v gum >/dev/null; then
|
||||||
if ! whiptail --title "Warning: use bash, not fish" --yesno \
|
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 install.sh\n\nContinue anyway?')"; then
|
||||||
"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 install.sh\n\nContinue anyway?" 15 72; then
|
|
||||||
echo "Aborted. Please run this script with bash, not fish. e.g.: bash install.sh"
|
echo "Aborted. Please run this script with bash, not fish. e.g.: bash install.sh"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -84,70 +83,70 @@ 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 whiptail --upgrade --no-confirm
|
shelly install standard git zsh curl wget libnewt rsync gum --upgrade --no-confirm
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v apt >/dev/null; then
|
if command -v apt >/dev/null; then
|
||||||
echo "apt detected. Installing prerequisites"
|
echo "apt detected. Installing prerequisites"
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt full-upgrade -y
|
sudo apt full-upgrade -y
|
||||||
sudo apt install git zsh curl wget whiptail rsync -y
|
sudo apt install git zsh curl wget rsync gum -y
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure whiptail is installed
|
# Ensure gum is installed
|
||||||
if ! command -v whiptail >/dev/null; then
|
if ! command -v gum >/dev/null; then
|
||||||
echo "whiptail 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 whiptail"
|
echo "shelly detected. Installing gum"
|
||||||
shelly install standard whiptail --no-confirm || {
|
shelly install standard gum --no-confirm || {
|
||||||
echo "Failed to install whiptail. Exiting."
|
echo "Failed to install gum. Exiting."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v apt >/dev/null; then
|
if command -v apt >/dev/null; then
|
||||||
echo "apt detected. Installing whiptail"
|
echo "apt detected. Installing gum"
|
||||||
sudo apt install whiptail -y || {
|
sudo apt install gum -y || {
|
||||||
echo "Failed to install whiptail. Exiting."
|
echo "Failed to install gum. Exiting."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Options for the whiptail menu
|
# Options for the gum menu
|
||||||
OPTIONS=(
|
OPTIONS=(
|
||||||
1 "Run zsh setup script" OFF
|
"Run zsh setup script"
|
||||||
2 "Run fish setup script" ON
|
"Run fish setup script"
|
||||||
3 "Run LazyVim setup script" OFF
|
"Run LazyVim setup script"
|
||||||
4 "Install Docker" OFF
|
"Install Docker"
|
||||||
5 "Install Standard Packages (Shelly)" ON
|
"Install Standard Packages (Shelly)"
|
||||||
6 "Install AUR Packages (Shelly)" ON
|
"Install AUR Packages (Shelly)"
|
||||||
7 "Install affinity with GUI" OFF
|
"Install affinity with GUI"
|
||||||
8 "Run bat setup script" ON
|
"Run bat setup script"
|
||||||
)
|
)
|
||||||
|
|
||||||
CHOICE=$(whiptail --title "Installation Options" --checklist \
|
CHOICE=$(gum choose --no-limit --height 14 \
|
||||||
"Choose components to install:" 20 78 10 \
|
--header "Tab = select, Enter = confirm:" \
|
||||||
"${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
--selected "Run fish setup script,Install Standard Packages (Shelly),Install AUR Packages (Shelly),Run bat setup script" \
|
||||||
|
"${OPTIONS[@]}") || {
|
||||||
exitstatus=$?
|
|
||||||
if [ $exitstatus = 0 ]; then
|
|
||||||
echo "User selected: $CHOICE"
|
|
||||||
else
|
|
||||||
echo "User cancelled installation."
|
echo "User cancelled installation."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
}
|
||||||
|
|
||||||
|
echo "User selected:"
|
||||||
|
echo "$CHOICE"
|
||||||
|
|
||||||
# Fail on any command.
|
# Fail on any command.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Process selected options
|
# Process selected options in menu order
|
||||||
for selection in $CHOICE; do
|
while IFS= read -r selection; do
|
||||||
clean_selection=$(echo "$selection" | tr -d '"')
|
# Skip options the user did not select
|
||||||
case $clean_selection in
|
grep -qxF "$selection" <<<"$CHOICE" || continue
|
||||||
"1")
|
case "$selection" in
|
||||||
|
"Run zsh setup script")
|
||||||
echo "Running zsh setup script..."
|
echo "Running zsh setup script..."
|
||||||
# Runs local script unless it does not exist or fails
|
# Runs local script unless it does not exist or fails
|
||||||
if [[ -f "zsh.sh" ]]; then
|
if [[ -f "zsh.sh" ]]; then
|
||||||
@@ -157,7 +156,7 @@ for selection in $CHOICE; do
|
|||||||
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/zsh.sh | bash -s -- --skip-watermark
|
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/zsh.sh | bash -s -- --skip-watermark
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"2")
|
"Run fish setup script")
|
||||||
echo "Running fish setup script..."
|
echo "Running fish setup script..."
|
||||||
# Runs local script unless it does not exist or fails
|
# Runs local script unless it does not exist or fails
|
||||||
if [[ -f "fish.sh" ]]; then
|
if [[ -f "fish.sh" ]]; then
|
||||||
@@ -167,7 +166,7 @@ for selection in $CHOICE; do
|
|||||||
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
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"3")
|
"Run LazyVim setup script")
|
||||||
echo "Running LazyVim setup script..."
|
echo "Running LazyVim setup script..."
|
||||||
# Runs local script unless it does not exist or fails
|
# Runs local script unless it does not exist or fails
|
||||||
if [[ -f "LazyVim.sh" ]]; then
|
if [[ -f "LazyVim.sh" ]]; then
|
||||||
@@ -177,7 +176,7 @@ for selection in $CHOICE; do
|
|||||||
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/LazyVim.sh | bash
|
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/LazyVim.sh | bash
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"4")
|
"Install Docker")
|
||||||
echo "Installing Docker..."
|
echo "Installing Docker..."
|
||||||
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..."
|
||||||
@@ -189,7 +188,7 @@ for selection in $CHOICE; do
|
|||||||
echo "+ sleep 10" && sleep 10
|
echo "+ sleep 10" && sleep 10
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"5")
|
"Install Standard Packages (Shelly)")
|
||||||
echo "Installing Standard Packages via Shelly backup..."
|
echo "Installing Standard Packages via Shelly backup..."
|
||||||
# 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
|
||||||
@@ -215,7 +214,7 @@ for selection in $CHOICE; do
|
|||||||
echo "+ sleep 10" && sleep 10
|
echo "+ sleep 10" && sleep 10
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"6")
|
"Install AUR Packages (Shelly)")
|
||||||
echo "Installing AUR Packages via Shelly backup..."
|
echo "Installing AUR Packages via Shelly backup..."
|
||||||
# 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
|
||||||
@@ -241,7 +240,7 @@ for selection in $CHOICE; do
|
|||||||
echo "+ sleep 10" && sleep 10
|
echo "+ sleep 10" && sleep 10
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"7")
|
"Install affinity with GUI")
|
||||||
echo "Installing GUI dependencies"
|
echo "Installing GUI dependencies"
|
||||||
if command -v shelly >/dev/null; then
|
if command -v shelly >/dev/null; then
|
||||||
echo "shelly found"
|
echo "shelly found"
|
||||||
@@ -262,7 +261,7 @@ for selection in $CHOICE; do
|
|||||||
sleep 10
|
sleep 10
|
||||||
curl -sSL https://raw.githubusercontent.com/ryzendew/AffinityOnLinux/refs/heads/main/AffinityScripts/AffinityLinuxInstaller.py | python3
|
curl -sSL https://raw.githubusercontent.com/ryzendew/AffinityOnLinux/refs/heads/main/AffinityScripts/AffinityLinuxInstaller.py | python3
|
||||||
;;
|
;;
|
||||||
"8")
|
"Run bat setup script")
|
||||||
echo "Running bat setup script..."
|
echo "Running bat setup script..."
|
||||||
# Runs local script unless it does not exist or fails
|
# Runs local script unless it does not exist or fails
|
||||||
if [[ -f "bat.sh" ]]; then
|
if [[ -f "bat.sh" ]]; then
|
||||||
@@ -277,6 +276,6 @@ for selection in $CHOICE; do
|
|||||||
echo "Invalid option selected: $selection"
|
echo "Invalid option selected: $selection"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done < <(printf '%s\n' "${OPTIONS[@]}")
|
||||||
|
|
||||||
echo "Installation process complete."
|
echo "Installation process complete."
|
||||||
|
|||||||
+418
-161
@@ -1,29 +1,17 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
|
||||||
PreviousWD=$(pwd)
|
# Strict mode from the very start: every tolerated failure in this script is
|
||||||
|
# an explicit guard (if ! / || record_failure), so preamble steps are checked
|
||||||
|
# too. Anything unguarded aborts the run via errexit.
|
||||||
|
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 whiptail >/dev/null; then
|
|
||||||
if ! whiptail --title "Warning: use bash, not fish" --yesno \
|
|
||||||
"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?" 15 72; 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"
|
|
||||||
read -r -p "Press Enter to continue anyway, or Ctrl+C to cancel..." </dev/tty || true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$skip_watermark" = false ]; then
|
if [ "$skip_watermark" = false ]; then
|
||||||
clear
|
clear 2>/dev/null || true
|
||||||
echo '
|
echo '
|
||||||
____ _ _ _ _ _
|
____ _ _ _ _ _
|
||||||
| __ ) _ _ __ _| |_ _(_)_ __ | | ___ | | | ___
|
| __ ) _ _ __ _| |_ _(_)_ __ | | ___ | | | ___
|
||||||
@@ -33,49 +21,210 @@ if [ "$skip_watermark" = false ]; then
|
|||||||
|___/
|
|___/
|
||||||
|
|
||||||
--------------- Single Download script ---------------
|
--------------- Single Download script ---------------
|
||||||
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE UNDER THE GPL-2.0 LICENCE,
|
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE UNDER THE GPL-2.0 LICENSE,
|
||||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
See the LICENCE for more detail
|
See the LICENSE for more detail
|
||||||
'
|
'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Function to display error message
|
# --- Failure handling & full console log ------------------------------------
|
||||||
function error_handler() {
|
# Everything from here on is teed to a log file in /tmp, and Arch-only step
|
||||||
set +x
|
# failures are recorded instead of aborting the rest of the installation.
|
||||||
echo -e "An error occurred. Please check the output above for details."
|
TTY_OK=false
|
||||||
|
if [ -t 1 ]; then
|
||||||
|
TTY_OK=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# One timestamp for both files, mktemp so the names are not predictable
|
||||||
|
STAMP="$(date +%Y%m%d-%H%M%S)"
|
||||||
|
LOG="$(mktemp "/tmp/installbeta-$STAMP-XXXXXX.log")"
|
||||||
|
REPORT="$(mktemp "/tmp/installbeta-failures-$STAMP-XXXXXX.txt")"
|
||||||
|
FAILED=()
|
||||||
|
|
||||||
|
exec > >(tee -a "$LOG") 2>&1
|
||||||
|
|
||||||
|
# gum draws its TUI on stderr - keep that on the real terminal, not the log pipe
|
||||||
|
gum_tty() {
|
||||||
|
if [ "$TTY_OK" = true ]; then
|
||||||
|
gum "$@" 2>/dev/tty
|
||||||
|
else
|
||||||
|
gum "$@"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Trap errors
|
# Run a long non-interactive step behind a spinner when gum exists, plain
|
||||||
trap error_handler ERR
|
# 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"
|
||||||
|
}
|
||||||
|
|
||||||
# Install shelly as a dependency if not present (Arch-based)
|
# 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_failure() {
|
||||||
|
FAILED+=("$1 — $2")
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# are a symlink attack vector on multi-user systems; the trap cleans up too.
|
||||||
|
WORK_DIR="$(mktemp -d)"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# depend on the caller's current directory. Empty when piped (curl | bash).
|
||||||
|
if [ -n "${BASH_SOURCE[0]:-}" ] && [ -f "${BASH_SOURCE[0]}" ]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
else
|
||||||
|
SCRIPT_DIR=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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
|
||||||
exit 1 # exit with an error
|
# 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 /tmp/shelly_install; 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 /tmp/shelly_install"
|
try_step "Shelly bootstrap (Arch-only)" "failed to build shelly with makepkg" \
|
||||||
echo "--------------------------------------------------------------------"
|
"Failed to build and install shelly." --spin "Install Shelly" \
|
||||||
exit 1 # exit with an error
|
makepkg -D "$WORK_DIR/shelly" -si --noconfirm || true
|
||||||
elif ! (cd /tmp/shelly_install && makepkg -si --noconfirm); then
|
|
||||||
echo "--------------------------------------------------------------------"
|
|
||||||
echo "Failed to build and install shelly. You can try running it manually:"
|
|
||||||
echo "cd /tmp/shelly_install && makepkg -si --noconfirm"
|
|
||||||
echo "--------------------------------------------------------------------"
|
|
||||||
exit 1 # exit with an error
|
|
||||||
fi
|
fi
|
||||||
cd "$PreviousWD"
|
|
||||||
rm -rf /tmp/shelly_install
|
|
||||||
else
|
else
|
||||||
echo "Shelly is already installed."
|
echo "Shelly is already installed."
|
||||||
fi
|
fi
|
||||||
@@ -84,198 +233,306 @@ 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 whiptail --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"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v apt >/dev/null; then
|
if command -v apt >/dev/null; then
|
||||||
echo "apt detected. Installing prerequisites"
|
echo "apt detected. Installing prerequisites"
|
||||||
sudo apt update
|
sudo apt update || record_failure "Prerequisites (apt)" "apt update failed"
|
||||||
sudo apt full-upgrade -y
|
sudo apt full-upgrade -y || record_failure "Prerequisites (apt)" "apt full-upgrade failed"
|
||||||
sudo apt install git zsh curl wget whiptail rsync -y
|
sudo -v
|
||||||
|
sudo apt install git zsh curl wget rsync gum -y ||
|
||||||
|
record_failure "Prerequisites (apt)" "apt install of base packages failed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure whiptail is installed
|
# Ensure gum is installed (safety net: pacman/apt above should already have
|
||||||
if ! command -v whiptail >/dev/null; then
|
# provided it, but nothing below can run without it)
|
||||||
echo "whiptail is not installed. Installing it now..."
|
if ! command -v gum >/dev/null; then
|
||||||
|
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 whiptail"
|
echo "shelly detected. Installing gum"
|
||||||
shelly install standard whiptail --no-confirm || {
|
shelly install standard gum --no-confirm || {
|
||||||
echo "Failed to install whiptail. Exiting."
|
log_fatal "Failed to install gum. Exiting."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v apt >/dev/null; then
|
if command -v apt >/dev/null; then
|
||||||
echo "apt detected. Installing whiptail"
|
echo "apt detected. Installing gum"
|
||||||
sudo apt install whiptail -y || {
|
sudo apt install gum -y || {
|
||||||
echo "Failed to install whiptail. Exiting."
|
log_fatal "Failed to install gum. Exiting."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Options for the whiptail menu
|
# gum drives every interactive dialog in this script and there is no fallback
|
||||||
OPTIONS=(
|
# menu, so a system that ended up without it must abort here.
|
||||||
1 "Run zsh setup script" OFF
|
if ! command -v gum >/dev/null; then
|
||||||
2 "Run fish setup script" ON
|
log_fatal "gum is required but could not be installed on this system (no shelly/apt found)."
|
||||||
3 "Run LazyVim setup script" OFF
|
echo "Install it manually: https://github.com/charmbracelet/gum" >&2
|
||||||
4 "Install Docker" OFF
|
|
||||||
5 "Install Standard Packages (Shelly)" ON
|
|
||||||
6 "Install AUR Packages (Shelly)" ON
|
|
||||||
7 "Install affinity with GUI" OFF
|
|
||||||
8 "Run bat setup script" ON
|
|
||||||
)
|
|
||||||
|
|
||||||
CHOICE=$(whiptail --title "Installation Options" --checklist \
|
|
||||||
"Choose components to install:" 20 78 10 \
|
|
||||||
"${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
|
||||||
|
|
||||||
exitstatus=$?
|
|
||||||
if [ $exitstatus = 0 ]; then
|
|
||||||
echo "User selected: $CHOICE"
|
|
||||||
else
|
|
||||||
echo "User cancelled installation."
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Fail on any command.
|
# Detect fish shell and warn user to run with bash instead (bash-only syntax).
|
||||||
set -euo pipefail
|
# 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
|
||||||
|
|
||||||
# Process selected options
|
# Options for the gum menu
|
||||||
for selection in $CHOICE; do
|
OPTIONS=(
|
||||||
clean_selection=$(echo "$selection" | tr -d '"')
|
"Run zsh setup script"
|
||||||
case $clean_selection in
|
"Run fish setup script"
|
||||||
"1")
|
"Run LazyVim setup script"
|
||||||
|
"Install Docker"
|
||||||
|
"Install Standard Packages (Shelly)"
|
||||||
|
"Install AUR Packages (Shelly)"
|
||||||
|
"Install affinity with GUI"
|
||||||
|
"Run bat setup script"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Present the menu with gum (gum is guaranteed by the bootstrap above)
|
||||||
|
if ! CHOICE=$(gum_tty choose --no-limit --height 14 \
|
||||||
|
--header "Tab = select, Enter = confirm:" \
|
||||||
|
--selected "Run fish setup script,Install Standard Packages (Shelly),Install AUR Packages (Shelly),Run bat setup script" \
|
||||||
|
"${OPTIONS[@]}"); then
|
||||||
|
echo "No options selected (menu cancelled). Nothing was installed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# gum exits 0 with empty output when nothing is selected - treat that as a
|
||||||
|
# cancel too, otherwise the run below would silently install nothing.
|
||||||
|
if [ -z "$CHOICE" ]; then
|
||||||
|
echo "No options selected. Nothing was installed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "User selected:"
|
||||||
|
echo "$CHOICE"
|
||||||
|
|
||||||
|
# Process selected options in menu order
|
||||||
|
while IFS= read -r selection; do
|
||||||
|
# Skip options the user did not select
|
||||||
|
grep -qxF "$selection" <<<"$CHOICE" || continue
|
||||||
|
case "$selection" in
|
||||||
|
"Run zsh setup script")
|
||||||
echo "Running zsh setup script..."
|
echo "Running zsh setup script..."
|
||||||
# Runs local script unless it does not exist or fails
|
sudo -v
|
||||||
if [[ -f "zsh.sh" ]]; then
|
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
||||||
|
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/zsh.sh" ]; then
|
||||||
echo "Found local script, running..."
|
echo "Found local script, running..."
|
||||||
bash zsh.sh --skip-watermark
|
bash "$SCRIPT_DIR/zsh.sh" --skip-watermark || {
|
||||||
|
rc=$?
|
||||||
|
record_failure "Run zsh setup script" "zsh.sh exited with status $rc"
|
||||||
|
}
|
||||||
else
|
else
|
||||||
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/zsh.sh | bash -s -- --skip-watermark
|
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/zsh.sh | bash -s -- --skip-watermark || {
|
||||||
|
rc=$?
|
||||||
|
record_failure "Run zsh setup script" "downloaded zsh.sh exited with status $rc"
|
||||||
|
}
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"2")
|
"Run fish setup script")
|
||||||
echo "Running fish setup script..."
|
echo "Running fish setup script..."
|
||||||
# Runs local script unless it does not exist or fails
|
sudo -v
|
||||||
if [[ -f "fish.sh" ]]; then
|
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
||||||
|
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/fish.sh" ]; then
|
||||||
echo "Found local script, running..."
|
echo "Found local script, running..."
|
||||||
bash fish.sh --skip-watermark
|
bash "$SCRIPT_DIR/fish.sh" --skip-watermark || {
|
||||||
|
rc=$?
|
||||||
|
record_failure "Run fish setup script" "fish.sh exited with status $rc"
|
||||||
|
}
|
||||||
else
|
else
|
||||||
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/fish.sh | bash -s -- --skip-watermark
|
# 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 || {
|
||||||
|
rc=$?
|
||||||
|
record_failure "Run fish setup script" "downloaded fish.sh exited with status $rc"
|
||||||
|
}
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"3")
|
"Run LazyVim setup script")
|
||||||
echo "Running LazyVim setup script..."
|
echo "Running LazyVim setup script..."
|
||||||
# Runs local script unless it does not exist or fails
|
sudo -v
|
||||||
if [[ -f "LazyVim.sh" ]]; then
|
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
||||||
|
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/LazyVim.sh" ]; then
|
||||||
echo "Found local script, running..."
|
echo "Found local script, running..."
|
||||||
bash LazyVim.sh
|
bash "$SCRIPT_DIR/LazyVim.sh" || {
|
||||||
|
rc=$?
|
||||||
|
record_failure "Run LazyVim setup script" "LazyVim.sh exited with status $rc"
|
||||||
|
}
|
||||||
else
|
else
|
||||||
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/LazyVim.sh | bash
|
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/LazyVim.sh | bash || {
|
||||||
|
rc=$?
|
||||||
|
record_failure "Run LazyVim setup script" "downloaded LazyVim.sh exited with status $rc"
|
||||||
|
}
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"4")
|
"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..."
|
||||||
curl -fsSL https://get.docker.com | sh
|
if curl -fsSL https://get.docker.com -o "$WORK_DIR/get-docker.sh" &&
|
||||||
sudo usermod -aG docker "$USER"
|
sh "$WORK_DIR/get-docker.sh"; then
|
||||||
|
sudo usermod -aG docker "$USER" ||
|
||||||
|
record_failure "Install Docker" "adding $USER to the docker group failed"
|
||||||
|
else
|
||||||
|
rc=$?
|
||||||
|
record_failure "Install Docker" "docker installer script exited with status $rc"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
sudo usermod -aG docker "$USER"
|
sudo usermod -aG docker "$USER" ||
|
||||||
|
record_failure "Install Docker" "adding $USER to the docker group failed"
|
||||||
echo "Docker is already installed."
|
echo "Docker is already installed."
|
||||||
echo "+ sleep 10" && sleep 10
|
|
||||||
fi
|
fi
|
||||||
|
echo "NOTE: $USER has been added to the docker group."
|
||||||
|
echo "Log out and back in (or reboot) for the group membership to take effect."
|
||||||
;;
|
;;
|
||||||
"5")
|
"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"
|
echo "Cannot proceed: shelly binary not found (Arch-only)"
|
||||||
exit 1 # exit with an error
|
record_failure "Install Standard Packages (Shelly)" "shelly binary not found (Arch-only)"
|
||||||
fi
|
|
||||||
# Install standard packages from shelly backup
|
# Install standard packages from shelly backup
|
||||||
# Runs local script unless it does not exist or fails
|
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
||||||
if [[ -f "./configs/shelly-standard.toml" ]]; then
|
elif [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/configs/shelly-standard.toml" ]; then
|
||||||
echo "Found local backup"
|
echo "Found local backup"
|
||||||
shelly backup --import --name shelly-standard --directory ./configs --no-confirm
|
shelly backup --import --name shelly-standard --directory "$SCRIPT_DIR/configs" --no-confirm || {
|
||||||
elif ! curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/shelly-standard.toml -o /tmp/shelly-standard.toml; then
|
rc=$?
|
||||||
echo "--------------------------------------------------------------------"
|
record_failure "Install Standard Packages (Shelly)" "shelly backup import exited with status $rc"
|
||||||
echo "Failed to download shelly backup. You can try running it manually:"
|
}
|
||||||
echo "curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/shelly-standard.toml -o /tmp/shelly-standard.toml"
|
elif try_step "Install Standard Packages (Shelly)" "failed to download shelly-standard.toml" \
|
||||||
echo "--------------------------------------------------------------------"
|
"Failed to download shelly backup." \
|
||||||
echo "+ sleep 10" && sleep 10
|
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 ! shelly backup --import --name shelly-standard --directory /tmp --no-confirm; then
|
try_step "Install Standard Packages (Shelly)" "shelly backup import failed" \
|
||||||
echo "--------------------------------------------------------------------"
|
"Failed to install Standard packages." \
|
||||||
echo "Failed to install Standard packages. You can try running it manually:"
|
shelly backup --import --name shelly-standard --directory "$WORK_DIR" --no-confirm || true
|
||||||
echo "shelly backup --import --name shelly-standard --directory /tmp --no-confirm"
|
|
||||||
echo "--------------------------------------------------------------------"
|
|
||||||
echo "+ sleep 10" && sleep 10
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"6")
|
"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"
|
echo "Cannot proceed: shelly binary not found (Arch-only)"
|
||||||
exit 1 # Exit with an error
|
record_failure "Install AUR Packages (Shelly)" "shelly binary not found (Arch-only)"
|
||||||
fi
|
|
||||||
# Install AUR packages from shelly backup
|
# Install AUR packages from shelly backup
|
||||||
# Runs local script unless it does not exist or fails
|
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
||||||
if [[ -f "./configs/shelly-aur.toml" ]]; then
|
elif [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/configs/shelly-aur.toml" ]; then
|
||||||
echo "Found local backup"
|
echo "Found local backup"
|
||||||
shelly backup --import --name shelly-aur --directory ./configs --no-confirm
|
shelly backup --import --name shelly-aur --directory "$SCRIPT_DIR/configs" --no-confirm || {
|
||||||
elif ! curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/shelly-aur.toml -o /tmp/shelly-aur.toml; then
|
rc=$?
|
||||||
echo "--------------------------------------------------------------------"
|
record_failure "Install AUR Packages (Shelly)" "shelly backup import exited with status $rc"
|
||||||
echo "Failed to download shelly backup. You can try running it manually:"
|
}
|
||||||
echo "curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/configs/shelly-aur.toml -o /tmp/shelly-aur.toml"
|
elif try_step "Install AUR Packages (Shelly)" "failed to download shelly-aur.toml" \
|
||||||
echo "--------------------------------------------------------------------"
|
"Failed to download shelly backup." \
|
||||||
echo "+ sleep 10" && sleep 10
|
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 ! shelly backup --import --name shelly-aur --directory /tmp --no-confirm; then
|
try_step "Install AUR Packages (Shelly)" "shelly backup import failed" \
|
||||||
echo "--------------------------------------------------------------------"
|
"Failed to install AUR packages." \
|
||||||
echo "Failed to install AUR packages. You can try running it manually:"
|
shelly backup --import --name shelly-aur --directory "$WORK_DIR" --no-confirm || true
|
||||||
echo "shelly backup --import --name shelly-aur --directory /tmp --no-confirm"
|
|
||||||
echo "--------------------------------------------------------------------"
|
|
||||||
echo "+ sleep 10" && sleep 10
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"7")
|
"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 ||
|
||||||
|
record_failure "Install affinity with GUI" "installing python-pyqt6 via shelly failed"
|
||||||
fi
|
fi
|
||||||
if command -v dnf >/dev/null; then
|
if command -v dnf >/dev/null; then
|
||||||
echo "dnf found"
|
echo "dnf found"
|
||||||
sudo dnf install python3-pyqt6
|
sudo dnf install python3-pyqt6 ||
|
||||||
|
record_failure "Install affinity with GUI" "installing python3-pyqt6 via dnf failed"
|
||||||
fi
|
fi
|
||||||
if command -v apt >/dev/null; then
|
if command -v apt >/dev/null; then
|
||||||
echo "apt found"
|
echo "apt found"
|
||||||
sudo apt install python3-pyqt6 -y
|
sudo apt install python3-pyqt6 -y ||
|
||||||
|
record_failure "Install affinity with GUI" "installing python3-pyqt6 via apt failed"
|
||||||
fi
|
fi
|
||||||
echo "Install affinity with ryzendew's gui installer"
|
echo "Install affinity with ryzendew's gui installer"
|
||||||
echo "You must manually select to install in the GUI"
|
echo "You must manually select to install in the GUI"
|
||||||
echo "Github repo: https://github.com/ryzendew/Linux-Affinity-Installer"
|
echo "Github repo: https://github.com/ryzendew/Linux-Affinity-Installer"
|
||||||
echo "+ 10 sleep"
|
echo "Starting the GUI installer in 10 seconds - read the notes above first."
|
||||||
sleep 10
|
sleep 10
|
||||||
curl -sSL https://raw.githubusercontent.com/ryzendew/AffinityOnLinux/refs/heads/main/AffinityScripts/AffinityLinuxInstaller.py | python3
|
{ curl -fsSL https://raw.githubusercontent.com/ryzendew/AffinityOnLinux/refs/heads/main/AffinityScripts/AffinityLinuxInstaller.py \
|
||||||
|
-o "$WORK_DIR/AffinityLinuxInstaller.py" &&
|
||||||
|
python3 "$WORK_DIR/AffinityLinuxInstaller.py"; } || {
|
||||||
|
rc=$?
|
||||||
|
record_failure "Install affinity with GUI" "affinity installer exited with status $rc"
|
||||||
|
}
|
||||||
;;
|
;;
|
||||||
"8")
|
"Run bat setup script")
|
||||||
echo "Running bat setup script..."
|
echo "Running bat setup script..."
|
||||||
# Runs local script unless it does not exist or fails
|
# Runs local script unless it does not exist (SCRIPT_DIR, not CWD)
|
||||||
if [[ -f "bat.sh" ]]; then
|
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/bat.sh" ]; then
|
||||||
echo "Found local script, running..."
|
echo "Found local script, running..."
|
||||||
bash bat.sh --skip-watermark
|
bash "$SCRIPT_DIR/bat.sh" --skip-watermark || {
|
||||||
|
rc=$?
|
||||||
|
record_failure "Run bat setup script" "bat.sh exited with status $rc"
|
||||||
|
}
|
||||||
else
|
else
|
||||||
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/bat.sh | bash -s -- --skip-watermark
|
curl -fsSL https://raw.githubusercontent.com/alvinlollo/Single-install-script/refs/heads/main/bat.sh | bash -s -- --skip-watermark || {
|
||||||
|
rc=$?
|
||||||
|
record_failure "Run bat setup script" "downloaded bat.sh exited with status $rc"
|
||||||
|
}
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Invalid option selected: $selection"
|
log_error "Invalid option selected: $selection"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done < <(printf '%s\n' "${OPTIONS[@]}")
|
||||||
|
|
||||||
echo "Installation process complete."
|
echo "Installation process complete."
|
||||||
|
|
||||||
|
# Show (and save) what failed - the run itself never aborts on a failed step
|
||||||
|
if [ "${#FAILED[@]}" -gt 0 ]; then
|
||||||
|
{
|
||||||
|
echo ""
|
||||||
|
echo "================ FAILURE SUMMARY ================"
|
||||||
|
echo "installbeta.sh finished at $(date '+%F %T') - ${#FAILED[@]} step(s) failed:"
|
||||||
|
printf ' - %s\n' "${FAILED[@]}"
|
||||||
|
echo ""
|
||||||
|
echo "Full console log: $LOG"
|
||||||
|
echo "Failure report: $REPORT"
|
||||||
|
echo "==================================================="
|
||||||
|
} | tee -a "$REPORT"
|
||||||
|
|
||||||
|
if [ "$TTY_OK" = true ] && command -v gum >/dev/null; then
|
||||||
|
gum_tty pager <"$REPORT" || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Non-zero so callers and CI can detect the failed run
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
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
|
||||||
|
|||||||
Executable
+145
@@ -0,0 +1,145 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Manual end-to-end smoke test. NOT part of the pre-push hook - run by hand:
|
||||||
|
#
|
||||||
|
# tests/smoke.sh # default set: bat.sh zsh.sh fish.sh
|
||||||
|
# tests/smoke.sh LazyVim.sh # opt in to the heavy LazyVim run
|
||||||
|
# tests/smoke.sh bat.sh # run specific scripts only
|
||||||
|
#
|
||||||
|
# Runs each script inside a throwaway Arch container with a stubbed 'sudo'
|
||||||
|
# (the container is already root). Needs docker or podman and network access.
|
||||||
|
# This is what catches the bug class shellcheck cannot: inverted conditions,
|
||||||
|
# swapped shell branches, quoting that collapses when written to a file.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
|
||||||
|
if command -v docker >/dev/null 2>&1; then
|
||||||
|
RUNTIME=docker
|
||||||
|
elif command -v podman >/dev/null 2>&1; then
|
||||||
|
RUNTIME=podman
|
||||||
|
else
|
||||||
|
echo "error: neither docker nor podman found - cannot run the smoke test." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEFAULT_SCRIPTS=(bat.sh zsh.sh fish.sh)
|
||||||
|
if [ "$#" -gt 0 ]; then
|
||||||
|
SCRIPTS=("$@")
|
||||||
|
else
|
||||||
|
SCRIPTS=("${DEFAULT_SCRIPTS[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate arguments up front so a typo does not run in a container first
|
||||||
|
for s in "${SCRIPTS[@]}"; do
|
||||||
|
if [ ! -f "$ROOT/$s" ]; then
|
||||||
|
echo "error: $s not found in $ROOT" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Everything inside the container:
|
||||||
|
# - copy the read-only mount so scripts that write to their own directory
|
||||||
|
# (fish.sh syncing configs/) cannot mutate the host checkout
|
||||||
|
# - stub 'sudo' -> exec (container already runs as root)
|
||||||
|
# - pacman -Sy so package lookups work on a fresh image
|
||||||
|
# - HOME=/root keeps every script writing inside the container
|
||||||
|
container_run() {
|
||||||
|
local body="$1"
|
||||||
|
"$RUNTIME" run --rm \
|
||||||
|
-v "$ROOT:/src:ro" \
|
||||||
|
-e HOME=/root \
|
||||||
|
archlinux:latest \
|
||||||
|
bash -c "
|
||||||
|
set -euo pipefail
|
||||||
|
cp -r /src /work
|
||||||
|
cd /work
|
||||||
|
printf '#!/bin/sh\nexec \"\$@\"\n' > /usr/local/bin/sudo
|
||||||
|
chmod +x /usr/local/bin/sudo
|
||||||
|
pacman -Sy --noconfirm >/dev/null 2>&1 || true
|
||||||
|
$body
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare -a RESULTS=()
|
||||||
|
overall=0
|
||||||
|
|
||||||
|
run_test() {
|
||||||
|
local script="$1" body="$2"
|
||||||
|
echo ""
|
||||||
|
echo "=== smoke: $script ==="
|
||||||
|
if container_run "$body"; then
|
||||||
|
echo "=== PASS: $script ==="
|
||||||
|
RESULTS+=("PASS $script")
|
||||||
|
else
|
||||||
|
echo "=== FAIL: $script (exit $?)" >&2
|
||||||
|
RESULTS+=("FAIL $script")
|
||||||
|
overall=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
for script in "${SCRIPTS[@]}"; do
|
||||||
|
case "$script" in
|
||||||
|
bat.sh)
|
||||||
|
run_test "$script" '
|
||||||
|
bash bat.sh --skip-watermark
|
||||||
|
command -v bat >/dev/null || { echo "bat not on PATH after install"; exit 1; }
|
||||||
|
grep -qxF -- "--theme=\"Catppuccin Mocha\"" "$(bat --config-dir)/config" \
|
||||||
|
|| { echo "theme line missing from bat config:"; cat "$(bat --config-dir)/config" 2>/dev/null; exit 1; }
|
||||||
|
[ -s "$(bat --config-dir)/themes/Catppuccin Mocha.tmTheme" ] \
|
||||||
|
|| { echo "theme file was not downloaded"; exit 1; }
|
||||||
|
# re-run must be idempotent (no duplicate config lines)
|
||||||
|
bash bat.sh --skip-watermark
|
||||||
|
[ "$(grep -cF -- "--theme=" "$(bat --config-dir)/config")" -eq 1 ] \
|
||||||
|
|| { echo "bat config is not idempotent"; exit 1; }
|
||||||
|
'
|
||||||
|
;;
|
||||||
|
zsh.sh)
|
||||||
|
run_test "$script" '
|
||||||
|
# pre-seed a config so BOTH runs have something to back up
|
||||||
|
printf "# preexisting zshrc marker\n" > /root/.zshrc
|
||||||
|
bash zsh.sh --skip-watermark
|
||||||
|
[ -f /root/.zshrc ] || { echo ".zshrc missing"; exit 1; }
|
||||||
|
[ -d /root/.oh-my-zsh/custom/plugins/zsh-autosuggestions ] || { echo "plugins missing"; exit 1; }
|
||||||
|
n=$(find /root -maxdepth 1 -name ".zshrc.bak-*" | wc -l)
|
||||||
|
[ "$n" -eq 1 ] || { echo "expected 1 backup after first run, got $n"; exit 1; }
|
||||||
|
grep -q "preexisting zshrc marker" /root/.zshrc.bak-* \
|
||||||
|
|| { echo "first backup does not hold the original config"; exit 1; }
|
||||||
|
# second run must rotate (new timestamped backup), not overwrite
|
||||||
|
bash zsh.sh --skip-watermark
|
||||||
|
n=$(find /root -maxdepth 1 -name ".zshrc.bak-*" | wc -l)
|
||||||
|
[ "$n" -eq 2 ] || { echo "expected 2 backups after second run (rotation), got $n"; exit 1; }
|
||||||
|
grep -q "preexisting zshrc marker" /root/.zshrc.bak-* \
|
||||||
|
|| { echo "second run destroyed an earlier backup"; exit 1; }
|
||||||
|
'
|
||||||
|
;;
|
||||||
|
fish.sh)
|
||||||
|
run_test "$script" '
|
||||||
|
export SKIP_END4_SETUP=1
|
||||||
|
bash fish.sh --skip-watermark
|
||||||
|
[ -f /root/.config/fish/config.fish ] || { echo "fish config not installed"; exit 1; }
|
||||||
|
fish -c "type -q fisher" || { echo "fisher not installed in fish"; exit 1; }
|
||||||
|
# the fixed prerequisite check must have installed fish itself
|
||||||
|
command -v fish >/dev/null || { echo "fish missing"; exit 1; }
|
||||||
|
'
|
||||||
|
;;
|
||||||
|
LazyVim.sh)
|
||||||
|
run_test "$script" '
|
||||||
|
bash LazyVim.sh
|
||||||
|
[ -d /root/.config/nvim ] || { echo "nvim config not cloned"; exit 1; }
|
||||||
|
command -v node >/dev/null || { echo "no node after install"; exit 1; }
|
||||||
|
# timestamped backup only if a config existed; none here, so just assert
|
||||||
|
# the clone exists and nothing is named plain nvim.bak
|
||||||
|
[ ! -e /root/.config/nvim.bak ] || { echo "legacy .bak name used"; exit 1; }
|
||||||
|
'
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "error: unknown smoke test '$script' (known: bat.sh zsh.sh fish.sh LazyVim.sh)" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== smoke summary ==="
|
||||||
|
printf '%s\n' "${RESULTS[@]}"
|
||||||
|
exit "$overall"
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
|
||||||
# Detect fish shell and ask user to switch to bash (bash-only syntax)
|
# Detect fish shell and ask user to switch to bash (bash-only syntax)
|
||||||
if [ -n "${FISH_VERSION:-}" ] || case "$SHELL" in *fish*) ;; *) false ;; esac; then
|
if [ -n "${FISH_VERSION:-}" ] || case "${SHELL:-}" in *fish*) ;; *) false ;; esac; then
|
||||||
echo "Warning: fish shell detected."
|
echo "Warning: fish shell detected."
|
||||||
echo "This script is written for bash and may have syntax issues under fish."
|
echo "This script is written for bash and may have syntax issues under fish."
|
||||||
echo "Please switch to bash first and re-run it, e.g.: bash $0"
|
echo "Please switch to bash first and re-run it, e.g.: bash $0"
|
||||||
|
if [ -t 0 ] && [ -e /dev/tty ]; then
|
||||||
read -r -p "Press Enter to continue anyway, or Ctrl+C to cancel..." </dev/tty || true
|
read -r -p "Press Enter to continue anyway, or Ctrl+C to cancel..." </dev/tty || true
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
skip_watermark=false
|
skip_watermark=false
|
||||||
if [ "$1" = "--skip-watermark" ]; then
|
if [ "${1:-}" = "--skip-watermark" ]; then
|
||||||
skip_watermark=true
|
skip_watermark=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -23,8 +25,8 @@ echo '
|
|||||||
|___/
|
|___/
|
||||||
|
|
||||||
--------------- ZSH Install Script ---------------
|
--------------- ZSH Install Script ---------------
|
||||||
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE UNDER THE GPL-2.0 LICENCE, THERE IS NO WARRANTY
|
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE UNDER THE GPL-2.0 LICENSE, THERE IS NO WARRANTY
|
||||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. See the LICENCE for more detail
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. See the LICENSE for more detail
|
||||||
'
|
'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -34,11 +36,11 @@ echo "This script will backup your current zsh config if it exists "
|
|||||||
# Enable exit on error
|
# Enable exit on error
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
# Install prerequisites if installed skips
|
# Install prerequisites if any of them is missing
|
||||||
if ! command -v zsh >/dev/null && command -v git >/dev/null && command -v curl >/dev/null && command -v fzf >/dev/null; then
|
if ! command -v zsh >/dev/null || ! command -v git >/dev/null || ! command -v curl >/dev/null || ! command -v fzf >/dev/null; then
|
||||||
if command -v pacman >/dev/null; then
|
if command -v pacman >/dev/null; then
|
||||||
echo "pacman detected. Installing prerequisites"
|
echo "pacman detected. Installing prerequisites"
|
||||||
sudo pacman -S zsh git curl fzf --noconfirm
|
sudo pacman -S --needed --noconfirm zsh git curl fzf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v apt >/dev/null; then
|
if command -v apt >/dev/null; then
|
||||||
@@ -47,7 +49,13 @@ if ! command -v zsh >/dev/null && command -v git >/dev/null && command -v curl >
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
zsh=$(which zsh)
|
if ! command -v zsh >/dev/null; then
|
||||||
|
echo "error: zsh could not be installed (no supported package manager, or install failed)." >&2
|
||||||
|
echo "Install zsh manually, then re-run this script." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
zsh=$(command -v zsh)
|
||||||
|
|
||||||
# Do not print commands
|
# Do not print commands
|
||||||
set +x
|
set +x
|
||||||
@@ -59,82 +67,119 @@ echo '
|
|||||||
# Print commands
|
# Print commands
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
# Disable exit on error
|
# Remove an existing oh-my-zsh install, but only when it really is one
|
||||||
set +eu
|
if [ -d "$HOME/.oh-my-zsh" ]; then
|
||||||
|
if [ -f "$HOME/.oh-my-zsh/oh-my-zsh.sh" ]; then
|
||||||
rm -rf ~/.oh-my-zsh
|
echo "Removing existing ~/.oh-my-zsh for a clean install"
|
||||||
|
rm -rf "$HOME/.oh-my-zsh"
|
||||||
# Enable exit on error
|
else
|
||||||
set -eu
|
echo "warning: ~/.oh-my-zsh exists but does not look like an oh-my-zsh install - leaving it in place" >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Do not print commands
|
# Do not print commands
|
||||||
set +x
|
set +x
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
BACKUP_STAMP="$(date +%Y%m%d-%H%M%S)"
|
||||||
|
for cfg in .zshrc .zshenv; do
|
||||||
|
if [ -f "$HOME/$cfg" ]; then
|
||||||
|
cfg_backup="$HOME/$cfg.bak-$BACKUP_STAMP"
|
||||||
|
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
|
||||||
|
done
|
||||||
|
|
||||||
# Install oh-my-zsh without entering zsh
|
# Install oh-my-zsh without entering zsh
|
||||||
CHSH=no sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
|
if ! omz_installer=$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh); then
|
||||||
|
echo "error: could not download the oh-my-zsh installer" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
CHSH=no sh -c "$omz_installer" "" --unattended
|
||||||
|
|
||||||
# Print commands
|
# Print commands
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
# Install Oh-My-Zsh plugins
|
# Install Oh-My-Zsh plugins
|
||||||
git clone https://github.com/zsh-users/zsh-history-substring-search.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-history-substring-search
|
# Note: use $HOME, not ~ inside ${VAR:-...} — tilde is not expanded there,
|
||||||
git clone https://github.com/zsh-users/zsh-autosuggestions.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
|
# which would clone into a literal ./~ directory.
|
||||||
git clone https://github.com/z-shell/zsh-eza.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-eza
|
ZSH_CUSTOM_DIR="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
|
||||||
git clone --depth 1 https://github.com/unixorn/fzf-zsh-plugin.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/fzf-zsh-plugin
|
clone_plugin() {
|
||||||
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
|
local dest="$1"
|
||||||
git clone --depth 1 https://github.com/marlonrichert/zsh-autocomplete.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autocomplete
|
shift
|
||||||
|
if ! git clone "$@" "$dest"; then
|
||||||
|
echo "warning: failed to clone into $dest (skipping)" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
clone_plugin "$ZSH_CUSTOM_DIR/plugins/zsh-history-substring-search" https://github.com/zsh-users/zsh-history-substring-search.git
|
||||||
|
clone_plugin "$ZSH_CUSTOM_DIR/plugins/zsh-autosuggestions" https://github.com/zsh-users/zsh-autosuggestions.git
|
||||||
|
clone_plugin "$ZSH_CUSTOM_DIR/plugins/zsh-eza" https://github.com/z-shell/zsh-eza.git
|
||||||
|
clone_plugin "$ZSH_CUSTOM_DIR/plugins/fzf-zsh-plugin" --depth=1 https://github.com/unixorn/fzf-zsh-plugin.git
|
||||||
|
clone_plugin "$ZSH_CUSTOM_DIR/plugins/zsh-syntax-highlighting" https://github.com/zsh-users/zsh-syntax-highlighting.git
|
||||||
|
clone_plugin "$ZSH_CUSTOM_DIR/plugins/zsh-autocomplete" --depth=1 https://github.com/marlonrichert/zsh-autocomplete.git
|
||||||
|
|
||||||
|
|
||||||
# Disable exit on error
|
|
||||||
set +eu
|
|
||||||
|
|
||||||
# Backup old config file if it exists
|
|
||||||
cp ~/.zshrc ~/.zshrc{,.bak} -r
|
|
||||||
|
|
||||||
# Enable exit on error
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
# 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
|
# Setup fzf: the oh-my-zsh fzf-zsh-plugin provides the integration.
|
||||||
mkdir -p ~/.fzf/shell
|
# (Previously an empty ~/.fzf/shell/key-bindings.zsh placeholder was created
|
||||||
touch ~/.fzf/shell/key-bindings.zsh
|
# here - nothing ever sourced it, and it would shadow real bindings.)
|
||||||
|
|
||||||
# Do not print commands
|
# Do not print commands
|
||||||
set +x
|
set +x
|
||||||
|
|
||||||
# If this user's login shell is already "zsh", do not attempt to switch.
|
# If this user's login shell is already "zsh", do not attempt to switch.
|
||||||
if [ "$(basename -- "$SHELL")" = "zsh" ]; then
|
user_name="${USER:-${LOGNAME:-$(id -un)}}"
|
||||||
|
if command -v getent >/dev/null 2>&1; then
|
||||||
|
login_shell="$(getent passwd "$user_name" | awk -F: '{print $7}' | tr -d '\n')"
|
||||||
|
else
|
||||||
|
login_shell="$(grep -E "^${user_name}:" /etc/passwd | awk -F: '{print $7}' | tr -d '\n' || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${login_shell:-}" ]; then
|
||||||
|
login_shell="${SHELL:-}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(basename -- "${login_shell:-}")" = "zsh" ]; then
|
||||||
echo "You already have zsh as the default shell"
|
echo "You already have zsh as the default shell"
|
||||||
echo "Successfully installed zsh configuration"
|
echo "Successfully installed zsh configuration"
|
||||||
exit 0 # Exit as success
|
exit 0 # Exit as success
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v chsh >/dev/null; then
|
if ! command -v chsh >/dev/null; then
|
||||||
echo "chsh command does not exist."
|
echo "chsh command does not exist."
|
||||||
echo "Please change your shell manually"
|
echo "Please change your shell manually:"
|
||||||
sleep 5 && exit 0
|
echo "chsh -s \"$zsh\""
|
||||||
|
echo "Successfully installed zsh configuration"
|
||||||
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Changing your shell to $zsh..."
|
echo "Changing your shell to $zsh..."
|
||||||
|
|
||||||
if ! echo "+ sudo -k chsh -s "$zsh" "$USER"" && sudo -k chsh -s "$zsh" "$USER"; then # -k forces password prompt
|
echo "+ sudo -k chsh -s \"$zsh\" \"$user_name\""
|
||||||
echo "Next command may fail."
|
if sudo -k chsh -s "$zsh" "$user_name"; then # -k forces password prompt
|
||||||
echo "+ chsh -s \"$(which zsh)\" \"$USER\""
|
|
||||||
chsh -s "$(which zsh)" "$USER" # run chsh normally may fail
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if the shell change was successful
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "chsh command unsuccessful. Change your default shell manually:"
|
|
||||||
echo "chsh -s \"$(which zsh)\" \"$USER\""
|
|
||||||
else
|
|
||||||
export SHELL="$zsh"
|
export SHELL="$zsh"
|
||||||
echo "Shell successfully changed to '$zsh'."
|
echo "Shell successfully changed to '$zsh'."
|
||||||
|
else
|
||||||
|
echo "sudo chsh failed. Trying without sudo (this may fail):"
|
||||||
|
echo "+ chsh -s \"$zsh\" \"$user_name\""
|
||||||
|
if chsh -s "$zsh" "$user_name"; then
|
||||||
|
export SHELL="$zsh"
|
||||||
|
echo "Shell successfully changed to '$zsh'."
|
||||||
|
else
|
||||||
|
echo "chsh command unsuccessful. Change your default shell manually:"
|
||||||
|
echo "chsh -s \"$zsh\" \"$user_name\""
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Successfully installed zsh"
|
echo "Successfully installed zsh"
|
||||||
|
|||||||
Reference in New Issue
Block a user