292 lines
12 KiB
Bash
Executable File
292 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# =============================================================================
|
||
# 00-init-tools.sh — 安装并配置服务器基础工具环境
|
||
#
|
||
# 功能:
|
||
# 1. 安装 Go(默认 1.22.5,可通过参数指定版本)
|
||
# 2. 配置 GOPROXY(自动测速选最快节点)
|
||
# 3. 安装 Node.js / npm(前端依赖)
|
||
# 4. 安装 Docker(基础设施容器)
|
||
# 5. 写入 /etc/profile.d/deploy-env.sh(永久生效)
|
||
#
|
||
# 用法:
|
||
# ./deploy-test/00-init-tools.sh # 安装全部
|
||
# ./deploy-test/00-init-tools.sh go # 只安装/配置 Go
|
||
# ./deploy-test/00-init-tools.sh node # 只安装 Node.js
|
||
# ./deploy-test/00-init-tools.sh docker # 只安装 Docker
|
||
# ./deploy-test/00-init-tools.sh goproxy # 只配置 GOPROXY
|
||
#
|
||
# 前置条件: root 或 sudo 权限,Ubuntu/Debian 系统
|
||
#
|
||
# 下一步: ./deploy-test/01-init-env.sh
|
||
# =============================================================================
|
||
set -euo pipefail
|
||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||
init_dirs
|
||
init_script_log
|
||
|
||
# ── 可调参数 ───────────────────────────────────────────────────────────────────
|
||
GO_VERSION="${GO_VERSION:-1.22.5}"
|
||
GO_ARCH="${GO_ARCH:-amd64}" # amd64 / arm64
|
||
NODE_VERSION="${NODE_VERSION:-20}" # Node.js LTS 大版本
|
||
PROFILE_FILE="/etc/profile.d/deploy-env.sh"
|
||
|
||
TARGET="${1:-all}"
|
||
|
||
header "步骤 0 — 初始化工具环境"
|
||
echo " 目标: ${TARGET} (Go=${GO_VERSION}, Node=${NODE_VERSION})"
|
||
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# 辅助函数
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
|
||
# 检查命令是否存在(不触发 set -e)
|
||
_has() { command -v "$1" &>/dev/null; }
|
||
|
||
# 追加到全局 profile(幂等:同一行不重复写)
|
||
_append_profile() {
|
||
local line="$1"
|
||
grep -qxF "$line" "$PROFILE_FILE" 2>/dev/null || echo "$line" >> "$PROFILE_FILE"
|
||
}
|
||
|
||
# 测试 GOPROXY 节点延迟,返回 ms(超时返回 9999)
|
||
_probe_proxy() {
|
||
local url="${1%/}/github.com/gin-gonic/gin/@v/list"
|
||
local ms
|
||
ms=$(curl -s -o /dev/null -w "%{time_total}" --max-time 5 "$url" 2>/dev/null || echo "9.999")
|
||
echo "${ms/./}" | sed 's/^0*//' | awk '{printf "%d\n", $1 * 1000 / 1000 + 0}'
|
||
# 简单地把秒转毫秒并取整
|
||
python3 -c "print(int(float('${ms}') * 1000))" 2>/dev/null || echo 9999
|
||
}
|
||
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# 1. Go
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
_install_go() {
|
||
step "安装 Go ${GO_VERSION} (linux/${GO_ARCH})"
|
||
|
||
local tarball="go${GO_VERSION}.linux-${GO_ARCH}.tar.gz"
|
||
local url="https://go.dev/dl/${tarball}"
|
||
local tmp="/tmp/${tarball}"
|
||
|
||
if _has go; then
|
||
local cur
|
||
cur=$(go version | awk '{print $3}' | sed 's/go//')
|
||
if [[ "$cur" == "$GO_VERSION" ]]; then
|
||
success " Go ${GO_VERSION} 已安装,跳过"
|
||
return 0
|
||
fi
|
||
warn " 当前 Go 版本 ${cur},将升级到 ${GO_VERSION}"
|
||
fi
|
||
|
||
info " 下载 ${url}"
|
||
curl -fL --progress-bar -o "$tmp" "$url" || {
|
||
# 备用镜像
|
||
warn " go.dev 下载失败,尝试备用镜像..."
|
||
curl -fL --progress-bar -o "$tmp" "https://golang.google.cn/dl/${tarball}" || {
|
||
error " 下载失败,请手动安装: https://go.dev/dl/"
|
||
return 1
|
||
}
|
||
}
|
||
|
||
info " 解压到 /usr/local/go"
|
||
rm -rf /usr/local/go
|
||
tar -C /usr/local -xzf "$tmp"
|
||
rm -f "$tmp"
|
||
|
||
# 写入 profile
|
||
[[ -f "$PROFILE_FILE" ]] || touch "$PROFILE_FILE"
|
||
_append_profile 'export PATH=$PATH:/usr/local/go/bin'
|
||
_append_profile 'export GOPATH=/root/go'
|
||
_append_profile 'export GOMODCACHE=/root/go/pkg/mod'
|
||
|
||
# 当前会话也生效
|
||
export PATH=$PATH:/usr/local/go/bin
|
||
export GOPATH=/root/go
|
||
export GOMODCACHE=/root/go/pkg/mod
|
||
|
||
success " Go $(go version | awk '{print $3}') 安装完成 → /usr/local/go/bin/go"
|
||
}
|
||
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# 2. GOPROXY — 自动选最快节点
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
_config_goproxy() {
|
||
step "配置 GOPROXY(自动测速)"
|
||
|
||
if ! _has go; then
|
||
warn " go 未安装,跳过 GOPROXY 配置"
|
||
return 0
|
||
fi
|
||
|
||
declare -A PROXIES=(
|
||
["proxy.golang.org"]="https://proxy.golang.org"
|
||
["goproxy.cn"]="https://goproxy.cn"
|
||
["goproxy.io"]="https://goproxy.io"
|
||
)
|
||
|
||
local best_name="goproxy.io"
|
||
local best_ms=9999
|
||
|
||
info " 测速中..."
|
||
for name in "${!PROXIES[@]}"; do
|
||
local url="${PROXIES[$name]}"
|
||
local ms
|
||
ms=$(python3 -c "
|
||
import urllib.request, time, sys
|
||
try:
|
||
t = time.time()
|
||
urllib.request.urlopen('${url}/github.com/gin-gonic/gin/@v/list', timeout=5)
|
||
print(int((time.time()-t)*1000))
|
||
except:
|
||
print(9999)
|
||
" 2>/dev/null)
|
||
echo " ${name}: ${ms}ms"
|
||
if (( ms < best_ms )); then
|
||
best_ms=$ms
|
||
best_name=$name
|
||
best_url="${url}"
|
||
fi
|
||
done
|
||
|
||
# 构建代理列表:最快的放第一
|
||
local proxy_list="${best_url},https://proxy.golang.org,https://goproxy.cn,direct"
|
||
# 去重
|
||
proxy_list=$(echo "$proxy_list" | tr ',' '\n' | awk '!seen[$0]++' | tr '\n' ',' | sed 's/,$//')
|
||
|
||
go env -w GOPROXY="${proxy_list}"
|
||
go env -w GONOSUMDB="*"
|
||
go env -w GOFLAGS=""
|
||
|
||
# 同步写入 profile(go env -w 已持久化到 GOENV,这里写 profile 作为双保险)
|
||
_append_profile "export GOPROXY=${proxy_list}"
|
||
_append_profile 'export GONOSUMDB=*'
|
||
|
||
success " GOPROXY=${proxy_list}"
|
||
success " 最快节点: ${best_name} (${best_ms}ms)"
|
||
}
|
||
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# 3. Node.js
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
_install_node() {
|
||
step "安装 Node.js ${NODE_VERSION}.x LTS"
|
||
|
||
if _has node; then
|
||
local cur
|
||
cur=$(node --version)
|
||
info " 当前 Node.js 版本: ${cur}"
|
||
# 检查大版本是否匹配
|
||
local major
|
||
major=$(echo "$cur" | sed 's/v//' | cut -d. -f1)
|
||
if (( major >= NODE_VERSION )); then
|
||
success " Node.js >= ${NODE_VERSION},跳过"
|
||
return 0
|
||
fi
|
||
warn " 版本过旧,将升级"
|
||
fi
|
||
|
||
if _has apt-get; then
|
||
info " 使用 NodeSource 安装 Node.js ${NODE_VERSION}.x"
|
||
curl -fsSL "https://deb.nodesource.com/setup_${NODE_VERSION}.x" | bash -
|
||
apt-get install -y nodejs
|
||
elif _has yum; then
|
||
curl -fsSL "https://rpm.nodesource.com/setup_${NODE_VERSION}.x" | bash -
|
||
yum install -y nodejs
|
||
else
|
||
warn " 未找到 apt-get/yum,请手动安装 Node.js: https://nodejs.org/"
|
||
return 1
|
||
fi
|
||
|
||
# 安装常用全局包管理器
|
||
npm install -g pnpm yarn 2>/dev/null || true
|
||
|
||
_append_profile 'export PATH=$PATH:/usr/local/bin'
|
||
|
||
success " Node.js $(node --version),npm $(npm --version) 安装完成"
|
||
_has pnpm && success " pnpm $(pnpm --version)"
|
||
_has yarn && success " yarn $(yarn --version)"
|
||
}
|
||
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# 4. Docker
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
_install_docker() {
|
||
step "安装 Docker"
|
||
|
||
if _has docker; then
|
||
success " Docker $(docker --version | awk '{print $3}' | tr -d ',') 已安装,跳过"
|
||
return 0
|
||
fi
|
||
|
||
if _has apt-get; then
|
||
info " 使用官方脚本安装 Docker..."
|
||
curl -fsSL https://get.docker.com | sh
|
||
elif _has yum; then
|
||
yum install -y docker
|
||
systemctl enable docker
|
||
else
|
||
warn " 未找到 apt-get/yum,请手动安装 Docker: https://docs.docker.com/engine/install/"
|
||
return 1
|
||
fi
|
||
|
||
# 启动服务
|
||
systemctl start docker 2>/dev/null || service docker start 2>/dev/null || true
|
||
|
||
success " Docker $(docker --version | awk '{print $3}' | tr -d ',') 安装完成"
|
||
}
|
||
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# 执行
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
case "$TARGET" in
|
||
go)
|
||
_install_go
|
||
_config_goproxy
|
||
;;
|
||
goproxy)
|
||
_config_goproxy
|
||
;;
|
||
node)
|
||
_install_node
|
||
;;
|
||
docker)
|
||
_install_docker
|
||
;;
|
||
all)
|
||
_install_go
|
||
_config_goproxy
|
||
_install_node
|
||
_install_docker
|
||
;;
|
||
*)
|
||
error "未知目标: $TARGET"
|
||
echo "用法: $0 [all|go|goproxy|node|docker]"
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
# 汇总
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
echo ""
|
||
success "环境初始化完成!"
|
||
echo ""
|
||
echo -e "${BOLD}当前工具版本:${NC}"
|
||
_has go && echo " Go: $(go version | awk '{print $3, $4}')" || echo " Go: 未安装"
|
||
_has node && echo " Node: $(node --version)" || echo " Node: 未安装"
|
||
_has npm && echo " npm: $(npm --version)" || echo " npm: 未安装"
|
||
_has pnpm && echo " pnpm: $(pnpm --version)" || echo " pnpm: 未安装"
|
||
_has yarn && echo " yarn: $(yarn --version)" || echo " yarn: 未安装"
|
||
_has docker && echo " Docker: $(docker --version | awk '{print $3}' | tr -d ',')" || echo " Docker: 未安装"
|
||
echo ""
|
||
echo -e "${BOLD}GOPROXY 配置:${NC}"
|
||
_has go && go env GOPROXY || echo " (go 未安装)"
|
||
echo ""
|
||
echo -e "${YELLOW}注意: 新终端需执行以下命令使环境变量生效:${NC}"
|
||
echo -e " ${CYAN}source /etc/profile.d/deploy-env.sh${NC}"
|
||
echo -e " 或重新登录 SSH"
|
||
echo ""
|
||
echo -e "${BOLD}下一步:${NC}"
|
||
echo -e " ${CYAN}./deploy-test/01-init-env.sh${NC}"
|