package model import "time" // ============================================ // DeployTool 工具配置(懒加载/预装/禁用)// Logan.Li // 按 deploy_ 前缀归属发布配置域。// Logan.Li // ============================================ // ToolInstallMethod 安装方式 type ToolInstallMethod string const ( ToolPreinstall ToolInstallMethod = "preinstall" // 预装在 Dockerfile 中 ToolLazy ToolInstallMethod = "lazy" // 懒加载:首次使用时自动下载 ToolDisabled ToolInstallMethod = "disabled" // 禁用:不安装 ) // ToolStatus 安装状态 type ToolStatus string const ( ToolNotInstalled ToolStatus = "not_installed" // 未安装 ToolInstalling ToolStatus = "installing" // 安装中 ToolInstalled ToolStatus = "installed" // 已安装 ToolFailed ToolStatus = "failed" // 安装失败 ) // DeployTool 工具配置表 type DeployTool struct { ID uint `gorm:"primaryKey;autoIncrement;comment:主键ID" json:"id"` Name string `gorm:"type:varchar(64);uniqueIndex;not null;comment:工具名称(英文标识)" json:"name"` DisplayName string `gorm:"type:varchar(128);not null;comment:显示名称" json:"display_name"` Description string `gorm:"type:varchar(256);comment:工具描述" json:"description,omitempty"` Category string `gorm:"type:varchar(32);default:other;comment:分类: k8s/build/deploy/automation" json:"category"` InstallMethod ToolInstallMethod `gorm:"type:varchar(16);default:lazy;comment:安装方式: preinstall/lazy/disabled" json:"install_method"` CheckCommand string `gorm:"type:varchar(256);comment:检测命令(如 kubectl version --client)" json:"check_command,omitempty"` BinaryName string `gorm:"type:varchar(64);comment:二进制文件名(如 kubectl/mvn/node)" json:"binary_name,omitempty"` InstallPath string `gorm:"type:varchar(256);comment:安装目标路径(如 /usr/local/bin)" json:"install_path,omitempty"` DownloadURL string `gorm:"type:varchar(512);comment:下载地址模板(支持 {version})" json:"download_url,omitempty"` VersionURL string `gorm:"type:varchar(512);comment:版本查询地址(如 stable.txt)" json:"version_url,omitempty"` InstallScript string `gorm:"type:text;comment:自定义安装脚本(Shell)" json:"install_script,omitempty"` DefaultVersion string `gorm:"type:varchar(32);comment:默认版本号" json:"default_version,omitempty"` Status ToolStatus `gorm:"type:varchar(16);default:not_installed;comment:当前安装状态" json:"status"` InstalledVer string `gorm:"type:varchar(128);comment:已安装版本号" json:"installed_version,omitempty"` LastCheck *time.Time `gorm:"comment:最后检测时间" json:"last_check,omitempty"` ErrorMsg string `gorm:"type:varchar(512);comment:安装失败原因" json:"error_msg,omitempty"` Enabled bool `gorm:"default:true;comment:是否启用" json:"enabled"` SortOrder int `gorm:"default:0;comment:排序" json:"sort_order"` CreatedAt time.Time `gorm:"not null;comment:创建时间" json:"created_at"` UpdatedAt time.Time `gorm:"not null;comment:更新时间" json:"updated_at"` } // TableName 表名 func (DeployTool) TableName() string { return "deploy_tools" } // ============================================ // 预置工具定义(Seeder)— 精简版 v2 // 移除: kubectl(Dockerfile 预装)、helm(非核心) // 新增: nvm(管理 Node 版本)、jdk(Java 构建) // ============================================ // PredefinedTools 返回系统预置的工具配置列表 func PredefinedTools() []DeployTool { return []DeployTool{ // ──── JDK — Java 构建 // Logan.Li ──── { Name: "jdk", DisplayName: "JDK (Java)", Description: "Java 开发工具包,流水线 Java/Maven 构建阶段使用", Category: "build", InstallMethod: ToolLazy, BinaryName: "java", InstallPath: "/usr/local/bin/java", CheckCommand: "/usr/local/bin/java -version 2>&1 | head -1", VersionURL: "https://api.adoptium.net/v3/info/release_versions?heap_size=normal&image_type=jdk&page=0&page_size=1&project=jdk&release_type=ga&semver=false&sort_method=DEFAULT&sort_order=DESC&version=%5B17%2C99%5D", DownloadURL: "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-{version}/OpenJDK17U-jdk_x64_linux_hotspot_{version}.tar.gz", DefaultVersion: "17.0.19+10", InstallScript: `#!/bin/sh # JDK lazy install — Alpine apk 优先,否则国内镜像轮询 Adoptium // Logan.Li JDK_VERSION="${TOOL_VERSION:-17.0.19+10}" JDK_MAJOR=$(echo "$JDK_VERSION" | cut -d. -f1) JDK_FILE_VER=$(echo "$JDK_VERSION" | tr '+' '_') echo "[JDK] Installing ${JDK_VERSION} (major ${JDK_MAJOR})..." if command -v apk >/dev/null 2>&1; then case "$JDK_MAJOR" in 11) APK_PKG="openjdk11-jdk" ;; 17) APK_PKG="openjdk17-jdk" ;; 21) APK_PKG="openjdk21-jdk" ;; *) APK_PKG="" ;; esac if [ -n "$APK_PKG" ]; then echo "[JDK] Trying apk: $APK_PKG" if apk add --no-cache "$APK_PKG"; then JAVA_BIN=$(command -v java 2>/dev/null || echo "/usr/lib/jvm/java-${JDK_MAJOR}-openjdk/bin/java") mkdir -p /usr/local/bin /usr/local/share/jdk/bin ln -sf "$JAVA_BIN" /usr/local/bin/java ln -sf "$(dirname "$JAVA_BIN")/javac" /usr/local/bin/javac 2>/dev/null || true ln -sf "$(dirname "$JAVA_BIN")/jar" /usr/local/bin/jar 2>/dev/null || true echo "[JDK] Installed via apk: $(java -version 2>&1 | head -1)" exit 0 fi fi fi cd /tmp && rm -rf jdk.tar.gz jdk-install mkdir -p jdk-install && cd jdk-install DOWNLOADED=0 # 国内镜像优先: 阿里云 > 清华 > 华为 > 中科大 > GitHub // Logan.Li for URL in \ "https://mirrors.aliyun.com/Adoptium/${JDK_MAJOR}/jdk/${JDK_VERSION}/OpenJDK${JDK_MAJOR}U-jdk_x64_linux_hotspot_${JDK_FILE_VER}.tar.gz" \ "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/${JDK_MAJOR}/jdk/x64/linux/OpenJDK${JDK_MAJOR}U-jdk_x64_linux_hotspot_${JDK_FILE_VER}.tar.gz" \ "https://mirrors.huaweicloud.com/Adoptium/${JDK_MAJOR}/jdk/${JDK_VERSION}/OpenJDK${JDK_MAJOR}U-jdk_x64_linux_hotspot_${JDK_FILE_VER}.tar.gz" \ "https://mirrors.ustc.edu.cn/Adoptium/${JDK_MAJOR}/jdk/${JDK_VERSION}/OpenJDK${JDK_MAJOR}U-jdk_x64_linux_hotspot_${JDK_FILE_VER}.tar.gz" \ "https://api.adoptium.net/v3/binary/version/${JDK_VERSION}/linux/x64/jdk/hotspot/normal/eclipse?project=jdk" \ "https://github.com/adoptium/temurin${JDK_MAJOR}-binaries/releases/download/jdk-${JDK_VERSION}/OpenJDK${JDK_MAJOR}U-jdk_x64_linux_hotspot_${JDK_FILE_VER}.tar.gz" do echo "[JDK] Trying $URL" if curl -fsSL -L --connect-timeout 15 --max-time 300 -o jdk.tar.gz "$URL"; then if [ -s jdk.tar.gz ] && file jdk.tar.gz | grep -q gzip; then DOWNLOADED=1 break fi fi rm -f jdk.tar.gz done [ "$DOWNLOADED" -eq 1 ] || { echo "[JDK] all mirrors failed"; exit 1; } tar xzf jdk.tar.gz || { echo "[JDK] extract failed"; exit 1; } JDK_DIR=$(find . -maxdepth 1 -type d -name 'jdk-*' | head -1) [ -n "$JDK_DIR" ] || { echo "[JDK] extract dir not found"; exit 1; } mkdir -p /usr/local/share rm -rf /usr/local/share/jdk mv "$JDK_DIR" /usr/local/share/jdk ln -sf /usr/local/share/jdk/bin/java /usr/local/bin/java ln -sf /usr/local/share/jdk/bin/javac /usr/local/bin/javac ln -sf /usr/local/share/jdk/bin/jar /usr/local/bin/jar cd /tmp && rm -rf jdk-install echo "[JDK] Installed: $(/usr/local/bin/java -version 2>&1 | head -1)" `, SortOrder: 1, }, // ──── Maven — Java 构建 // Logan.Li ──── { Name: "maven", DisplayName: "Maven", Description: "Java 项目构建工具,流水线 Java 构建阶段使用(依赖 JDK)", Category: "build", InstallMethod: ToolLazy, BinaryName: "mvn", InstallPath: "/usr/local/bin/mvn", CheckCommand: "/usr/local/bin/mvn --version 2>/dev/null | head -1", DownloadURL: "https://dlcdn.apache.org/maven/maven-3/{version}/binaries/apache-maven-{version}-bin.tar.gz", VersionURL: "https://dlcdn.apache.org/maven/maven-3/", DefaultVersion: "3.9.16", InstallScript: `#!/bin/sh # Maven lazy install — 国内镜像优先(阿里云 > 华为 > 清华 > 中科大 > 官方)// Logan.Li MAVEN_VERSION="${TOOL_VERSION:-3.9.16}" MAJOR="${MAVEN_VERSION%%.*}" echo "[Maven] Downloading ${MAVEN_VERSION}..." cd /tmp && rm -rf maven.tar.gz maven-install mkdir -p maven-install && cd maven-install DOWNLOADED=0 for URL in \ "https://mirrors.aliyun.com/apache/maven/maven-${MAJOR}/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \ "https://repo.huaweicloud.com/apache/maven/maven-${MAJOR}/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \ "https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-${MAJOR}/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \ "https://mirrors.ustc.edu.cn/apache/maven/maven-${MAJOR}/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \ "https://mirrors.nju.edu.cn/apache/maven/maven-${MAJOR}/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \ "https://dlcdn.apache.org/maven/maven-${MAJOR}/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \ "https://archive.apache.org/dist/maven/maven-${MAJOR}/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" do echo "[Maven] Trying $URL" if curl -fsSL --connect-timeout 15 --max-time 120 -o maven.tar.gz "$URL"; then if [ -s maven.tar.gz ] && file maven.tar.gz | grep -q gzip; then DOWNLOADED=1 break fi fi rm -f maven.tar.gz done [ "$DOWNLOADED" -eq 1 ] || { echo "[Maven] all mirrors failed"; exit 1; } tar xzf maven.tar.gz || { echo "[Maven] extract failed"; exit 1; } MVN_DIR=$(find . -maxdepth 1 -type d -name 'apache-maven-*' | head -1) [ -n "$MVN_DIR" ] || { echo "[Maven] extract dir not found"; exit 1; } mkdir -p /usr/local/share rm -rf /usr/local/share/maven mv "$MVN_DIR" /usr/local/share/maven ln -sf /usr/local/share/maven/bin/mvn /usr/local/bin/mvn cd /tmp && rm -rf maven-install echo "[Maven] Installed: $(/usr/local/bin/mvn --version 2>&1 | head -1)" `, SortOrder: 2, }, // ──── Go — 后端构建 // Logan.Li ──── { Name: "golang", DisplayName: "Go (Golang)", Description: "Go 语言工具链,流水线 go build/test 阶段使用", Category: "build", InstallMethod: ToolLazy, BinaryName: "go", InstallPath: "/usr/local/bin/go", CheckCommand: "/usr/local/bin/go version 2>/dev/null", DownloadURL: "https://go.dev/dl/go{version}.linux-amd64.tar.gz", DefaultVersion: "1.24.0", InstallScript: `#!/bin/sh # Go lazy install — 国内镜像优先 // Logan.Li GO_VERSION="${TOOL_VERSION:-1.24.0}" GO_VERSION="${GO_VERSION#go}" echo "[Go] Installing go${GO_VERSION}..." cd /tmp && rm -rf go.tar.gz go-install mkdir -p go-install && cd go-install DOWNLOADED=0 for URL in \ "https://golang.google.cn/dl/go${GO_VERSION}.linux-amd64.tar.gz" \ "https://mirrors.aliyun.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" \ "https://mirrors.ustc.edu.cn/golang/go${GO_VERSION}.linux-amd64.tar.gz" \ "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" do echo "[Go] Trying $URL" if curl -fsSL -L -o go.tar.gz "$URL"; then DOWNLOADED=1 break fi rm -f go.tar.gz done [ "$DOWNLOADED" -eq 1 ] || { echo "[Go] all mirrors failed"; exit 1; } tar xzf go.tar.gz [ -d go ] || { echo "[Go] extract dir not found"; exit 1; } rm -rf /usr/local/go mv go /usr/local/go mkdir -p /usr/local/bin ln -sf /usr/local/go/bin/go /usr/local/bin/go ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt mkdir -p /etc/profile.d cat > /etc/profile.d/golang.sh << 'GO_PROFILE' export GOROOT=/usr/local/go export PATH=$GOROOT/bin:$PATH export GOPROXY=https://goproxy.cn,direct export GOTOOLCHAIN=local GO_PROFILE chmod +x /etc/profile.d/golang.sh export GOROOT=/usr/local/go export PATH=$GOROOT/bin:$PATH export GOPROXY=https://goproxy.cn,direct export GOTOOLCHAIN=local echo "[Go] Installed: $(/usr/local/bin/go version)" `, SortOrder: 3, }, // ──── NVM — Node Version Manager // Logan.Li ──── { Name: "nvm", DisplayName: "NVM (Node 版本管理)", Description: "Node 版本管理器,流水线 Node.js 构建阶段使用(node 依赖此工具)", Category: "build", InstallMethod: ToolLazy, BinaryName: "nvm", InstallPath: "/usr/local/share/nvm/nvm.sh", CheckCommand: ". /usr/local/share/nvm/nvm.sh 2>/dev/null && nvm --version 2>/dev/null", VersionURL: "https://api.github.com/repos/nvm-sh/nvm/releases/latest", DefaultVersion: "0.40.1", InstallScript: `#!/bin/sh # NVM lazy install — 使用 bash 执行官方脚本 // Logan.Li NVM_VERSION="${TOOL_VERSION:-0.40.1}" NVM_DIR="/usr/local/share/nvm" echo "[NVM] Downloading v${NVM_VERSION}..." mkdir -p "$NVM_DIR" curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh" -o /tmp/nvm-install.sh export NVM_DIR bash /tmp/nvm-install.sh rm -f /tmp/nvm-install.sh [ -s "$NVM_DIR/nvm.sh" ] || { echo "[NVM] nvm.sh not found"; exit 1; } mkdir -p /etc/profile.d cat > /etc/profile.d/nvm.sh << 'NVM_PROFILE' export NVM_DIR="/usr/local/share/nvm" [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" NVM_PROFILE chmod +x /etc/profile.d/nvm.sh . "$NVM_DIR/nvm.sh" echo "[NVM] Installed: $(nvm --version 2>/dev/null || echo v${NVM_VERSION})" `, SortOrder: 4, }, // ──── Node.js — 通过 NVM 安装 // Logan.Li ──── { Name: "nodejs", DisplayName: "Node.js + npm", Description: "前端构建工具,流水线 npm install/build 阶段使用(通过 NVM 安装)", Category: "build", InstallMethod: ToolLazy, BinaryName: "node", InstallPath: "/usr/local/bin/node", CheckCommand: "node --version 2>/dev/null", DefaultVersion: "lts", InstallScript: `#!/bin/sh # Node.js 安装 — Alpine 用 unofficial-builds musl 包(官方已停 musl 预编译)// Logan.Li NODE_VERSION="${TOOL_VERSION:-lts}" NVM_DIR="/usr/local/share/nvm" NODE_MIRROR="${NODE_MIRROR:-https://npmmirror.com/mirrors/node}" echo "[Node.js] Installing (${NODE_VERSION})..." mkdir -p "$NVM_DIR/versions/node" export NVM_DIR resolve_version() { case "$1" in lts) curl -fsSL "https://nodejs.org/dist/index.json" 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print(next(x['version'] for x in d if x.get('lts')))" 2>/dev/null || echo "v24.18.0" ;; latest|node) curl -fsSL "https://nodejs.org/dist/index.json" 2>/dev/null | python3 -c "import json,sys; print(json.load(sys.stdin)[0]['version'])" 2>/dev/null || echo "v24.18.0" ;; *) echo "v${1#v}" ;; esac } direct_install_binary() { RESOLVED="$1" DEST="$NVM_DIR/versions/node/${RESOLVED}" TMP="/tmp/node-dl-$$" ARCHIVE="/tmp/node-archive-$$.tar.xz" rm -rf "$TMP" "$DEST" "$ARCHIVE" mkdir -p "$TMP" # 国内优先: 阿里云 OSS > npmmirror > 清华 > 官方 // Logan.Li if [ -f /etc/alpine-release ]; then URLS=" https://unofficial-builds.nodejs.org/download/release/${RESOLVED}/node-${RESOLVED}-linux-x64-musl.tar.xz " else URLS=" https://mirrors.aliyun.com/nodejs-release/${RESOLVED}/node-${RESOLVED}-linux-x64.tar.xz https://mirrors.tuna.tsinghua.edu.cn/nodejs-release/${RESOLVED}/node-${RESOLVED}-linux-x64.tar.xz ${NODE_MIRROR}/${RESOLVED}/node-${RESOLVED}-linux-x64.tar.xz https://nodejs.org/dist/${RESOLVED}/node-${RESOLVED}-linux-x64.tar.xz " fi DOWNLOADED=0 for URL in $URLS; do [ -n "$URL" ] || continue echo "[Node.js] Downloading $URL" rm -f "$ARCHIVE" if curl -fsSL -L --connect-timeout 30 --max-time 300 -o "$ARCHIVE" "$URL"; then if [ -s "$ARCHIVE" ]; then DOWNLOADED=1 break fi fi echo "[Node.js] Failed: $URL" rm -f "$ARCHIVE" done [ "$DOWNLOADED" -eq 1 ] || { echo "[Node.js] all mirrors failed"; rm -rf "$TMP"; return 1; } # xz is not always available; fallback to python3 tarfile // Logan.Li if command -v xz >/dev/null 2>&1; then xz -dc "$ARCHIVE" | tar -xC "$TMP" 2>/dev/null || true fi if [ ! -x "$TMP"/*/bin/node ] 2>/dev/null; then python3 -c " import tarfile, os with tarfile.open('$ARCHIVE') as tf: tf.extractall(path='$TMP', filter='fully_trusted') " 2>/dev/null || true fi rm -f "$ARCHIVE" SUB=$(find "$TMP" -mindepth 1 -maxdepth 1 -type d | head -1) if [ -n "$SUB" ] && [ -x "$SUB/bin/node" ]; then mv "$SUB" "$DEST" rm -rf "$TMP" echo "[Node.js] Installed $RESOLVED" return 0 fi echo "[Node.js] extract failed" rm -rf "$TMP" return 1 } RESOLVED=$(resolve_version "$NODE_VERSION") [ -n "$RESOLVED" ] || { echo "[Node.js] cannot resolve version for $NODE_VERSION"; exit 1; } echo "[Node.js] Target version: $RESOLVED" direct_install_binary "$RESOLVED" || { echo "[Node.js] binary install failed"; exit 1; } INST="$RESOLVED" mkdir -p /usr/local/bin ln -sf "$NVM_DIR/versions/node/${INST}/bin/node" /usr/local/bin/node ln -sf "$NVM_DIR/versions/node/${INST}/bin/npm" /usr/local/bin/npm ln -sf "$NVM_DIR/versions/node/${INST}/bin/npx" /usr/local/bin/npx "$NVM_DIR/versions/node/${INST}/bin/npm" config set registry https://registry.npmmirror.com echo "[Node.js] Installed: $(/usr/local/bin/node --version) / npm $("$NVM_DIR/versions/node/${INST}/bin/npm" --version)" `, SortOrder: 5, }, // ──── Git — 版本控制(懒加载, U4 镜像瘦身)// Logan.Li ──── { Name: "git", DisplayName: "Git", Description: "版本控制工具,流水线代码拉取使用(首次使用自动安装)", Category: "build", InstallMethod: ToolLazy, BinaryName: "git", InstallPath: "/usr/bin/git", CheckCommand: "git --version 2>/dev/null", InstallScript: `apk add --no-cache git`, SortOrder: 6, }, // ──── Docker CLI — 容器构建(懒加载, U4 镜像瘦身)// Logan.Li ──── { Name: "docker-cli", DisplayName: "Docker CLI", Description: "Docker 命令行工具,流水线镜像构建/推送使用(首次使用自动安装)", Category: "deploy", InstallMethod: ToolLazy, BinaryName: "docker", InstallPath: "/usr/bin/docker", CheckCommand: "docker --version 2>/dev/null", InstallScript: `apk add --no-cache docker-cli`, SortOrder: 7, }, } }