feat: initial project scaffold for lxs multi-tool
- add main entrypoint with interactive menu and CLI dispatcher (lxs.sh) - add shared helpers library with colors, loggers, and spinner (lib/common.sh) - add app installers for coolify, pterodactyl, uptime-kuma, cloudpanel, and proxmox (apps/) - add system tools for monitoring, benchmarking, and hardening (tools/) - add VERSION file (0.1.0) as single source of truth for releases - add MIT LICENSE - expand README with usage, project structure, and release workflow
This commit is contained in:
+151
@@ -0,0 +1,151 @@
|
||||
#!/bin/bash
|
||||
|
||||
# LXS - Common library
|
||||
# Sourced by lxs.sh and all sub-scripts. Provides colors, UI helpers, loggers,
|
||||
# spinner, OS guards, and shared utilities (public IP, password generation,
|
||||
# apt lock wait, root re-exec, apt non-interactive setup).
|
||||
# Repo: https://git.hyko.cx/hykocx/lxs
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# Colors
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
PURPLE='\033[1;94m'
|
||||
CYAN='\033[0;36m'
|
||||
WHITE='\033[1;37m'
|
||||
GRAY='\033[0;37m'
|
||||
NC='\033[0m'
|
||||
BOLD='\033[1m'
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# UI helpers
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
show_separator() {
|
||||
echo -e "${GRAY}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
}
|
||||
|
||||
info() { echo -e "${PURPLE}[*]${NC} $*"; }
|
||||
ok() { echo -e "${GREEN}[✓]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||
err() { echo -e "${RED}[✗]${NC} $*" >&2; }
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# Spinner — runs a shell command, redirects output to a log file, shows
|
||||
# a spinner with a label, prints ✓/✗ on completion. Returns the command's
|
||||
# exit code.
|
||||
#
|
||||
# Usage:
|
||||
# run_spinner "Installing foo..." "apt-get install -y foo"
|
||||
#
|
||||
# Log file: ${LXS_LOG_FILE:-/tmp/lxs.log}
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
run_spinner() {
|
||||
local message=$1
|
||||
shift
|
||||
local command="$*"
|
||||
local spinstr='|/-\'
|
||||
local log="${LXS_LOG_FILE:-/tmp/lxs.log}"
|
||||
|
||||
echo -e "${PURPLE}[*] ${message}${NC}"
|
||||
eval "$command" > "$log" 2>&1 &
|
||||
local pid=$!
|
||||
|
||||
while kill -0 "$pid" 2>/dev/null; do
|
||||
local temp=${spinstr#?}
|
||||
printf "\r${PURPLE}[%c]${NC} ${message}" "$spinstr"
|
||||
spinstr=$temp${spinstr%"$temp"}
|
||||
sleep 0.15
|
||||
done
|
||||
|
||||
wait "$pid"
|
||||
local exit_code=$?
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
printf "\r${GREEN}[✓]${NC} ${message}\n"
|
||||
else
|
||||
printf "\r${RED}[✗]${NC} ${message}\n"
|
||||
fi
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# Guards
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
require_debian_ubuntu() {
|
||||
if ! grep -qiE 'debian|ubuntu' /etc/os-release 2>/dev/null; then
|
||||
err "This script supports Debian/Ubuntu only."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Re-exec the current script via sudo if not already root. Preserves env and
|
||||
# arguments. Call near the top of a sub-script:
|
||||
# require_root "$0" "$@"
|
||||
require_root() {
|
||||
[ "$EUID" -eq 0 ] && return 0
|
||||
local self=$1
|
||||
shift
|
||||
exec sudo -E "$self" "$@"
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# Network / random / apt helpers
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
get_public_ip() {
|
||||
local ip=""
|
||||
ip=$(curl -s --max-time 5 ifconfig.me 2>/dev/null) || \
|
||||
ip=$(curl -s --max-time 5 icanhazip.com 2>/dev/null) || \
|
||||
ip=$(curl -s --max-time 5 ipinfo.io/ip 2>/dev/null) || \
|
||||
ip=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||
echo "$ip"
|
||||
}
|
||||
|
||||
# Generate a random alphanumeric string. Default length: 32.
|
||||
generate_password() {
|
||||
local length=${1:-32}
|
||||
LC_ALL=C tr -dc 'a-zA-Z0-9' </dev/urandom | head -c "$length"
|
||||
}
|
||||
|
||||
# Configure apt/debconf for fully non-interactive runs. Safe to call multiple
|
||||
# times. No-op when apt is absent.
|
||||
apt_noninteractive() {
|
||||
command -v apt >/dev/null 2>&1 || return 0
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
export NEEDRESTART_MODE=a
|
||||
export NEEDRESTART_SUSPEND=1
|
||||
echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Wait for other apt/dpkg processes to release their locks. Up to 120s.
|
||||
wait_for_apt() {
|
||||
command -v apt >/dev/null 2>&1 || return 0
|
||||
|
||||
local max_wait=120
|
||||
local wait_count=0
|
||||
local lock_detected=false
|
||||
|
||||
while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 || \
|
||||
fuser /var/lib/dpkg/lock >/dev/null 2>&1 || \
|
||||
fuser /var/lib/apt/lists/lock >/dev/null 2>&1; do
|
||||
|
||||
[ "$lock_detected" = false ] && lock_detected=true && \
|
||||
echo -e "${YELLOW}[!] Waiting for other package manager to finish...${NC}"
|
||||
|
||||
wait_count=$((wait_count + 1))
|
||||
[ $wait_count -ge $max_wait ] && \
|
||||
echo -e "${RED}[✗] Timeout waiting for package manager${NC}" && return 1
|
||||
|
||||
printf "\r${GRAY}[i] Waiting... (%ds/%ds)${NC}" $wait_count $max_wait
|
||||
sleep 1
|
||||
done
|
||||
|
||||
[ "$lock_detected" = true ] && \
|
||||
echo -e "\n${GREEN}[✓] Package manager is now available${NC}"
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user