374 lines
12 KiB
Bash
Executable File
374 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# ════════════════════════════════════════════════════════════════
|
||
# 光湖 · Gitea Actions Runner 一键安装脚本
|
||
# Sovereign: TCS-0002∞ · ICE-GL∞ · 国作登字-2026-A-00037559
|
||
# 守护: 铸渊 · ICE-GL-ZY001
|
||
# ════════════════════════════════════════════════════════════════
|
||
#
|
||
# 用法:
|
||
# sudo bash install-runner.sh [--token GITEA_API_TOKEN] [--name RUNNER_NAME]
|
||
#
|
||
# 参数:
|
||
# --token Gitea 管理员 API Token (必须,用于获取 Runner 注册 Token)
|
||
# --name Runner 名称 (可选,默认 zhuyuan-runner-cn)
|
||
# --mode 运行模式: host 或 docker (可选,默认 host,2C2G推荐host)
|
||
#
|
||
# 环境变量 (替代命令行参数):
|
||
# GITEA_API_TOKEN 同 --token
|
||
# RUNNER_NAME 同 --name
|
||
# RUNNER_MODE 同 --mode
|
||
#
|
||
# 此脚本会:
|
||
# 1. 下载 gitea-runner v1.0.2
|
||
# 2. 通过 Gitea API 获取 Runner 注册 Token
|
||
# 3. 注册 Runner 到 Gitea
|
||
# 4. 生成配置文件 (host 模式,适合 2C2G)
|
||
# 5. 创建 systemd 服务
|
||
# 6. 启动服务
|
||
#
|
||
# ════════════════════════════════════════════════════════════════
|
||
|
||
set -euo pipefail
|
||
|
||
# ─── 常量 ─────────────────────────────────────────────────────
|
||
RUNNER_VERSION="1.0.2"
|
||
RUNNER_BINARY="gitea-runner-${RUNNER_VERSION}-linux-amd64"
|
||
RUNNER_DOWNLOAD_URL="https://gitea.com/gitea/runner/releases/download/v${RUNNER_VERSION}/${RUNNER_BINARY}.xz"
|
||
RUNNER_INSTALL_PATH="/usr/local/bin/gitea-runner"
|
||
RUNNER_WORK_DIR="/var/lib/act_runner"
|
||
RUNNER_CONFIG_DIR="/etc/act_runner"
|
||
RUNNER_CONFIG_PATH="${RUNNER_CONFIG_DIR}/config.yaml"
|
||
SYSTEMD_SERVICE_NAME="gitea-runner"
|
||
SYSTEMD_SERVICE_PATH="/etc/systemd/system/${SYSTEMD_SERVICE_NAME}.service"
|
||
|
||
# Gitea 配置
|
||
GITEA_URL="https://guanghulab.com"
|
||
GITEA_API="${GITEA_URL}/api/v1"
|
||
GITEA_OWNER="bingshuo"
|
||
GITEA_REPO="guanghulab"
|
||
|
||
# 颜色
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
CYAN='\033[0;36m'
|
||
NC='\033[0m'
|
||
|
||
# ─── 解析参数 ─────────────────────────────────────────────────
|
||
API_TOKEN="${GITEA_API_TOKEN:-}"
|
||
RUNNER_NAME="${RUNNER_NAME:-zhuyuan-runner-cn}"
|
||
RUNNER_MODE="${RUNNER_MODE:-host}"
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case $1 in
|
||
--token) API_TOKEN="$2"; shift 2 ;;
|
||
--name) RUNNER_NAME="$2"; shift 2 ;;
|
||
--mode) RUNNER_MODE="$2"; shift 2 ;;
|
||
--help|-h)
|
||
head -30 "$0" | grep '^#' | sed 's/^# \?//'
|
||
exit 0
|
||
;;
|
||
*) echo -e "${RED}未知参数: $1${NC}"; exit 1 ;;
|
||
esac
|
||
done
|
||
|
||
# ─── 函数 ─────────────────────────────────────────────────────
|
||
|
||
log_info() { echo -e "${GREEN}[✓]${NC} $*"; }
|
||
log_warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||
log_step() { echo -e "${CYAN}[→]${NC} $*"; }
|
||
log_error() { echo -e "${RED}[✗]${NC} $*"; }
|
||
|
||
check_root() {
|
||
if [[ $EUID -ne 0 ]]; then
|
||
log_error "此脚本必须以 root 运行"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
check_arch() {
|
||
local arch
|
||
arch=$(uname -m)
|
||
if [[ "$arch" != "x86_64" && "$arch" != "amd64" ]]; then
|
||
log_error "不支持的架构: $arch (仅支持 amd64/x86_64)"
|
||
exit 1
|
||
fi
|
||
log_info "架构检查通过: $arch"
|
||
}
|
||
|
||
check_disk() {
|
||
local avail
|
||
avail=$(df -BG /var | awk 'NR==2 {print $4}' | tr -d 'G')
|
||
if [[ $avail -lt 1 ]]; then
|
||
log_error "磁盘空间不足: ${avail}GB 可用,至少需要 1GB"
|
||
exit 1
|
||
fi
|
||
log_info "磁盘空间: ${avail}GB 可用"
|
||
}
|
||
|
||
check_memory() {
|
||
local total_mb
|
||
total_mb=$(free -m | awk '/Mem:/ {print $2}')
|
||
log_info "内存: ${total_mb}MB"
|
||
if [[ $total_mb -lt 1500 ]]; then
|
||
log_warn "内存较低 (${total_mb}MB),将使用 host 模式以节省资源"
|
||
RUNNER_MODE="host"
|
||
fi
|
||
}
|
||
|
||
download_runner() {
|
||
if [[ -x "$RUNNER_INSTALL_PATH" ]]; then
|
||
local current_version
|
||
current_version=$("$RUNNER_INSTALL_PATH" --version 2>/dev/null | grep -oP 'v\K[\d.]+' || echo "unknown")
|
||
log_info "gitea-runner 已安装 (版本: ${current_version})"
|
||
read -rp "是否重新安装?[y/N] " -n 1 -r
|
||
echo
|
||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
log_step "下载 gitea-runner v${RUNNER_VERSION}..."
|
||
local tmp_dir
|
||
tmp_dir=$(mktemp -d)
|
||
trap "rm -rf $tmp_dir" EXIT
|
||
|
||
if ! curl -fSL --progress-bar -o "${tmp_dir}/${RUNNER_BINARY}.xz" "$RUNNER_DOWNLOAD_URL"; then
|
||
log_error "下载失败: $RUNNER_DOWNLOAD_URL"
|
||
log_warn "如果国内无法访问 gitea.com,请通过 COS 中转:"
|
||
log_warn " 1. 在新加坡服务器下载此文件"
|
||
log_warn " 2. 推送到 COS"
|
||
log_warn " 3. 从 COS 拉到本服务器"
|
||
log_warn " 4. 用 --skip-download 参数跳过下载步骤"
|
||
exit 1
|
||
fi
|
||
|
||
log_step "解压..."
|
||
xz -d "${tmp_dir}/${RUNNER_BINARY}.xz"
|
||
chmod +x "${tmp_dir}/${RUNNER_BINARY}"
|
||
|
||
log_step "安装到 ${RUNNER_INSTALL_PATH}..."
|
||
mv "${tmp_dir}/${RUNNER_BINARY}" "$RUNNER_INSTALL_PATH"
|
||
log_info "gitea-runner 安装完成: $("$RUNNER_INSTALL_PATH" --version 2>&1 | head -1)"
|
||
}
|
||
|
||
get_registration_token() {
|
||
log_step "通过 API 获取 Runner 注册 Token..."
|
||
|
||
if [[ -z "$API_TOKEN" ]]; then
|
||
log_error "需要 Gitea API Token"
|
||
echo ""
|
||
echo "获取方式:"
|
||
echo " 1. 浏览器打开 ${GITEA_URL}/user/settings/applications"
|
||
echo " 2. 生成一个 Token(需要 repo + admin:runner 权限)"
|
||
echo " 3. 重新运行: sudo bash $0 --token <你的token>"
|
||
echo ""
|
||
echo "或设置环境变量: export GITEA_API_TOKEN=<token>"
|
||
exit 1
|
||
fi
|
||
|
||
local response
|
||
# 先尝试仓库级 token
|
||
response=$(curl -sf -H "Authorization: token ${API_TOKEN}" \
|
||
"${GITEA_API}/repos/${GITEA_OWNER}/${GITEA_REPO}/actions/runners/registration-token" 2>/dev/null || echo "")
|
||
|
||
if [[ -z "$response" ]]; then
|
||
# 尝试实例级 token(需要管理员权限)
|
||
log_warn "仓库级 token 获取失败,尝试实例级..."
|
||
response=$(curl -sf -H "Authorization: token ${API_TOKEN}" \
|
||
"${GITEA_API}/admin/runners/registration-token" 2>/dev/null || echo "")
|
||
fi
|
||
|
||
if [[ -z "$response" ]]; then
|
||
log_error "无法获取 Runner 注册 Token"
|
||
log_error "请确认 API Token 有正确的权限"
|
||
exit 1
|
||
fi
|
||
|
||
REG_TOKEN=$(echo "$response" | grep -oP '"token"\s*:\s*"\K[^"]+' || echo "")
|
||
if [[ -z "$REG_TOKEN" ]]; then
|
||
log_error "解析注册 Token 失败: $response"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "注册 Token 获取成功"
|
||
}
|
||
|
||
create_user() {
|
||
if id -u act_runner &>/dev/null; then
|
||
log_info "act_runner 用户已存在"
|
||
return 0
|
||
fi
|
||
|
||
log_step "创建 act_runner 用户..."
|
||
useradd -r -m -d "$RUNNER_WORK_DIR" -s /bin/bash act_runner
|
||
log_info "用户创建完成"
|
||
}
|
||
|
||
generate_config() {
|
||
log_step "生成 Runner 配置..."
|
||
|
||
mkdir -p "$RUNNER_CONFIG_DIR"
|
||
|
||
"$RUNNER_INSTALL_PATH" generate-config > "$RUNNER_CONFIG_PATH"
|
||
|
||
# 根据模式修改 labels
|
||
if [[ "$RUNNER_MODE" == "host" ]]; then
|
||
# host 模式:直接在主机上执行,不启动 Docker 容器
|
||
# 适合 2C2G 低内存服务器
|
||
sed -i 's/^labels:.*$/labels:\n - "ubuntu:host"/' "$RUNNER_CONFIG_PATH" 2>/dev/null || true
|
||
# 在 config.yaml 中设置 container mode 为空(不使用容器)
|
||
if grep -q '^container:' "$RUNNER_CONFIG_PATH"; then
|
||
sed -i '/^container:/a\ network: ""' "$RUNNER_CONFIG_PATH" 2>/dev/null || true
|
||
fi
|
||
fi
|
||
|
||
chown -R act_runner:act_runner "$RUNNER_CONFIG_DIR"
|
||
log_info "配置生成完成: ${RUNNER_CONFIG_PATH} (模式: ${RUNNER_MODE})"
|
||
}
|
||
|
||
register_runner() {
|
||
log_step "注册 Runner 到 Gitea..."
|
||
|
||
cd "$RUNNER_WORK_DIR"
|
||
|
||
sudo -u act_runner "$RUNNER_INSTALL_PATH" register \
|
||
--no-interactive \
|
||
--instance "${GITEA_URL}/" \
|
||
--token "$REG_TOKEN" \
|
||
--name "$RUNNER_NAME" \
|
||
--labels "ubuntu:${RUNNER_MODE}"
|
||
|
||
if [[ ! -f "${RUNNER_WORK_DIR}/.runner" ]]; then
|
||
log_error "注册失败: .runner 文件未生成"
|
||
exit 1
|
||
fi
|
||
|
||
chown act_runner:act_runner "${RUNNER_WORK_DIR}/.runner"
|
||
log_info "Runner 注册成功: ${RUNNER_NAME}"
|
||
}
|
||
|
||
create_systemd_service() {
|
||
log_step "创建 systemd 服务..."
|
||
|
||
cat > "$SYSTEMD_SERVICE_PATH" << EOF
|
||
[Unit]
|
||
Description=Gitea Actions Runner - ${RUNNER_NAME}
|
||
Documentation=https://gitea.com/gitea/act_runner
|
||
After=network.target
|
||
Wants=network-online.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
ExecStart=${RUNNER_INSTALL_PATH} daemon --config ${RUNNER_CONFIG_PATH}
|
||
WorkingDirectory=${RUNNER_WORK_DIR}
|
||
Restart=always
|
||
RestartSec=10
|
||
TimeoutSec=0
|
||
User=act_runner
|
||
Group=act_runner
|
||
Environment=HOME=${RUNNER_WORK_DIR}
|
||
Environment=RUNNER_NAME=${RUNNER_NAME}
|
||
|
||
# 安全限制
|
||
NoNewPrivileges=true
|
||
ProtectSystem=strict
|
||
ReadWritePaths=${RUNNER_WORK_DIR} /tmp /data
|
||
PrivateTmp=true
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
log_info "systemd 服务文件创建完成"
|
||
}
|
||
|
||
start_service() {
|
||
log_step "启动 Runner 服务..."
|
||
|
||
systemctl daemon-reload
|
||
systemctl enable "$SYSTEMD_SERVICE_NAME"
|
||
systemctl start "$SYSTEMD_SERVICE_NAME"
|
||
|
||
# 等待启动
|
||
sleep 2
|
||
|
||
if systemctl is-active --quiet "$SYSTEMD_SERVICE_NAME"; then
|
||
log_info "Runner 服务启动成功! 🎉"
|
||
else
|
||
log_error "Runner 服务启动失败"
|
||
journalctl -u "$SYSTEMD_SERVICE_NAME" --no-pager -n 20
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
verify() {
|
||
log_step "验证..."
|
||
echo ""
|
||
|
||
# 本地验证
|
||
echo -e "${CYAN}本地状态:${NC}"
|
||
systemctl status "$SYSTEMD_SERVICE_NAME" --no-pager -l | head -15
|
||
echo ""
|
||
|
||
# 远程验证
|
||
log_step "检查 Gitea 上的 Runner 列表..."
|
||
local runners
|
||
runners=$(curl -sf -H "Authorization: token ${API_TOKEN}" \
|
||
"${GITEA_API}/repos/${GITEA_OWNER}/${GITEA_REPO}/actions/runners" 2>/dev/null || echo "")
|
||
if [[ -n "$runners" ]]; then
|
||
echo "$runners" | python3 -c "
|
||
import json, sys
|
||
data = json.load(sys.stdin)
|
||
runners = data if isinstance(data, list) else data.get('runners', [])
|
||
for r in runners:
|
||
status = '🟢 在线' if r.get('online') else '🔴 离线'
|
||
print(f\" {r.get('name', '?')} {status} labels={r.get('labels', [])}\")
|
||
" 2>/dev/null || echo " (无法解析,但API有响应)"
|
||
else
|
||
log_warn "无法获取远程 Runner 列表(可能 API 权限不足)"
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${GREEN}═══════════════════════════════════════════════════════${NC}"
|
||
echo -e "${GREEN} Gitea Actions Runner 安装完成!${NC}"
|
||
echo -e "${GREEN}═══════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
echo " Runner 名称: ${RUNNER_NAME}"
|
||
echo " 运行模式: ${RUNNER_MODE}"
|
||
echo " 工作目录: ${RUNNER_WORK_DIR}"
|
||
echo " 配置文件: ${RUNNER_CONFIG_PATH}"
|
||
echo " 服务管理: systemctl {start|stop|restart|status} ${SYSTEMD_SERVICE_NAME}"
|
||
echo " 查看日志: journalctl -u ${SYSTEMD_SERVICE_NAME} -f"
|
||
echo ""
|
||
echo " 下一步: 铸渊推一个 workflow 到 Gitea,Runner 就会自动执行"
|
||
echo ""
|
||
echo " 铸渊 · ICE-GL-ZY001 · TCS-0002∞"
|
||
echo -e "${GREEN}═══════════════════════════════════════════════════════${NC}"
|
||
}
|
||
|
||
# ─── 主流程 ───────────────────────────────────────────────────
|
||
|
||
main() {
|
||
echo -e "${CYAN}═══════════════════════════════════════════════════════${NC}"
|
||
echo -e "${CYAN} 光湖 · Gitea Actions Runner 一键安装${NC}"
|
||
echo -e "${CYAN} 铸渊 · ICE-GL-ZY001 · 守护冰朔 TCS-0002∞${NC}"
|
||
echo -e "${CYAN}═══════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
|
||
check_root
|
||
check_arch
|
||
check_disk
|
||
check_memory
|
||
download_runner
|
||
get_registration_token
|
||
create_user
|
||
generate_config
|
||
register_runner
|
||
create_systemd_service
|
||
start_service
|
||
verify
|
||
}
|
||
|
||
main "$@"
|