Compare commits

..

6 Commits

Author SHA1 Message Date
FlintyLemming
d53788e2c6 fix: forward proxy env vars to sudo commands via psudo wrapper 2026-03-05 17:49:54 +08:00
FlintyLemming
e63cb83370 Merge branch 'main' of https://git.mitsea.com/FlintyLemming/scripts-public 2026-03-05 15:58:15 +08:00
FlintyLemming
ad203a093c add install-docker 2026-03-05 15:57:55 +08:00
FlintyLemming
5cd803b90c fix: read interactive input from /dev/tty for curl|bash runs 2026-03-03 23:23:51 +08:00
FlintyLemming
72fefab22d 🔧 chore(docker): force NYIST mirror and remove interactive prompt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 17:54:02 +08:00
FlintyLemming
ee752d4988 feat(menu): add interactive script launcher menu
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 17:53:59 +08:00
3 changed files with 159 additions and 36 deletions

View File

@@ -127,7 +127,7 @@ if [ -z "$REPO_FILE" ]; then
esac esac
fi fi
mirror='' mirror='nyist'
DRY_RUN=${DRY_RUN:-} DRY_RUN=${DRY_RUN:-}
REPO_ONLY=${REPO_ONLY:-0} REPO_ONLY=${REPO_ONLY:-0}
NO_AUTOSTART=${NO_AUTOSTART:-0} NO_AUTOSTART=${NO_AUTOSTART:-0}

100
linux-managements/menu.sh Normal file
View File

@@ -0,0 +1,100 @@
#!/bin/bash
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
_NAMES=(); _DESCS=(); _URLS=()
add() { _NAMES+=("$1"); _DESCS+=("$2"); _URLS+=("$3"); }
# Read from terminal even when script is run via `curl ... | bash`.
prompt_read() {
local __var_name="$1"
local __prompt="$2"
local __input=""
if [ -r /dev/tty ]; then
read -r -p "$__prompt" __input < /dev/tty
else
read -r -p "$__prompt" __input
fi
printf -v "$__var_name" '%s' "$__input"
}
# ════════════════════════════════════════════════════════════════════════════════
# CONFIGURATION — 在此处添加或修改脚本条目
# 用法: add "<显示名称>" "<脚本说明>" "<脚本 URL>"
# ════════════════════════════════════════════════════════════════════════════════
add "Linux 初始化配置" \
"配置 HTTP 代理、SSH 密钥、安装 Homebrew 及常用软件、配置 Fish Shell、同步 Dotfiles" \
"https://git.mitsea.com/FlintyLemming/scripts-public/raw/branch/main/linux-managements/setup.sh"
add "安装 Docker Engine中国大陆服务器" \
"自动检测发行版,经由南洋理工镜像源安装 Docker Engine、Compose、Buildx 等组件" \
"https://git.mitsea.com/FlintyLemming/scripts-public/raw/branch/main/linux-managements/install-docker.sh"
add "配置 Snapper 快照" \
"为 Btrfs 子卷配置 Snapper 自动快照,按天/周/月/年策略保留快照并启用 systemd 定时器" \
"https://git.mitsea.com/FlintyLemming/scripts-public/raw/branch/main/linux-managements/setup-snapper.sh"
# ════════════════════════════════════════════════════════════════════════════════
# 以下为脚本逻辑,无需修改
# ════════════════════════════════════════════════════════════════════════════════
draw_menu() {
echo -e "\n${BOLD}${BLUE}╔══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${BLUE}║ Linux Management Scripts ║${NC}"
echo -e "${BOLD}${BLUE}╚══════════════════════════════════════════════════════════════╝${NC}\n"
local total="${#_NAMES[@]}"
for (( i=0; i<total; i++ )); do
echo -e " ${BOLD}${CYAN}[$((i+1))]${NC} ${BOLD}${_NAMES[$i]}${NC}"
echo -e " ${YELLOW}${_DESCS[$i]}${NC}"
echo
done
echo -e " ${BOLD}${RED}[0]${NC} 退出\n"
}
run_script() {
local idx=$1
local name="${_NAMES[$idx]}"
local url="${_URLS[$idx]}"
echo -e "\n${GREEN}[OK]${NC} 正在执行: ${BOLD}$name${NC}"
echo -e "${CYAN}[INFO]${NC} 来源: $url\n"
echo -e "${BLUE}──────────────────────────────────────────────────────────────${NC}\n"
if command -v curl &>/dev/null; then
bash <(curl -fsSL "$url")
elif command -v wget &>/dev/null; then
bash <(wget -qO- "$url")
else
echo -e "${RED}[ERR]${NC} 未找到 curl 或 wget无法下载脚本。" >&2
exit 1
fi
}
main() {
local total="${#_NAMES[@]}"
while true; do
draw_menu
prompt_read choice "请选择要执行的脚本 [0-$total]: "
if [[ "$choice" == "0" ]]; then
echo -e "\n${CYAN}[INFO]${NC} 已退出。\n"
exit 0
fi
if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > total )); then
echo -e "\n${RED}[ERR]${NC} 无效选项,请输入 0 到 $total 之间的数字。\n"
continue
fi
run_script $(( choice - 1 ))
break
done
}
main

View File

@@ -11,6 +11,19 @@ warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERR]${NC} $*" >&2; } error() { echo -e "${RED}[ERR]${NC} $*" >&2; }
step() { echo -e "\n${BOLD}${BLUE}══ $* ${NC}"; } step() { echo -e "\n${BOLD}${BLUE}══ $* ${NC}"; }
# Read from terminal even when script is run via `curl ... | bash`.
prompt_read() {
local __var_name="$1"
local __prompt="$2"
local __input=""
if [ -r /dev/tty ]; then
read -r -p "$__prompt" __input < /dev/tty
else
read -r -p "$__prompt" __input
fi
printf -v "$__var_name" '%s' "$__input"
}
# ─── OS Detection ───────────────────────────────────────────────────────────── # ─── OS Detection ─────────────────────────────────────────────────────────────
detect_os() { detect_os() {
if [ -f /etc/os-release ]; then if [ -f /etc/os-release ]; then
@@ -43,11 +56,11 @@ detect_os() {
setup_proxy() { setup_proxy() {
step "HTTP Proxy" step "HTTP Proxy"
echo -e "Do you want to configure an HTTP proxy for this session? ${YELLOW}(helps with Homebrew downloads)${NC}" echo -e "Do you want to configure an HTTP proxy for this session? ${YELLOW}(helps with Homebrew downloads)${NC}"
read -rp "Configure proxy? [y/N] " ans prompt_read ans "Configure proxy? [y/N] "
case "$ans" in case "$ans" in
[Yy]*) [Yy]*)
while true; do while true; do
read -rp "Enter proxy URL (e.g. http://192.168.1.1:7890): " proxy_url prompt_read proxy_url "Enter proxy URL (e.g. http://192.168.1.1:7890): "
if [[ "$proxy_url" =~ ^https?://[^:]+:[0-9]+$ ]]; then if [[ "$proxy_url" =~ ^https?://[^:]+:[0-9]+$ ]]; then
break break
fi fi
@@ -65,6 +78,26 @@ setup_proxy() {
esac esac
} }
# ─── Proxy-aware sudo ─────────────────────────────────────────────────────────
# sudo's default env_reset policy strips proxy variables.
# This wrapper forwards them so that package managers / curl under sudo
# can reach the network through the configured proxy.
psudo() {
local -a env_args=()
[ -n "${http_proxy:-}" ] && env_args+=("http_proxy=$http_proxy")
[ -n "${https_proxy:-}" ] && env_args+=("https_proxy=$https_proxy")
[ -n "${HTTP_PROXY:-}" ] && env_args+=("HTTP_PROXY=$HTTP_PROXY")
[ -n "${HTTPS_PROXY:-}" ] && env_args+=("HTTPS_PROXY=$HTTPS_PROXY")
[ -n "${no_proxy:-}" ] && env_args+=("no_proxy=$no_proxy")
[ -n "${NO_PROXY:-}" ] && env_args+=("NO_PROXY=$NO_PROXY")
if [ ${#env_args[@]} -gt 0 ]; then
sudo env "${env_args[@]}" "$@"
else
sudo "$@"
fi
}
# ─── SSH Key Setup ──────────────────────────────────────────────────────────── # ─── SSH Key Setup ────────────────────────────────────────────────────────────
setup_ssh_key() { setup_ssh_key() {
step "SSH Key Configuration" step "SSH Key Configuration"
@@ -91,7 +124,7 @@ setup_ssh_key() {
local added=0 local added=0
while true; do while true; do
read -rp "Public key (or blank to finish): " pubkey prompt_read pubkey "Public key (or blank to finish): "
[[ -z "$pubkey" ]] && break [[ -z "$pubkey" ]] && break
if [[ "$pubkey" =~ ^(ssh-rsa|ssh-ed25519|ecdsa-sha2-nistp256|sk-ssh-ed25519) ]]; then if [[ "$pubkey" =~ ^(ssh-rsa|ssh-ed25519|ecdsa-sha2-nistp256|sk-ssh-ed25519) ]]; then
if grep -qF "$pubkey" ~/.ssh/authorized_keys 2>/dev/null; then if grep -qF "$pubkey" ~/.ssh/authorized_keys 2>/dev/null; then
@@ -119,10 +152,10 @@ setup_ssh_key() {
sudo_set_sshd() { sudo_set_sshd() {
local key="$1" val="$2" local key="$1" val="$2"
# Uncomment or add the line # Uncomment or add the line
if sudo grep -qE "^\s*#?\s*${key}\s" "$SSHD_CONF"; then if psudo grep -qE "^\s*#?\s*${key}\s" "$SSHD_CONF"; then
sudo sed -i -E "s|^\s*#?\s*(${key})\s+.*|\1 ${val}|" "$SSHD_CONF" psudo sed -i -E "s|^\s*#?\s*(${key})\s+.*|\1 ${val}|" "$SSHD_CONF"
else else
echo "${key} ${val}" | sudo tee -a "$SSHD_CONF" > /dev/null echo "${key} ${val}" | psudo tee -a "$SSHD_CONF" > /dev/null
fi fi
} }
@@ -138,7 +171,7 @@ setup_ssh_key() {
fi fi
# Restart SSH # Restart SSH
if sudo systemctl restart ssh 2>/dev/null || sudo systemctl restart sshd 2>/dev/null; then if psudo systemctl restart ssh 2>/dev/null || psudo systemctl restart sshd 2>/dev/null; then
success "SSH service restarted" success "SSH service restarted"
else else
warn "Could not restart SSH service automatically — please restart it manually" warn "Could not restart SSH service automatically — please restart it manually"
@@ -153,9 +186,9 @@ install_git() {
fi fi
info "Installing git via system package manager ..." info "Installing git via system package manager ..."
case "$DISTRO" in case "$DISTRO" in
aosc) sudo oma install -y git ;; aosc) psudo oma install -y git ;;
debian|ubuntu) sudo apt-get update -qq && sudo apt-get install -y git ;; debian|ubuntu) psudo apt-get update -qq && psudo apt-get install -y git ;;
fedora) sudo dnf install -y git ;; fedora) psudo dnf install -y git ;;
esac esac
success "git installed" success "git installed"
} }
@@ -227,7 +260,7 @@ install_packages() {
case "$DISTRO" in case "$DISTRO" in
aosc) aosc)
info "Installing packages via oma ..." info "Installing packages via oma ..."
sudo oma install -y git fish eza fastfetch btop docker docker-compose-plugin docker-buildx-plugin psudo oma install -y git fish eza fastfetch btop docker docker-compose docker-buildx
success "All packages installed via oma" success "All packages installed via oma"
;; ;;
debian|ubuntu) debian|ubuntu)
@@ -255,7 +288,7 @@ setup_fish() {
# Add fish to /etc/shells if not already present # Add fish to /etc/shells if not already present
if ! grep -qF "$FISH_PATH" /etc/shells; then if ! grep -qF "$FISH_PATH" /etc/shells; then
echo "$FISH_PATH" | sudo tee -a /etc/shells > /dev/null echo "$FISH_PATH" | psudo tee -a /etc/shells > /dev/null
success "Added $FISH_PATH to /etc/shells" success "Added $FISH_PATH to /etc/shells"
else else
info "$FISH_PATH already in /etc/shells" info "$FISH_PATH already in /etc/shells"
@@ -266,7 +299,7 @@ setup_fish() {
if [ "$current_shell" = "$FISH_PATH" ]; then if [ "$current_shell" = "$FISH_PATH" ]; then
info "fish is already the default shell" info "fish is already the default shell"
else else
sudo chsh -s "$FISH_PATH" "$USER" psudo chsh -s "$FISH_PATH" "$USER"
success "Default shell changed to fish ($FISH_PATH)" success "Default shell changed to fish ($FISH_PATH)"
fi fi
@@ -296,20 +329,9 @@ install_docker() {
if command -v docker &>/dev/null; then if command -v docker &>/dev/null; then
info "Docker already installed ($(docker --version)), skipping" info "Docker already installed ($(docker --version)), skipping"
else else
echo ""
echo -e "Use a ${BOLD}domestic mirror${NC} for Docker installation? ${YELLOW}(recommended in China)${NC}"
read -rp "Use mirror? [y/N] " use_mirror
case "$use_mirror" in
[Yy]*)
curl -fsSL https://git.mitsea.com/FlintyLemming/scripts-public/raw/branch/main/linux-managements/install-docker.sh \ curl -fsSL https://git.mitsea.com/FlintyLemming/scripts-public/raw/branch/main/linux-managements/install-docker.sh \
-o /tmp/install-docker.sh -o /tmp/install-docker.sh
sudo sh /tmp/install-docker.sh --mirror nyist psudo sh /tmp/install-docker.sh
;;
*)
curl -fsSL https://get.docker.com -o /tmp/get-docker.sh
sudo sh /tmp/get-docker.sh
;;
esac
success "Docker installed" success "Docker installed"
fi fi
;; ;;
@@ -318,9 +340,9 @@ install_docker() {
info "Docker already installed ($(docker --version)), skipping" info "Docker already installed ($(docker --version)), skipping"
else else
info "Setting up Docker CE repository ..." info "Setting up Docker CE repository ..."
sudo curl -fsSL https://download.docker.com/linux/fedora/docker-ce.repo \ psudo curl -fsSL https://download.docker.com/linux/fedora/docker-ce.repo \
-o /etc/yum.repos.d/docker-ce.repo -o /etc/yum.repos.d/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io \ psudo dnf install -y docker-ce docker-ce-cli containerd.io \
docker-compose-plugin docker-buildx-plugin docker-compose-plugin docker-buildx-plugin
success "Docker installed" success "Docker installed"
fi fi
@@ -335,21 +357,21 @@ docker_no_root() {
info "Configuring Docker for non-root usage ..." info "Configuring Docker for non-root usage ..."
if ! getent group docker > /dev/null 2>&1; then if ! getent group docker > /dev/null 2>&1; then
sudo groupadd docker psudo groupadd docker
fi fi
if id -nG "$USER" | grep -qw docker; then if id -nG "$USER" | grep -qw docker; then
info "User '$USER' is already in the docker group" info "User '$USER' is already in the docker group"
else else
sudo usermod -aG docker "$USER" psudo usermod -aG docker "$USER"
success "User '$USER' added to the docker group" success "User '$USER' added to the docker group"
warn "Log out and back in for the group change to take effect" warn "Log out and back in for the group change to take effect"
fi fi
if ! sudo systemctl is-enabled --quiet docker 2>/dev/null; then if ! psudo systemctl is-enabled --quiet docker 2>/dev/null; then
sudo systemctl enable docker psudo systemctl enable docker
fi fi
sudo systemctl start docker psudo systemctl start docker
success "Docker service running" success "Docker service running"
} }
@@ -412,6 +434,7 @@ ensure_sudo() {
# Keep sudo ticket alive in the background for the duration of the script # Keep sudo ticket alive in the background for the duration of the script
( while true; do sudo -n true 2>/dev/null; sleep 50; done ) & ( while true; do sudo -n true 2>/dev/null; sleep 50; done ) &
SUDO_KEEPALIVE_PID=$! SUDO_KEEPALIVE_PID=$!
# Note: ensure_sudo uses raw sudo intentionally — psudo is not defined yet
return return
fi fi
@@ -456,7 +479,7 @@ main() {
configure_ssh_config configure_ssh_config
step "Starting Docker" step "Starting Docker"
if sudo systemctl start docker; then if psudo systemctl start docker; then
success "Docker started" success "Docker started"
else else
warn "Could not start Docker — please start it manually" warn "Could not start Docker — please start it manually"