330 lines
14 KiB
Bash
Executable File
330 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# This is a metascript called by parent scripts to verify the execution environment.
|
|
# It checks if the current shell is bash or not. If not, it terminates execution.
|
|
|
|
if [ -z "${BASH_VERSION:-}" ]; then
|
|
echo "Error: This script must be run using bash." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Detect if the script is sourced
|
|
is_sourced=false
|
|
if [ -n "${BASH_SOURCE[0]:-}" ] && [ "${BASH_SOURCE[0]}" != "$0" ]; then
|
|
is_sourced=true
|
|
fi
|
|
# Detect eval from installers based on presence of specific variables
|
|
if [ -n "${METASCRIPT_URL:-}" ]; then
|
|
is_sourced=true
|
|
fi
|
|
|
|
# Locate or download libraries so that sourced installers can use them
|
|
BOOTSTRAP_DIR="${BOOTSTRAP_DIR:-$HOME/.config/bootstrap}"
|
|
_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd 2>/dev/null || pwd)"
|
|
|
|
if [ -f "$_SCRIPT_DIR/lib/common.sh" ]; then
|
|
# Dev/local mode: source directly from repo
|
|
BOOTSTRAP_SOURCE_DIR="$_SCRIPT_DIR"
|
|
elif [ -f "$BOOTSTRAP_DIR/lib/common.sh" ]; then
|
|
# Installed mode: source from bootstrap dir
|
|
BOOTSTRAP_SOURCE_DIR="$BOOTSTRAP_DIR"
|
|
else
|
|
# Standalone/remote mode: download to a temp directory and source
|
|
export BOOTSTRAP_TMP_DIR
|
|
BOOTSTRAP_TMP_DIR="$(mktemp -d)"
|
|
trap 'rm -rf "$BOOTSTRAP_TMP_DIR"' EXIT
|
|
BOOTSTRAP_SOURCE_DIR="$BOOTSTRAP_TMP_DIR"
|
|
|
|
_BASE_URL="https://git.adityagupta.dev/sortedcord/bootstrap/raw/branch/master"
|
|
_LIBS=("lib/common.sh" "lib/rollback.sh" "lib/platform.sh" "lib/shell_config.sh" "lib/plugins.sh" "lib/registry_helpers.sh" "lib/github.sh")
|
|
|
|
_curl_args=()
|
|
for _lib in "${_LIBS[@]}"; do
|
|
mkdir -p "$BOOTSTRAP_TMP_DIR/$(dirname "$_lib")"
|
|
_curl_args+=("-o" "$BOOTSTRAP_TMP_DIR/$_lib" "$_BASE_URL/$_lib")
|
|
done
|
|
curl -fsSL "${_curl_args[@]}" 2>/dev/null
|
|
fi
|
|
|
|
if [ -f "$BOOTSTRAP_SOURCE_DIR/lib/common.sh" ]; then
|
|
. "$BOOTSTRAP_SOURCE_DIR/lib/common.sh"
|
|
. "$BOOTSTRAP_SOURCE_DIR/lib/rollback.sh"
|
|
. "$BOOTSTRAP_SOURCE_DIR/lib/platform.sh"
|
|
. "$BOOTSTRAP_SOURCE_DIR/lib/shell_config.sh"
|
|
. "$BOOTSTRAP_SOURCE_DIR/lib/registry_helpers.sh"
|
|
. "$BOOTSTRAP_SOURCE_DIR/lib/github.sh"
|
|
init_rollback_system
|
|
else
|
|
echo "Error: Failed to locate or download bootstrap libraries." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Install/update the bootstrap loader and download all necessary files
|
|
install_bootstrap() {
|
|
local target_files=()
|
|
[ -f "$HOME/.bashrc" ] && target_files+=("$HOME/.bashrc")
|
|
|
|
local routes_dir="$HOME/.config/bootstrap"
|
|
mkdir -p "$routes_dir"
|
|
mkdir -p "$routes_dir/env.d"
|
|
mkdir -p "$routes_dir/aliases.d"
|
|
|
|
# List of all files to download/copy
|
|
local files=(
|
|
"VERSION"
|
|
"b.sh"
|
|
"lib/routes.sh"
|
|
"lib/registry.sh"
|
|
"lib/common.sh"
|
|
"lib/rollback.sh"
|
|
"lib/platform.sh"
|
|
"lib/shell_config.sh"
|
|
"lib/registry_helpers.sh"
|
|
"lib/github.sh"
|
|
"lib/plugins.sh"
|
|
"commands/help.sh"
|
|
"commands/con.sh"
|
|
"commands/uninstall.sh"
|
|
"commands/up.sh"
|
|
)
|
|
|
|
if ! pkg_check jq >/dev/null 2>&1; then
|
|
log_info "jq is missing. Installing jq..."
|
|
pkg_install jq
|
|
fi
|
|
|
|
if [ -f "$_SCRIPT_DIR/b.sh" ] && [ -f "$_SCRIPT_DIR/lib/routes.sh" ]; then
|
|
log_info "Using local files from repository..."
|
|
for file in "${files[@]}"; do
|
|
mkdir -p "$(dirname "$routes_dir/$file")"
|
|
if [ -f "$_SCRIPT_DIR/$file" ]; then
|
|
cp "$_SCRIPT_DIR/$file" "$routes_dir/$file"
|
|
fi
|
|
done
|
|
|
|
# Also copy installers if they exist locally
|
|
if [ -d "$_SCRIPT_DIR/installers" ]; then
|
|
mkdir -p "$routes_dir/installers"
|
|
cp -r "$_SCRIPT_DIR/installers/"* "$routes_dir/installers/"
|
|
fi
|
|
else
|
|
log_info "Downloading bootstrap scripts..."
|
|
local base_url="https://git.adityagupta.dev/sortedcord/bootstrap/raw/branch/master"
|
|
|
|
local curl_args=()
|
|
for file in "${files[@]}"; do
|
|
mkdir -p "$(dirname "$routes_dir/$file")"
|
|
curl_args+=("-o" "$routes_dir/$file" "$base_url/$file")
|
|
done
|
|
if ! curl -fsSL "${curl_args[@]}"; then
|
|
log_error "Failed to download bootstrap scripts."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Create ~/.bash_aliases if it doesn't exist
|
|
if [ ! -f "$HOME/.bash_aliases" ]; then
|
|
log_info "Creating ~/.bash_aliases..."
|
|
touch "$HOME/.bash_aliases"
|
|
fi
|
|
|
|
# Set up shell configuration files
|
|
for config_file in "${target_files[@]}"; do
|
|
# 1. Clean up old embedded function block if it exists (from previous setup)
|
|
remove_block "$config_file" "bootstrap-cli b function"
|
|
|
|
# 2. Clean up old loader block if it exists
|
|
remove_block "$config_file" "bootstrap-cli setup"
|
|
|
|
# 3. Append the new lightweight loader block that sources modular configs
|
|
log_info "Adding bootstrap loader to $config_file..."
|
|
cat << 'EOF' >> "$config_file"
|
|
|
|
# >>> bootstrap-cli setup >>>
|
|
export BOOTSTRAP_DIR="$HOME/.config/bootstrap"
|
|
[ -f "$BOOTSTRAP_DIR/b.sh" ] && . "$BOOTSTRAP_DIR/b.sh"
|
|
for f in "$BOOTSTRAP_DIR/env.d/"*.sh; do [ -r "$f" ] && . "$f"; done
|
|
for f in "$BOOTSTRAP_DIR/aliases.d/"*.sh; do [ -r "$f" ] && . "$f"; done
|
|
# <<< bootstrap-cli setup <<<
|
|
EOF
|
|
|
|
# 4. Ensure ~/.bash_aliases is sourced in ~/.bashrc if not already
|
|
if [ "$config_file" = "$HOME/.bashrc" ]; then
|
|
if ! grep -q "bash_aliases" "$config_file" 2>/dev/null; then
|
|
local alias_source_content
|
|
alias_source_content=$(cat << 'EOF'
|
|
# Source aliases file if it exists
|
|
if [ -f "$HOME/.bash_aliases" ]; then
|
|
. "$HOME/.bash_aliases"
|
|
fi
|
|
EOF
|
|
)
|
|
inject_block "$config_file" "bootstrap-cli bash_aliases setup" "$alias_source_content"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Initialize the last update timestamp to prevent immediate update on first execution (Fix 2)
|
|
local last_update_file="$routes_dir/.last_b_update"
|
|
date +%s 2>/dev/null > "$last_update_file" || date +%s > "$last_update_file"
|
|
|
|
# Set up pre-commit hook if in a git repository locally
|
|
if [ -d "$_SCRIPT_DIR/.git" ]; then
|
|
log_info "Setting up git pre-commit hook..."
|
|
mkdir -p "$_SCRIPT_DIR/.git/hooks"
|
|
if [ -f "$_SCRIPT_DIR/scripts/pre-commit" ]; then
|
|
cp "$_SCRIPT_DIR/scripts/pre-commit" "$_SCRIPT_DIR/.git/hooks/pre-commit"
|
|
chmod +x "$_SCRIPT_DIR/.git/hooks/pre-commit"
|
|
log_success "Pre-commit hook installed to .git/hooks/pre-commit"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Only execute installation if not sourced (Fix 3)
|
|
if [ "$is_sourced" = false ]; then
|
|
clear 2>/dev/null || true
|
|
|
|
# Locate or download pixel_art.ansi and VERSION
|
|
_art_file="$BOOTSTRAP_SOURCE_DIR/assets/pixel_art.ansi"
|
|
_version_file="$BOOTSTRAP_SOURCE_DIR/VERSION"
|
|
|
|
if [ ! -f "$_art_file" ] && [ -n "${BOOTSTRAP_TMP_DIR:-}" ]; then
|
|
_base_url="${_BASE_URL:-https://git.adityagupta.dev/sortedcord/bootstrap/raw/branch/master}"
|
|
mkdir -p "$(dirname "$_art_file")"
|
|
curl -fsSL -o "$_art_file" "$_base_url/assets/pixel_art.ansi" 2>/dev/null || true
|
|
curl -fsSL -o "$_version_file" "$_base_url/VERSION" 2>/dev/null || true
|
|
fi
|
|
|
|
if [ -f "$_art_file" ]; then
|
|
# Calculate terminal width and center the logo
|
|
_cols=""
|
|
if command -v tput >/dev/null 2>&1; then
|
|
_cols=$(tput cols 2>/dev/null)
|
|
fi
|
|
if [ -z "$_cols" ] || ! [ "$_cols" -gt 0 ] 2>/dev/null; then
|
|
if command -v stty >/dev/null 2>&1; then
|
|
_cols=$(stty size 2>/dev/null | cut -d' ' -f2)
|
|
fi
|
|
fi
|
|
if [ -z "$_cols" ] || ! [ "$_cols" -gt 0 ] 2>/dev/null; then
|
|
_cols="${COLUMNS:-0}"
|
|
fi
|
|
|
|
_logo_width=64
|
|
if [ -n "$_cols" ] && [ "$_cols" -ge "$_logo_width" ] 2>/dev/null; then
|
|
_padding_width=$(( (_cols - _logo_width) / 2 ))
|
|
_padding=""
|
|
if [ "$_padding_width" -gt 0 ]; then
|
|
_padding=$(printf "%${_padding_width}s" "")
|
|
fi
|
|
sed "s/^/$_padding/" "$_art_file"
|
|
echo
|
|
cat << 'EOF' | sed "s/^/$_padding/"
|
|
▀█████████▄ ▄██████▄ ▄██████▄ ███
|
|
███ ███ ███ ███ ███ ███ ▀█████████▄
|
|
███ ███ ███ ███ ███ ███ ▀███▀▀██
|
|
▄███▄▄▄██▀ ███ ███ ███ ███ ███ ▀
|
|
▀▀███▀▀▀██▄ ███ ███ ███ ███ ███
|
|
███ ██▄ ███ ███ ███ ███ ███
|
|
███ ███ ███ ███ ███ ███ ███
|
|
▄█████████▀ ▀██████▀ ▀██████▀ ▄████▀
|
|
|
|
▄████████ ███ ▄████████ ▄████████ ▄███████▄
|
|
███ ███ ▀█████████▄ ███ ███ ███ ███ ███ ███
|
|
███ █▀ ▀███▀▀██ ███ ███ ███ ███ ███ ███
|
|
███ ███ ▀ ▄███▄▄▄▄██▀ ███ ███ ███ ███
|
|
▀███████████ ███ ▀▀███▀▀▀▀▀ ▀███████████ ▀█████████▀
|
|
███ ███ ▀███████████ ███ ███ ███
|
|
▄█ ███ ███ ███ ███ ███ ███ ███
|
|
▄████████▀ ▄████▀ ███ ███ ███ █▀ ▄████▀
|
|
███ ███
|
|
EOF
|
|
echo
|
|
|
|
# Display centered version of bootstrap in bold
|
|
_version=""
|
|
if [ -f "$_version_file" ]; then
|
|
_version=$(cat "$_version_file" | tr -d '\r\n')
|
|
fi
|
|
if [ -z "$_version" ]; then
|
|
_version="0.0.0" # Fallback if VERSION missing
|
|
fi
|
|
_version_str="v$_version"
|
|
_version_len=${#_version_str}
|
|
_version_padding_width=$(( (_cols - _version_len) / 2 ))
|
|
_version_padding=""
|
|
if [ "$_version_padding_width" -gt 0 ]; then
|
|
_version_padding=$(printf "%${_version_padding_width}s" "")
|
|
fi
|
|
printf "%s\033[1m%s\033[0m\n" "$_version_padding" "$_version_str"
|
|
echo
|
|
|
|
# Display centered aqua blue progress bar (non-blocking)
|
|
_bar_width=40
|
|
_bar_padding_width=$(( (_cols - (_bar_width + 6)) / 2 ))
|
|
_bar_padding=""
|
|
if [ "$_bar_padding_width" -gt 0 ]; then
|
|
_bar_padding=$(printf "%${_bar_padding_width}s" "")
|
|
fi
|
|
|
|
_filled_all="████████████████████████████████████████"
|
|
_empty_all="░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░"
|
|
|
|
# Hide cursor
|
|
printf "\033[?25l"
|
|
|
|
# Capture install output to a temp file so logs don't bleed into the bar
|
|
_install_log=$(mktemp)
|
|
|
|
# Launch progress bar in background
|
|
(
|
|
for i in $(seq 1 40); do
|
|
_filled_part="${_filled_all:0:i}"
|
|
_empty_part="${_empty_all:i}"
|
|
_pct=$(( i * 100 / 40 ))
|
|
printf "\r%s\033[38;2;0;210;255m%s\033[90m%s\033[0m %3d%%" "$_bar_padding" "$_filled_part" "$_empty_part" "$_pct"
|
|
sleep 0.05
|
|
done
|
|
) &
|
|
_bar_pid=$!
|
|
|
|
# Run installation concurrently, silencing all output to the log file
|
|
install_bootstrap >"$_install_log" 2>&1
|
|
|
|
# Wait for the progress bar to finish
|
|
wait "$_bar_pid" 2>/dev/null
|
|
|
|
# Snap to 100% in case install finished before the bar
|
|
printf "\r%s\033[38;2;0;210;255m%s\033[0m %3d%%" "$_bar_padding" "$_filled_all" 100
|
|
|
|
# Restore cursor, clear screen, then replay captured install logs
|
|
printf "\033[?25h\n"
|
|
clear 2>/dev/null || true
|
|
cat "$_install_log"
|
|
rm -f "$_install_log"
|
|
_bootstrap_installed=true
|
|
fi
|
|
fi
|
|
if [ "${_bootstrap_installed:-false}" != true ]; then
|
|
install_bootstrap
|
|
fi
|
|
|
|
# Load the b function immediately in the current subshell
|
|
if [ -f "$HOME/.config/bootstrap/b.sh" ]; then
|
|
. "$HOME/.config/bootstrap/b.sh"
|
|
fi
|
|
|
|
# Handle sourcing the shell configuration file or printing instructions (Fix 1)
|
|
echo
|
|
log_success "Bootstrap CLI installed successfully!"
|
|
log_info "To start using the 'b' command in this terminal session, run:"
|
|
echo " source ~/.bashrc"
|
|
else
|
|
# Sourced mode (e.g., when sourced by installers or manually by user)
|
|
# Load the b function in the current shell context
|
|
if [ -f "$HOME/.config/bootstrap/b.sh" ]; then
|
|
. "$HOME/.config/bootstrap/b.sh"
|
|
fi
|
|
fi
|
|
|