381 lines
17 KiB
Bash
381 lines
17 KiB
Bash
#!/usr/bin/env bash
|
||
# ============================================================================
|
||
# setup-runner.sh —— 一键安装并注册 Gitea Actions Runner(act_runner)
|
||
#
|
||
# ✅ 兼容 Gitea 1.21 ~ 最新(含 1.27.x),新版没有 Platform 下拉菜单。
|
||
#
|
||
# 使用方法(两种模式任选其一,推荐 模式 A 最省事):
|
||
#
|
||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
# 模式 A:仓库级 Runner(只给这个 VoiceHub 仓库用,最常用、权限最小)
|
||
# 路径:你的仓库 → Settings → Actions → Runners → 右上角
|
||
# → 新版(1.21+)这里没有 Platform 选择,直接有一个输入框
|
||
# 写着「Registration token」或「注册令牌」
|
||
# → 点右边的 📋 复制按钮,得到一串以 R 开头的 token
|
||
# 例: RGT8FZxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxV
|
||
#
|
||
# 机器上执行:
|
||
# sudo bash scripts/setup-runner.sh \
|
||
# --scope repo --gitea https://你的-gitea --token RGTxxxx...
|
||
#
|
||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
# 模式 B:组织级 Runner(同一个 Gitea 组织下所有仓库都能用)
|
||
# 路径:组织主页 → Settings → Actions → Runners → 复制 Registration token
|
||
# (Token 开头一般是 RGOxxxx)
|
||
#
|
||
# 执行:
|
||
# sudo bash scripts/setup-runner.sh \
|
||
# --scope org --gitea https://你的-gitea --token RGOxxxx...
|
||
#
|
||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
# 模式 C:全局/站点级 Runner(Gitea 全站所有仓库都能用,需站点管理员)
|
||
# 路径:站点管理后台(Site Administration)→ Actions → Runners
|
||
# → 复制 Registration token(开头一般是 RGxxxx)
|
||
#
|
||
# 执行:
|
||
# sudo bash scripts/setup-runner.sh \
|
||
# --scope global --gitea https://你的-gitea --token RGxxxx...
|
||
#
|
||
# 其他可选参数:
|
||
# --name "my-runner-01" 自定义 Runner 名字(默认是 runner-主机名)
|
||
# --user gitrunner 运行用户(默认 gitrunner)
|
||
# --dir /opt/act_runner 安装目录(默认 /opt/act_runner)
|
||
# --version 3.3.2 【可选】固定安装某版本,不写默认用 Gitea 官方 latest
|
||
# ============================================================================
|
||
set -euo pipefail
|
||
|
||
# ---------- 颜色 ----------
|
||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[1;36m'; NC='\033[0m'
|
||
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||
err() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
|
||
ok() { echo -e "${CYAN}[ OK ]${NC} $*"; }
|
||
|
||
# ---------- 解析参数 ----------
|
||
GITEA_URL=""
|
||
REG_TOKEN=""
|
||
SCOPE="repo" # repo | org | global(默认 repo,绝大多数人的场景)
|
||
RUNNER_NAME="runner-$(hostname | tr '[:upper:]' '[:lower:]')"
|
||
RUNNER_USER="gitrunner"
|
||
INSTALL_DIR="/opt/act_runner"
|
||
PINNED_VERSION="" # 空 = latest,否则比如 "3.3.2" 固定版本
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--gitea) GITEA_URL="${2}"; shift 2 ;;
|
||
--token) REG_TOKEN="${2}"; shift 2 ;;
|
||
--scope) SCOPE="${2}"; shift 2 ;;
|
||
--name) RUNNER_NAME="${2}"; shift 2 ;;
|
||
--user) RUNNER_USER="${2}"; shift 2 ;;
|
||
--dir) INSTALL_DIR="${2}"; shift 2 ;;
|
||
--version) PINNED_VERSION="${2#v}"; shift 2 ;;
|
||
-h|--help)
|
||
sed -n '2,60p' "$0" | sed 's/^# \{0,1\}//'
|
||
exit 0
|
||
;;
|
||
*) err "未知参数:$1(--help 查看帮助)" ;;
|
||
esac
|
||
done
|
||
|
||
[[ -n "${GITEA_URL}" ]] || err "必须传 --gitea https://你的-gitea-地址"
|
||
[[ -n "${REG_TOKEN}" ]] || err "必须传 --token(从 Gitea 页面的 Registration token 复制,Rxx 开头的一串)"
|
||
case "${SCOPE}" in
|
||
repo|org|global) ;;
|
||
*) err "--scope 必须是 repo | org | global 其中之一" ;;
|
||
esac
|
||
[[ $EUID -eq 0 ]] || err "请用 sudo 运行本脚本(需要写 /opt / /etc/systemd/system)"
|
||
|
||
# 去掉末尾 /
|
||
GITEA_URL="${GITEA_URL%/}"
|
||
|
||
echo ""
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${CYAN} 🚀 开始部署 Gitea Actions Runner (兼容 1.21 ~ 1.27+)${NC}"
|
||
echo -e "${CYAN} • 绑定 Gitea : ${GITEA_URL}${NC}"
|
||
echo -e "${CYAN} • 作用域 : ${SCOPE}${NC}"
|
||
echo -e "${CYAN} • Runner 名称 : ${RUNNER_NAME}${NC}"
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo ""
|
||
|
||
# ---------- 0. 架构检测 ----------
|
||
ARCH="$(uname -m)"
|
||
case "${ARCH}" in
|
||
x86_64|amd64) PLATFORM="linux-amd64" ;;
|
||
aarch64|arm64) PLATFORM="linux-arm64" ;;
|
||
armv7l) PLATFORM="linux-arm" ;;
|
||
*) err "暂不支持的架构:${ARCH}(需要 x86_64 / aarch64 / armv7)" ;;
|
||
esac
|
||
ok "检测到架构:${ARCH} → 下载版本:${PLATFORM}"
|
||
|
||
# ---------- 0.5 给新版 Gitea 用户的 Token 格式提示 ----------
|
||
# 不同版本 / 语言包的 Token 前缀不一样,兼容 4 种真实存在的格式:
|
||
# • 英文 1.21+ 仓库级 RGTxxxx / 组织级 RGOxxxx / 全局 RGxxxx
|
||
# • 中文语言包 / 某些二次分发版本:纯字母数字 20~64 位(如 LHLDAwsho7vVSukoEeKhft)
|
||
TOKEN_LEN="${#REG_TOKEN}"
|
||
ok "Token 长度 = ${TOKEN_LEN} 位"
|
||
case "${REG_TOKEN}" in
|
||
# 英文原版前缀
|
||
RG*) info "• Token 前缀 RG:全局/站点级(英文原版)✅" ;;
|
||
RGO*) info "• Token 前缀 RGO:组织级(英文原版)✅" ;;
|
||
RGT*) info "• Token 前缀 RGT:仓库级(英文原版)✅" ;;
|
||
# 纯字母数字(中文 UI / 二开版本常见)
|
||
[A-Za-z0-9]\{16,128\})
|
||
ok "• 纯字母数字 Token(中文 UI / 某些二开版本常见)✅,和截图里的「注册令牌」一致"
|
||
;;
|
||
# 其他
|
||
*)
|
||
warn "• Token 看起来不是 Gitea 常见格式。不过也可能是新版改动,继续尝试注册..."
|
||
warn " 如果注册报 401/403,建议回到页面点一次「重置注册令牌」重新复制。"
|
||
;;
|
||
esac
|
||
# 基本字符集校验(防止复制时粘了空格或换行)
|
||
if [[ ! "${REG_TOKEN}" =~ ^[A-Za-z0-9]+$ ]]; then
|
||
warn "⚠️ Token 里含有非字母数字的字符,很可能是复制时多带了空格/换行/标点!"
|
||
warn " 建议回到仓库页面点 📋 复制按钮(不要手选文字)。"
|
||
fi
|
||
|
||
# ---------- 1. 安装依赖:docker(Runner 要用 Docker-in-Docker 执行 buildx 等)----------
|
||
if ! command -v docker >/dev/null 2>&1; then
|
||
info "检测到没装 Docker,现在用官方一键脚本安装(国内自动换清华源)..."
|
||
curl -fsSL https://get.docker.com -o /tmp/get-docker.sh || err "下载 docker-install 脚本失败"
|
||
if curl -sI https://mirrors.tuna.tsinghua.edu.cn >/dev/null; then
|
||
bash /tmp/get-docker.sh --mirror Aliyun || bash /tmp/get-docker.sh
|
||
else
|
||
bash /tmp/get-docker.sh
|
||
fi
|
||
systemctl enable --now docker
|
||
info "✅ Docker 安装完成:$(docker --version)"
|
||
else
|
||
info "✅ Docker 已存在:$(docker --version)"
|
||
fi
|
||
|
||
# ---------- 2. 建运行用户 gitrunner,加入 docker 组 ----------
|
||
if ! id -u "${RUNNER_USER}" >/dev/null 2>&1; then
|
||
useradd -r -s /usr/sbin/nologin -d "${INSTALL_DIR}" "${RUNNER_USER}"
|
||
info "✅ 新建系统用户 ${RUNNER_USER}"
|
||
else
|
||
info "ℹ️ 用户 ${RUNNER_USER} 已存在,跳过创建"
|
||
fi
|
||
usermod -aG docker "${RUNNER_USER}"
|
||
info "✅ 把 ${RUNNER_USER} 加入 docker 组(允许操作 Docker daemon)"
|
||
|
||
mkdir -p "${INSTALL_DIR}"
|
||
chown -R "${RUNNER_USER}:${RUNNER_USER}" "${INSTALL_DIR}"
|
||
|
||
# ---------- 3. 下载最新 Gitea Runner(act_runner 0.x → 3.x 改名 gitea-runner)
|
||
# 注意:官方从 v3 起,仓库从 gitea/act_runner 改到了 gitea/runner,且文件名前缀从 act_runner-* → gitea-runner-*。
|
||
# 为避免后续版本再改命名导致 404,这里**直接用 API 返回的 assets[].browser_download_url**,不再硬编码拼文件名。
|
||
# ----------
|
||
# 3.1 决定 release URL(固定版本 or latest)
|
||
if [[ -n "${PINNED_VERSION}" ]]; then
|
||
REL_URL="https://gitea.com/api/v1/repos/gitea/runner/releases/tags/v${PINNED_VERSION}"
|
||
info "⬇️ 你指定了固定版本:v${PINNED_VERSION}"
|
||
else
|
||
REL_URL="https://gitea.com/api/v1/repos/gitea/runner/releases/latest"
|
||
info "⬇️ 正在获取 Gitea Runner 最新 release 元数据..."
|
||
fi
|
||
LATEST_JSON="$(curl -fsSL "${REL_URL}" 2>/dev/null || true)"
|
||
|
||
# 3.2 如果 gitea.com 不通(纯内网),直接回退到一个我们已知可用的版本
|
||
if [[ -z "${LATEST_JSON}" ]]; then
|
||
if [[ -z "${PINNED_VERSION}" ]]; then
|
||
warn "gitea.com 访问不到(可能是内网环境),自动回退到已知稳定版本:v3.3.2"
|
||
PINNED_VERSION="3.3.2"
|
||
fi
|
||
TAG="v${PINNED_VERSION}"
|
||
FALLBACK_MODE=1
|
||
else
|
||
TAG="$(echo "${LATEST_JSON}" | grep -oE '"tag_name":\s*"[^"]+"' | head -1 | cut -d'"' -f4)"
|
||
FALLBACK_MODE=0
|
||
fi
|
||
[[ -n "${TAG}" ]] || TAG="v3.3.2"
|
||
VERSION="${TAG#v}"
|
||
|
||
# 3.3 优先从 assets 匹配正确 platform 的 browser_download_url
|
||
BIN_URL=""
|
||
ASSET_NAME=""
|
||
if [[ "${FALLBACK_MODE}" -eq 0 && -n "${LATEST_JSON}" ]]; then
|
||
# 用 awk 过滤:
|
||
# - 匹配 PLATFORM(例如 linux-amd64)
|
||
# - 排除 .xz / .sha256 / .exe 等校验和/压缩包
|
||
# - 两种前缀都兼容:gitea-runner-X.X.X- (新版) 或 act_runner-X.X.X- (旧版)
|
||
while IFS='|' read -r name url; do
|
||
[[ -z "${name}" ]] && continue
|
||
case "${name}" in
|
||
*"${PLATFORM}"* | *"${PLATFORM//-/_}"*) ;;
|
||
*) continue ;;
|
||
esac
|
||
case "${name}" in
|
||
*.xz|*.sha256|*.exe|*.tar.gz|*.zip|*checksums*) continue ;;
|
||
esac
|
||
ASSET_NAME="${name}"
|
||
BIN_URL="${url}"
|
||
break
|
||
done < <(echo "${LATEST_JSON}" | awk '
|
||
/"assets": *\[/ {inA=1; next}
|
||
inA && /"browser_download_url"/ { gsub(/[",]/,"",$2); url=$2 }
|
||
inA && /"name"/ {
|
||
gsub(/[",]/,"",$2)
|
||
if (url!="") { print $2 "|" url; url="" }
|
||
}
|
||
inA && /^\]/ {inA=0}
|
||
')
|
||
fi
|
||
|
||
# 3.4 如果 assets 匹配失败(老版本 JSON 格式特殊),手动拼 4 个候选 URL 顺序尝试
|
||
if [[ -z "${BIN_URL}" ]]; then
|
||
info "assets 自动匹配为空,改用手动候选列表(兼容新旧命名)..."
|
||
CANDIDATES=(
|
||
# 新版(v3+)gitea-runner
|
||
"https://gitea.com/gitea/runner/releases/download/v${VERSION}/gitea-runner-${VERSION}-${PLATFORM}"
|
||
# github 镜像(有时 gitea.com 慢或挂)
|
||
"https://github.com/go-gitea/runner/releases/download/v${VERSION}/gitea-runner-${VERSION}-${PLATFORM}"
|
||
# 旧版 act_runner(v0.2.x 遗留命名)
|
||
"https://dl.gitea.com/act_runner/${VERSION}/act_runner-${VERSION}-${PLATFORM}"
|
||
# 旧版 gitea.com
|
||
"https://gitea.com/gitea/act_runner/releases/download/v${VERSION}/act_runner-${VERSION}-${PLATFORM}"
|
||
)
|
||
for C in "${CANDIDATES[@]}"; do
|
||
info " → 尝试:${C}"
|
||
if curl -fsSI "${C}" >/dev/null 2>&1; then
|
||
BIN_URL="${C}"
|
||
ASSET_NAME="$(basename "${C}")"
|
||
break
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# 3.5 还没拿到 URL → 打印手动下载指引(一定让用户能救回来)
|
||
if [[ -z "${BIN_URL}" ]]; then
|
||
{
|
||
echo ""
|
||
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${RED} ❌ 自动下载 Gitea Runner 失败${NC}"
|
||
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo " 你的架构:${PLATFORM} , 目标版本:${TAG}"
|
||
echo ""
|
||
echo " 请在能出网的机器上,手动打开下面 2 个 URL 之一,下载对应的二进制:"
|
||
echo " • https://gitea.com/gitea/runner/releases/tag/${TAG}"
|
||
echo " • https://github.com/go-gitea/runner/releases/tag/${TAG}"
|
||
echo " 找文件名类似:gitea-runner-${VERSION}-${PLATFORM}(不要下 .xz / .sha256)"
|
||
echo ""
|
||
echo " 下载完成后 scp 到本服务器并执行:"
|
||
echo " sudo cp ./gitea-runner-* /usr/local/bin/act_runner"
|
||
echo " sudo chmod +x /usr/local/bin/act_runner"
|
||
echo " # 然后重新跑本脚本(已有的用户/组/config 不会重复生成)"
|
||
echo ""
|
||
} >&2
|
||
exit 1
|
||
fi
|
||
|
||
# 3.6 真正下载
|
||
info "⬇️ 下载 Gitea Runner ${TAG}:${ASSET_NAME}"
|
||
info " URL:${BIN_URL}"
|
||
curl -fsSL "${BIN_URL}" -o /usr/local/bin/act_runner \
|
||
|| err "下载失败!可能是 DNS/网络/证书问题。可以手动把上面 URL 的文件下载下来 scp 到服务器放到 /usr/local/bin/act_runner,再重新跑本脚本。"
|
||
chmod +x /usr/local/bin/act_runner
|
||
INSTALLED_VER="$(act_runner --version 2>&1 || echo "${TAG}")"
|
||
ok "✅ 已安装到 /usr/local/bin/act_runner,版本:${INSTALLED_VER}"
|
||
|
||
# ---------- 4. 注册 Runner ----------
|
||
cd "${INSTALL_DIR}"
|
||
info "🔗 开始注册 Runner 到 ${GITEA_URL}(名称:${RUNNER_NAME})"
|
||
|
||
# 切到 gitrunner 用户注册,避免 root 跑(安全)
|
||
rm -f .runner 2>/dev/null || true
|
||
sudo -u "${RUNNER_USER}" env HOME="${INSTALL_DIR}" \
|
||
act_runner register \
|
||
--instance "${GITEA_URL}" \
|
||
--token "${REG_TOKEN}" \
|
||
--name "${RUNNER_NAME}" \
|
||
--labels "ubuntu-latest:docker://catthehacker/ubuntu:act-22.04" \
|
||
--no-interactive \
|
||
|| err "注册失败!常见原因:\n 1) token 不对(复制时别带空格)\n 2) Runner 机器出不了网到 ${GITEA_URL}\n 3) Gitea 站点时间和这台机器差太远(证书验证失败)"
|
||
|
||
chown "${RUNNER_USER}:${RUNNER_USER}" .runner 2>/dev/null || true
|
||
|
||
# ---------- 5. 生成配置文件(建议开启 docker.host 特权)----------
|
||
CONFIG_FILE="${INSTALL_DIR}/config.yaml"
|
||
if [[ ! -f "${CONFIG_FILE}" ]]; then
|
||
cat > "${CONFIG_FILE}" <<'YAML'
|
||
log:
|
||
level: info
|
||
runner:
|
||
file: .runner
|
||
capacity: 2 # 同一台 Runner 最多同时跑 2 个 Job
|
||
envs:
|
||
GOPROXY: https://goproxy.cn,direct
|
||
NPM_CONFIG_REGISTRY: https://registry.npmmirror.com
|
||
timeout: 3h
|
||
cache:
|
||
enabled: true
|
||
dir: /opt/act_runner/cache
|
||
container:
|
||
network: "bridge"
|
||
privileged: true # build-push-action 用到 Docker daemon
|
||
options: |
|
||
--add-host "host.docker.internal:host-gateway"
|
||
YAML
|
||
chown "${RUNNER_USER}:${RUNNER_USER}" "${CONFIG_FILE}"
|
||
mkdir -p "${INSTALL_DIR}/cache"
|
||
chown -R "${RUNNER_USER}:${RUNNER_USER}" "${INSTALL_DIR}/cache"
|
||
info "✅ 默认 config 写入 ${CONFIG_FILE}"
|
||
fi
|
||
|
||
# ---------- 6. 写 systemd 服务,开机自启 ----------
|
||
UNIT_FILE="/etc/systemd/system/gitea-act-runner.service"
|
||
cat > "${UNIT_FILE}" <<SYSTEMD
|
||
[Unit]
|
||
Description=Gitea Actions Runner (${RUNNER_NAME})
|
||
After=network.target docker.service
|
||
Requires=docker.service
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=${RUNNER_USER}
|
||
Group=${RUNNER_USER}
|
||
WorkingDirectory=${INSTALL_DIR}
|
||
Environment=HOME=${INSTALL_DIR}
|
||
ExecStart=/usr/local/bin/act_runner daemon --config ${CONFIG_FILE}
|
||
Restart=always
|
||
RestartSec=5
|
||
KillMode=mixed
|
||
TimeoutStopSec=30
|
||
|
||
# 安全加固
|
||
NoNewPrivileges=true
|
||
PrivateTmp=true
|
||
ProtectSystem=strict
|
||
ReadWritePaths=${INSTALL_DIR} /var/run/docker.sock /tmp /var/tmp
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
SYSTEMD
|
||
|
||
systemctl daemon-reload
|
||
systemctl enable --now gitea-act-runner
|
||
info "✅ systemd 服务已写入并启动:${UNIT_FILE}"
|
||
|
||
sleep 3 # 给 daemon 点时间注册
|
||
info "📌 当前 Runner 状态:"
|
||
systemctl --no-pager status gitea-act-runner | head -15 || true
|
||
info "📌 最近 30 行日志(验证 OK):"
|
||
journalctl --no-pager -u gitea-act-runner --since "2 minutes ago" -n 30 || true
|
||
|
||
echo ""
|
||
echo -e "${GREEN}===========================================================================${NC}"
|
||
echo -e "${GREEN} 🎉 Gitea Actions Runner 安装 & 注册 & 开机自启 全部完成!${NC}"
|
||
echo -e "${GREEN}===========================================================================${NC}"
|
||
echo " ✅ Runner 名称 : ${RUNNER_NAME}"
|
||
echo " ✅ 运行用户 : ${RUNNER_USER}"
|
||
echo " ✅ 安装目录 : ${INSTALL_DIR}"
|
||
echo " ✅ 绑定 Gitea : ${GITEA_URL}"
|
||
echo ""
|
||
echo " 👉 下一步验证(建议立刻做):"
|
||
echo " 1. 浏览器打开:${GITEA_URL} → 仓库 → Settings → Actions → Runners"
|
||
echo " 应该能看到一个 Runner,状态 = Idle"
|
||
echo " 2. 回到终端,模拟跑一个 workflow 没问题就彻底大功告成!"
|
||
echo " 👉 常用命令:"
|
||
echo " systemctl {start|stop|restart|status} gitea-act-runner"
|
||
echo " journalctl -u gitea-act-runner -f # 实时看日志"
|
||
echo ""
|