Files
opencode/skill/gitea/create-runner.md

1105 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
description: 创建并启动 Gitea Actions Runner 的完整脚本
agent: general
---
# Gitea Actions Runner 创建工具
本文档提供了多种创建 Gitea Actions Runner 的方式,支持 Host 模式和 Docker 模式。
## 📦 快速使用
### 方法一:直接执行(推荐)
复制以下命令到终端执行,可指定参数:
```bash
# 创建 Docker Runner默认
curl -s https://git.shigongcao.com/ai/opencode/raw/branch/main/skill/gitea/create-runner.md | \
sed -n '/^```bash$/,/^```$/p' | sed '1d;$d' | \
bash -s -- --docker --token "你的令牌"
# 创建 Host Runner
curl -s https://git.shigongcao.com/ai/opencode/raw/branch/main/skill/gitea/create-runner.md | \
sed -n '/^```bash$/,/^```$/p' | sed '1d;$d' | \
bash -s -- --host --token "你的令牌"
# 同时创建 Docker 和 Host Runner
curl -s https://git.shigongcao.com/raw/ai/opencode/main/skill/gitea/create-runner.md | \
sed -n '/^```bash$/,/^```$/p' | sed '1d;$d' | \
bash -s -- --all --token "你的令牌" --gitea-url "https://git.shigongcao.com"
```
### 方法二:定义快捷函数
将以下函数添加到 `~/.bashrc``~/.zshrc`
```bash
# Gitea Runner 创建函数
gitea-create-runner() {
local mode="${1:-docker}"
local token="${2:-$GITEA_TOKEN}"
local gitea_url="${3:-https://git.shigongcao.com}"
local name="${4:-runner-$(hostname -s)-$mode}"
echo "创建 $mode runner: $name"
# 临时脚本路径
local script_path="/tmp/create_runner_$$.sh"
# 生成脚本
cat > "$script_path" << 'EOF'
#!/bin/bash
set -e
# 参数解析
MODE="docker"
TOKEN=""
GITEA_URL="https://git.shigongcao.com"
RUNNER_NAME="runner-$(hostname -s)-docker"
ALL_MODE=false
while [[ $# -gt 0 ]]; do
case $1 in
--docker) MODE="docker" ;;
--host) MODE="host" ;;
--all) ALL_MODE=true ;;
--token) TOKEN="$2"; shift ;;
--gitea-url) GITEA_URL="$2"; shift ;;
--name) RUNNER_NAME="$2"; shift ;;
*) echo "未知参数: $1"; exit 1 ;;
esac
shift
done
if [ -z "$TOKEN" ]; then
echo "❌ 必须提供 --token 参数"
exit 1
fi
create_runner() {
local mode=$1
local name=$2
echo "创建 $mode runner: $name"
if [ "$mode" = "docker" ]; then
# Docker runner 创建逻辑
if ! docker info >/dev/null 2>&1; then
echo "❌ Docker 未运行"
return 1
fi
local runner_dir="$HOME/.config/gitea/runners/$name"
mkdir -p "$runner_dir"/{data,cache,workspace}
cat > "$runner_dir/config.yaml" << YAML
log:
level: info
runner:
file: /data/.runner
capacity: 2
timeout: 3h
shutdown_timeout: 30s
insecure: false
fetch_timeout: 5s
fetch_interval: 2s
labels: []
cache:
enabled: true
dir: "/data/cache"
host: "host.docker.internal"
port: 9040
container:
network: "host"
privileged: false
options:
workdir_parent: /data/workspace
valid_volumes: []
docker_host: ""
force_pull: false
host:
workdir_parent: /data/workspace
YAML
local labels="ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye,ubuntu-20.04:docker://node:16-buster,linux:docker://node:16-bullseye"
docker run -d \
--name "$name" \
--restart always \
--network host \
-v "$runner_dir/config.yaml:/config.yaml" \
-v "$runner_dir/data:/data" \
-v "/var/run/docker.sock:/var/run/docker.sock" \
-e GITEA_INSTANCE_URL="$GITEA_URL" \
-e GITEA_RUNNER_REGISTRATION_TOKEN="$TOKEN" \
-e GITEA_RUNNER_NAME="$name" \
-e GITEA_RUNNER_LABELS="$labels" \
gitea/act_runner:latest daemon --config /config.yaml
echo "✅ Docker Runner 已启动: $name"
else
# Host runner 创建逻辑
if ! command -v act_runner &> /dev/null; then
echo "❌ act_runner 未安装"
return 1
fi
local runner_dir="$HOME/.config/gitea/runners/$name"
mkdir -p "$runner_dir"/{cache,workspace}
local OS=$(uname -s)
local ARCH=$(uname -m)
case "$OS" in
Darwin) os_label="macOS" ;;
Linux) os_label="ubuntu" ;;
*) os_label="unknown" ;;
esac
case "$ARCH" in
arm64|aarch64) arch_label="ARM64" ;;
x86_64) arch_label="x64" ;;
*) arch_label="unknown" ;;
esac
local combined=$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]')
local labels="self-hosted:host,${os_label}:host,${arch_label}:host,${combined}:host"
cat > "$runner_dir/config.yaml" << YAML
log:
level: info
runner:
file: .runner
capacity: 1
timeout: 3h
shutdown_timeout: 30s
insecure: false
fetch_timeout: 5s
fetch_interval: 2s
labels: []
cache:
enabled: true
dir: "$runner_dir/cache"
host: "127.0.0.1"
port: 0
host:
workdir_parent: "$runner_dir/workspace"
YAML
act_runner register \
--config "$runner_dir/config.yaml" \
--instance "$GITEA_URL" \
--token "$TOKEN" \
--name "$name" \
--labels "$labels" \
--no-interactive
nohup act_runner daemon --config "$runner_dir/config.yaml" \
> "$runner_dir/runner.log" 2>&1 &
local pid=$!
sleep 2
if ps -p $pid > /dev/null 2>&1; then
echo $pid > "$runner_dir/pid"
echo "✅ Host Runner 已启动: $name (PID: $pid)"
else
echo "❌ Host Runner 启动失败"
return 1
fi
fi
}
if [ "$ALL_MODE" = true ]; then
create_runner "docker" "runner-$(hostname -s)-docker"
create_runner "host" "runner-$(hostname -s)-host"
else
create_runner "$MODE" "$RUNNER_NAME"
fi
EOF
chmod +x "$script_path"
"$script_path" --"$mode" --token "$token" --gitea-url "$gitea_url" --name "$name"
rm -f "$script_path"
}
# 使用示例:
# gitea-create-runner docker "wibnIxvgeyYj3D7d53VTQvNxv0UVqArBwAtPFBWD"
# gitea-create-runner host "wibnIxvgeyYj3D7d53VTQvNxv0UVqArBwAtPFBWD"
# gitea-create-runner all "wibnIxvgeyYj3D7d53VTQvNxv0UVqArBwAtPFBWD"
```
## 🔄 恢复离线 Runner
**场景**Runner 在 Gitea 服务器上被删除,但本地配置文件仍在。需要重新注册并上线。
**快速恢复命令**(针对 runner-Mac-mini4-host
```bash
cd ~/.config/gitea/runners/runner-Mac-mini4-host
# 停止旧进程
if [ -f pid ]; then kill $(cat pid) 2>/dev/null || true; fi
# 加载配置并重新注册
source ~/.config/gitea/config.env
# 获取令牌(需要 jq
token=$(curl -s -H "Authorization: token $GITEA_TOKEN" \
"${GITEA_URL}/api/v1/admin/runners/registration-token" | jq -r '.token')
# 生成标签
OS=$(uname -s); ARCH=$(uname -m)
case "$OS" in Darwin) os="macOS";; Linux) os="ubuntu";; *) os="unknown";; esac
case "$ARCH" in arm64|aarch64) arch="ARM64";; x86_64) arch="x64";; *) arch="unknown";; esac
labels="self-hosted:host,${os}:host,${arch}:host,$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]'):host"
# 重新注册并启动
act_runner register --config config.yaml --instance "$GITEA_URL" \
--token "$token" --name "runner-Mac-mini4-host" --labels "$labels" --no-interactive
nohup act_runner daemon --config config.yaml > runner.log 2>&1 &
echo $! > pid
echo "✅ Runner 恢复完成 (PID: $(cat pid))"
```
**说明**
- 确保 `config.yaml` 中的 `labels:` 配置为空(`labels: []`),注册时会使用命令行参数
- 如果全局令牌权限不足,需要获取组织令牌(参考 runner-management.md
- 恢复后 runner 会获得新的 ID但名称和 labels 保持不变
### 方法三:完整脚本
如果你需要更多自定义选项,可以使用下面的完整脚本:
## 脚本文件
你可以将以下内容保存为 `create_runner.sh` 并赋予执行权限 (`chmod +x create_runner.sh`)。
```bash
#!/bin/bash
# Gitea Runner Creation Script
# Generated by OpenCode Skill
# 支持命令行参数,无需交互,支持批量创建
set -e
# ==========================================
# 1. Parse Command Line Arguments
# ==========================================
RUNNER_MODE="docker" # 默认模式
GITEA_URL=""
GITEA_TOKEN=""
RUNNER_NAME=""
ALL_MODE=false
INTERACTIVE=false
SKIP_CHECKS=false
# 显示帮助
show_help() {
cat << EOF
Gitea Actions Runner 创建脚本
用法: $0 [选项]
选项:
--docker 创建 Docker runner (默认)
--host 创建 Host runner
--all 同时创建 Docker 和 Host runner
--token TOKEN Gitea 注册令牌 (必需)
--gitea-url URL Gitea 实例地址 (默认: https://git.shigongcao.com)
--name NAME Runner 名称 (默认: runner-\$(hostname)-模式)
--skip-checks 跳过依赖检查
--interactive 交互模式 (传统方式)
--help 显示此帮助信息
示例:
$0 --docker --token "wibnIxvgeyYj3D7d53VTQvNxv0UVqArBwAtPFBWD"
$0 --host --token "your_token" --gitea-url "https://git.example.com"
$0 --all --token "your_token"
$0 --all --token "your_token" --name "custom-runner"
直接使用技能:
# 从技能文档直接执行
bash <(curl -s https://git.shigongcao.com/ai/opencode/raw/main/skill/gitea/create-runner.md | sed -n '/^```bash$/,/^```$/p' | sed '1d;\$d') --all --token "your_token"
EOF
exit 0
}
# 解析参数
while [[ $# -gt 0 ]]; do
case $1 in
--docker) RUNNER_MODE="docker" ;;
--host) RUNNER_MODE="host" ;;
--all) ALL_MODE=true ;;
--token) GITEA_TOKEN="$2"; shift ;;
--gitea-url) GITEA_URL="$2"; shift ;;
--name) RUNNER_NAME="$2"; shift ;;
--skip-checks) SKIP_CHECKS=true ;;
--interactive) INTERACTIVE=true ;;
--help) show_help ;;
*) echo "❌ 未知参数: $1"; echo "使用 --help 查看帮助"; exit 1 ;;
esac
shift
done
# 检查必需参数
if [ -z "$GITEA_TOKEN" ] && [ "$INTERACTIVE" = false ]; then
echo "❌ 必须提供 --token 参数"
echo "使用 --help 查看帮助"
exit 1
fi
# ==========================================
# 2. Interactive Mode (向后兼容)
# ==========================================
if [ "$INTERACTIVE" = true ]; then
echo "请选择 Runner 运行模式:"
echo " 1. Host Mode (直接在宿主机运行,支持 macOS/iOS 原生构建)"
echo " 2. Docker Mode (在容器中运行,环境隔离,适合标准 Linux 构建)"
echo ""
read -p "请输入选项 [1/2] (默认 1): " mode_choice
if [ "$mode_choice" = "2" ]; then
RUNNER_MODE="docker"
echo "✅ 已选择: Docker Mode"
else
RUNNER_MODE="host"
echo "✅ 已选择: Host Mode"
fi
echo ""
fi
# ==========================================
# 3. Load Gitea Configuration
# ==========================================
# 如果未通过命令行提供,尝试从配置文件加载
if [ -z "$GITEA_URL" ] || [ -z "$GITEA_TOKEN" ]; then
config_file="$HOME/.config/gitea/config.env"
if [ -f "$config_file" ]; then
echo "从配置文件加载 Gitea 配置..."
source "$config_file"
fi
fi
# 如果仍然缺少 URL使用默认值
if [ -z "$GITEA_URL" ]; then
GITEA_URL="https://git.shigongcao.com"
echo "⚠️ 使用默认 Gitea URL: $GITEA_URL"
fi
# 检查必需参数
if [ -z "$GITEA_TOKEN" ]; then
echo "❌ 必须提供 Gitea 注册令牌"
echo " 使用方法: $0 --token YOUR_TOKEN [其他选项]"
echo " 或创建配置文件: $HOME/.config/gitea/config.env"
exit 1
fi
echo "✓ Gitea 配置:"
echo " URL: $GITEA_URL"
echo " Token: ${GITEA_TOKEN:0:8}..."
echo ""
# ==========================================
# 4. Generate Runner Name
# ==========================================
# 如果未指定名称,生成默认名称
if [ -z "$RUNNER_NAME" ]; then
hostname=$(hostname -s 2>/dev/null || echo "unknown")
if [ "$ALL_MODE" = true ]; then
# 批量模式会创建两个runner
echo "批量模式:将创建 docker 和 host runner"
else
RUNNER_NAME="runner-$hostname-$RUNNER_MODE"
echo "生成 Runner 名称: $RUNNER_NAME"
fi
else
echo "使用指定的 Runner 名称: $RUNNER_NAME"
fi
# 检查名称有效性(如果不是批量模式)
if [ "$ALL_MODE" = false ] && [[ ! "$RUNNER_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo "❌ Runner 名称无效 (仅限字母、数字、下划线、连字符)"
exit 1
fi
echo ""
read -p "请输入选项 [1/2] (默认 1): " mode_choice
if [ "$mode_choice" = "2" ]; then
RUNNER_MODE="docker"
echo "✅ 已选择: Docker Mode"
else
RUNNER_MODE="host"
echo "✅ 已选择: Host Mode"
fi
echo ""
fi
# Check dependencies based on mode
if [ "$RUNNER_MODE" = "host" ]; then
echo "检查 act_runner 安装状态..."
if command -v act_runner &> /dev/null; then
version=$(act_runner --version 2>&1 | head -n1)
echo "✓ act_runner 已安装: $version"
else
echo "⚠️ act_runner 未安装"
echo "正在使用 Homebrew 安装..."
if ! command -v brew &> /dev/null; then
echo "❌ 需要先安装 Homebrew"
exit 1
fi
brew install act_runner
if [ $? -eq 0 ]; then
echo "✓ act_runner 安装成功"
else
echo "❌ act_runner 安装失败"
exit 1
fi
fi
else
# Docker mode checks
echo "检查 Docker 环境..."
if command -v docker &> /dev/null; then
if docker info &> /dev/null; then
docker_ver=$(docker --version)
echo "✓ Docker 已安装并运行: $docker_ver"
else
echo "❌ Docker 已安装但未运行"
echo " 请启动 Docker Desktop"
exit 1
fi
else
echo "❌ Docker 未安装"
echo " Docker 模式需要预先安装 Docker"
echo " 请访问 https://www.docker.com/products/docker-desktop/ 下载安装"
exit 1
fi
fi
echo ""
# ==========================================
# 3. Load Gitea Configuration
# ==========================================
# 如果未通过命令行提供,尝试从配置文件加载
if [ -z "$GITEA_URL" ] || [ -z "$GITEA_TOKEN" ]; then
config_file="$HOME/.config/gitea/config.env"
if [ -f "$config_file" ]; then
echo "从配置文件加载 Gitea 配置..."
source "$config_file"
fi
fi
# 如果仍然缺少 URL使用默认值
if [ -z "$GITEA_URL" ]; then
GITEA_URL="https://git.shigongcao.com"
echo "⚠️ 使用默认 Gitea URL: $GITEA_URL"
fi
# 检查必需参数
if [ -z "$GITEA_TOKEN" ]; then
echo "❌ 必须提供 Gitea 注册令牌"
echo " 使用方法: $0 --token YOUR_TOKEN [其他选项]"
echo " 或创建配置文件: $HOME/.config/gitea/config.env"
exit 1
fi
echo "✓ Gitea 配置:"
echo " URL: $GITEA_URL"
echo " Token: ${GITEA_TOKEN:0:8}..."
echo ""
# ==========================================
# 4. Generate Runner Name
# ==========================================
# 如果未指定名称,生成默认名称
if [ -z "$RUNNER_NAME" ]; then
hostname=$(hostname -s 2>/dev/null || echo "unknown")
if [ "$ALL_MODE" = true ]; then
# 批量模式会创建两个runner
echo "批量模式:将创建 docker 和 host runner"
else
RUNNER_NAME="runner-$hostname-$RUNNER_MODE"
echo "生成 Runner 名称: $RUNNER_NAME"
fi
else
echo "使用指定的 Runner 名称: $RUNNER_NAME"
fi
# 检查名称有效性(如果不是批量模式)
if [ "$ALL_MODE" = false ] && [[ ! "$RUNNER_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo "❌ Runner 名称无效 (仅限字母、数字、下划线、连字符)"
exit 1
fi
echo ""
# ==========================================
# 5. Main Creation Function
# ==========================================
create_runner() {
local mode="$1"
local name="$2"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "创建 $mode runner: $name"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
runners_dir="$HOME/.config/gitea/runners"
runner_dir="$runners_dir/$name"
# 检查是否已存在
if [ "$mode" = "host" ]; then
if [ -d "$runner_dir" ]; then
echo "❌ Runner 目录已存在: $runner_dir"
return 1
fi
else
# Docker mode: check directory AND container
if [ -d "$runner_dir" ]; then
echo "❌ Runner 配置目录已存在: $runner_dir"
return 1
fi
if docker ps -a --format '{{.Names}}' | grep -q "^${name}$"; then
echo "❌ Docker 容器已存在: $name"
echo " 请先删除旧容器: docker rm -f $name"
return 1
fi
fi
# ==========================================
# 5.1 Check Dependencies
# ==========================================
if [ "$SKIP_CHECKS" = false ]; then
if [ "$mode" = "host" ]; then
echo "检查 act_runner 安装状态..."
if ! command -v act_runner &> /dev/null; then
echo "❌ act_runner 未安装"
echo " 请安装: brew install act_runner"
return 1
fi
version=$(act_runner --version 2>&1 | head -n1)
echo "✓ act_runner 已安装: $version"
else
echo "检查 Docker 环境..."
if ! command -v docker &> /dev/null; then
echo "❌ Docker 未安装"
return 1
fi
if ! docker info &> /dev/null; then
echo "❌ Docker 已安装但未运行"
return 1
fi
docker_ver=$(docker --version)
echo "✓ Docker 已安装并运行: $docker_ver"
fi
fi
# ==========================================
# 5.2 Detect System Environment & Labels
# ==========================================
echo "生成 Labels..."
if [ "$mode" = "host" ]; then
OS=$(uname -s)
case "$OS" in
Darwin) os_label="macOS" ;;
Linux) os_label="ubuntu" ;;
*) os_label="unknown" ;;
esac
ARCH=$(uname -m)
case "$ARCH" in
arm64|aarch64) arch_label="ARM64" ;;
x86_64) arch_label="x64" ;;
*) arch_label="unknown" ;;
esac
combined=$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]')
labels="self-hosted:host,${os_label}:host,${arch_label}:host,${combined}:host"
else
# Docker mode uses standard labels mapping to images
# Format: label:docker://image
labels="ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye,ubuntu-20.04:docker://node:16-buster,linux:docker://node:16-bullseye"
fi
echo "✓ Labels ($mode):"
echo " $labels"
echo ""
# ==========================================
# 5.3 Create Runner Directory
# ==========================================
echo "创建 Runner 目录..."
if [ "$mode" = "host" ]; then
mkdir -p "$runner_dir"/{cache,workspace}
else
mkdir -p "$runner_dir"/{data,cache,workspace}
fi
echo "✓ 目录: $runner_dir"
echo ""
# ==========================================
# 5.4 Create Configuration File
# ==========================================
echo "创建配置文件..."
if [ "$mode" = "host" ]; then
# Host Mode Config
cat > "$runner_dir/config.yaml" << EOF
log:
level: info
runner:
file: .runner
capacity: 1
timeout: 3h
shutdown_timeout: 30s
insecure: false
fetch_timeout: 5s
fetch_interval: 2s
labels: []
cache:
enabled: true
dir: "$runner_dir/cache"
host: "127.0.0.1"
port: 0
host:
workdir_parent: "$runner_dir/workspace"
EOF
echo "注册 Host Runner..."
act_runner register \
--config "$runner_dir/config.yaml" \
--instance "$GITEA_URL" \
--token "$GITEA_TOKEN" \
--name "$name" \
--labels "$labels" \
--no-interactive
if [ $? -ne 0 ]; then
echo "❌ 注册失败"
return 1
fi
echo "启动后台进程..."
nohup act_runner daemon --config "$runner_dir/config.yaml" \
> "$runner_dir/runner.log" 2>&1 &
runner_pid=$!
sleep 3
if ps -p $runner_pid > /dev/null 2>&1; then
echo "✓ Host Runner 正在后台运行 (PID: $runner_pid)"
# Save PID
echo $runner_pid > "$runner_dir/pid"
echo "日志文件: $runner_dir/runner.log"
else
echo "❌ 启动失败,请检查日志"
return 1
fi
else
# Docker Mode Config
cat > "$runner_dir/config.yaml" << EOF
log:
level: info
runner:
file: /data/.runner
capacity: 2
timeout: 3h
shutdown_timeout: 30s
insecure: false
fetch_timeout: 5s
fetch_interval: 2s
labels: []
cache:
enabled: true
dir: "/data/cache"
host: "host.docker.internal"
port: 9040
container:
network: "host"
privileged: false
options:
workdir_parent: /data/workspace
valid_volumes: []
docker_host: ""
force_pull: false
host:
workdir_parent: /data/workspace
EOF
echo "启动 Docker 容器 (自动注册)..."
docker run -d \
--name "$name" \
--restart always \
--network host \
-v "$runner_dir/config.yaml:/config.yaml" \
-v "$runner_dir/data:/data" \
-v "/var/run/docker.sock:/var/run/docker.sock" \
-e GITEA_INSTANCE_URL="$GITEA_URL" \
-e GITEA_RUNNER_REGISTRATION_TOKEN="$GITEA_TOKEN" \
-e GITEA_RUNNER_NAME="$name" \
-e GITEA_RUNNER_LABELS="$labels" \
gitea/act_runner:latest daemon --config /config.yaml
if [ $? -eq 0 ]; then
echo "✓ Docker Runner 容器已启动: $name"
# 等待容器启动并查看日志
sleep 5
echo "容器日志:"
docker logs "$name" 2>&1 | tail -10
else
echo "❌ 容器启动失败"
return 1
fi
fi
# ==========================================
# 5.5 Display Runner Info
# ==========================================
echo ""
echo "$mode runner 创建成功: $name"
if [ "$mode" = "host" ]; then
echo " PID: $runner_pid"
echo " 停止: kill \$(cat $runner_dir/pid)"
echo " 日志: tail -f $runner_dir/runner.log"
else
echo " 容器: $name"
echo " 停止: docker stop $name"
echo " 日志: docker logs -f $name"
fi
echo " 目录: $runner_dir"
echo ""
return 0
}
# ==========================================
# 6. Main Execution Logic
# ==========================================
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Gitea Actions Runner 创建工具"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# 批量模式:创建 docker 和 host runner
if [ "$ALL_MODE" = true ]; then
hostname=$(hostname -s 2>/dev/null || echo "unknown")
if [ -n "$RUNNER_NAME" ]; then
# 如果指定了名称,使用该名称作为前缀
docker_name="${RUNNER_NAME}-docker"
host_name="${RUNNER_NAME}-host"
else
# 使用默认名称
docker_name="runner-${hostname}-docker"
host_name="runner-${hostname}-host"
fi
echo "批量创建模式:"
echo " Docker Runner: $docker_name"
echo " Host Runner: $host_name"
echo ""
# 创建 Docker runner
create_runner "docker" "$docker_name"
docker_result=$?
# 创建 Host runner
create_runner "host" "$host_name"
host_result=$?
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "批量创建完成"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if [ $docker_result -eq 0 ] && [ $host_result -eq 0 ]; then
echo "✅ 两个 runner 都创建成功!"
exit 0
elif [ $docker_result -eq 0 ]; then
echo "⚠️ Docker runner 创建成功,但 Host runner 失败"
exit 1
elif [ $host_result -eq 0 ]; then
echo "⚠️ Host runner 创建成功,但 Docker runner 失败"
exit 1
else
echo "❌ 两个 runner 都创建失败"
exit 1
fi
else
# 单个 runner 模式
create_runner "$RUNNER_MODE" "$RUNNER_NAME"
result=$?
if [ $result -eq 0 ]; then
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Runner 创建完成!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
else
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "❌ Runner 创建失败"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
exit 1
fi
fi
# ==========================================
# 5. Detect System Environment & Labels
# ==========================================
echo "生成 Labels..."
if [ "$RUNNER_MODE" = "host" ]; then
OS=$(uname -s)
case "$OS" in
Darwin) os_label="macOS" ;;
Linux) os_label="ubuntu" ;;
*) os_label="unknown" ;;
esac
ARCH=$(uname -m)
case "$ARCH" in
arm64|aarch64) arch_label="ARM64" ;;
x86_64) arch_label="x64" ;;
*) arch_label="unknown" ;;
esac
combined=$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]')
labels="self-hosted:host,${os_label}:host,${arch_label}:host,${combined}:host"
else
# Docker mode uses standard labels mapping to images
# Format: label:docker://image
labels="ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye,ubuntu-20.04:docker://node:16-buster,linux:docker://node:16-bullseye"
fi
echo "✓ Labels ($RUNNER_MODE):"
echo " $labels"
echo ""
# ==========================================
# 6. Create Runner Directory
# ==========================================
echo "创建 Runner 目录..."
mkdir -p "$runner_dir"/{cache,workspace}
if [ "$RUNNER_MODE" = "docker" ]; then
# Docker mode might strictly need data dir mapping
mkdir -p "$runner_dir/data"
fi
echo "✓ 目录: $runner_dir"
echo ""
# ==========================================
# 7. Get Registration Token
# ==========================================
echo "正在获取 Runner 注册 Token..."
# 默认尝试全局 Runner管理员权限
echo "尝试创建全局 Runner可用于所有组织和仓库..."
response=$(curl -s -w "\n%{http_code}" \
-H "Authorization: token $GITEA_TOKEN" \
"${GITEA_URL}/api/v1/admin/runners/registration-token")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
# 如果全局 Runner 失败(权限不足),降级到组织 Runner
if [ "$http_code" != "200" ]; then
echo "⚠️ 全局 Runner 权限不足 (HTTP $http_code)"
echo " 全局 Runner 需要管理员 Token"
echo ""
echo "降级到组织 Runner..."
runner_level="organization"
if [ -n "$GITEA_DEFAULT_ORG" ]; then
org_name="$GITEA_DEFAULT_ORG"
else
read -p "请输入组织名称: " org_input
if [ -z "$org_input" ]; then
echo "❌ 必须指定组织名称"
exit 1
fi
org_name="$org_input"
fi
echo "使用组织: $org_name"
response=$(curl -s -w "\n%{http_code}" -X POST \
-H "Authorization: token $GITEA_TOKEN" \
"${GITEA_URL}/api/v1/orgs/$org_name/actions/runners/registration-token")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
else
echo "✓ 使用全局 Runner"
runner_level="global"
fi
if [ "$http_code" != "200" ]; then
echo "❌ 获取注册 Token 失败 (HTTP $http_code)"
echo "$body"
exit 1
fi
# Need jq for parsing json
if ! command -v jq &> /dev/null; then
# Simple grep fallback if jq not available, but jq is better
registration_token=$(echo "$body" | grep -o '"token":"[^"]*"' | cut -d'"' -f4)
else
registration_token=$(echo "$body" | jq -r '.token')
fi
if [ -z "$registration_token" ]; then
echo "❌ 无法解析注册 Token"
exit 1
fi
echo "✓ 注册 Token 已获取"
echo ""
# ==========================================
# 8. Start Runner (Register & Run)
# ==========================================
echo "启动 Runner..."
if [ "$RUNNER_MODE" = "host" ]; then
# Host Mode Config
cat > "$runner_dir/config.yaml" << EOF
log:
level: info
runner:
file: .runner
capacity: 1
timeout: 3h
shutdown_timeout: 30s
insecure: false
fetch_timeout: 5s
fetch_interval: 2s
labels: []
cache:
enabled: true
dir: "$runner_dir/cache"
host: "127.0.0.1"
port: 0
host:
workdir_parent: "$runner_dir/workspace"
EOF
echo "注册 Host Runner..."
act_runner register \
--config "$runner_dir/config.yaml" \
--instance "$GITEA_URL" \
--token "$registration_token" \
--name "$runner_name" \
--labels "$labels" \
--no-interactive
if [ $? -ne 0 ]; then
echo "❌ 注册失败"
exit 1
fi
echo "启动后台进程..."
nohup act_runner daemon --config "$runner_dir/config.yaml" \
> "$runner_dir/runner.log" 2>&1 &
runner_pid=$!
sleep 3
if ps -p $runner_pid > /dev/null 2>&1; then
echo "✓ Host Runner 正在后台运行 (PID: $runner_pid)"
# Save PID
echo $runner_pid > "$runner_dir/pid"
else
echo "❌ 启动失败,请检查日志"
exit 1
fi
else
# Docker Mode
# Strategy: Use environment variables for auto-registration on startup
# This avoids the "Instance Address Empty" issue seen with 'act_runner register' inside containers
cat > "$runner_dir/config.yaml" << EOF
log:
level: info
runner:
file: /data/.runner
capacity: 2
timeout: 3h
shutdown_timeout: 30s
insecure: false
fetch_timeout: 5s
fetch_interval: 2s
labels: []
cache:
enabled: true
dir: "/data/cache"
host: "host.docker.internal"
port: 9040
container:
# 使用 host 网络模式,确保 runner 和 job 容器共处一个网络,以便 cache 能够正常访问
network: "host"
privileged: false
options:
workdir_parent: /data/workspace
valid_volumes: []
docker_host: ""
force_pull: false
host:
workdir_parent: /data/workspace
EOF
echo "启动 Docker 容器 (自动注册)..."
# 使用 host 网络模式,确保 runner 和 job 容器共处一个网络,以便 cache 能够正常访问
docker run -d \
--name "$runner_name" \
--restart always \
--network host \
-v "$runner_dir/config.yaml:/config.yaml" \
-v "$runner_dir/data:/data" \
-v "/var/run/docker.sock:/var/run/docker.sock" \
-e GITEA_INSTANCE_URL="$GITEA_URL" \
-e GITEA_RUNNER_REGISTRATION_TOKEN="$registration_token" \
-e GITEA_RUNNER_NAME="$runner_name" \
-e GITEA_RUNNER_LABELS="$labels" \
gitea/act_runner:latest daemon --config /config.yaml
if [ $? -eq 0 ]; then
echo "✓ Docker Runner 容器已启动: $runner_name"
# 等待容器启动并查看日志
sleep 5
echo "容器日志:"
docker logs "$runner_name" 2>&1 | tail -20
else
echo "❌ 容器启动失败"
exit 1
fi
fi
echo ""
```