11 KiB
11 KiB
description, agent
| description | agent |
|---|---|
| 创建并启动 Gitea Actions Runner 的完整脚本 | general |
Create Runner Script
本文档包含用于创建 Gitea Actions Runner 的完整 Bash 脚本。该脚本支持 Host 模式和 Docker 模式。
脚本文件
你可以将以下内容保存为 create_runner.sh 并赋予执行权限 (chmod +x create_runner.sh)。
#!/bin/bash
# Gitea Runner Creation Script
# Generated by OpenCode Skill
set -e
# ==========================================
# 1. Choose Mode & Check Dependencies
# ==========================================
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 ""
# 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 ""
# ==========================================
# 2. Load Gitea Configuration
# ==========================================
config_file="$HOME/.config/gitea/config.env"
if [ ! -f "$config_file" ]; then
echo "❌ Gitea 配置不存在,请先运行 /gitea-reset"
exit 1
fi
source "$config_file"
if [ -z "$GITEA_URL" ] || [ -z "$GITEA_TOKEN" ]; then
echo "❌ Gitea 配置不完整 (缺少 URL 或 TOKEN)"
echo " 请运行 /gitea-reset 重新配置"
exit 1
fi
echo "✓ 已加载 Gitea 配置: $GITEA_URL"
echo ""
# ==========================================
# 3. Generate Runner Name
# ==========================================
if [ -n "$1" ]; then
runner_name="$1"
echo "使用指定的 Runner 名称: $runner_name"
else
hostname=$(hostname -s 2>/dev/null || echo "unknown")
runner_name="runner-$hostname-$RUNNER_MODE"
echo "生成 Runner 名称: $runner_name"
fi
if [[ ! "$runner_name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo "❌ Runner 名称无效 (仅限字母、数字、下划线、连字符)"
exit 1
fi
echo ""
# ==========================================
# 4. Check Runner Existence
# ==========================================
runners_dir="$HOME/.config/gitea/runners"
runner_dir="$runners_dir/$runner_name"
if [ "$RUNNER_MODE" = "host" ]; then
if [ -d "$runner_dir" ]; then
echo "❌ Runner 目录已存在: $runner_dir"
echo " 请使用 /gitea-delete-runner 删除旧 Runner 或指定新名称"
exit 1
fi
else
# Docker mode: check directory AND container
if [ -d "$runner_dir" ]; then
echo "❌ Runner 配置目录已存在: $runner_dir"
exit 1
fi
if docker ps -a --format '{{.Names}}' | grep -q "^${runner_name}$"; then
echo "❌ Docker 容器已存在: $runner_name"
echo " 请先删除旧容器: docker rm -f $runner_name"
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 ""
# ==========================================
# 9. Display Summary
# ==========================================
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Runner 创建成功!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "基本信息:"
echo " 名称: $runner_name"
echo " 模式: $RUNNER_MODE"
echo " 状态: 🟢 运行中"
if [ "$RUNNER_MODE" = "host" ]; then
echo " PID: $runner_pid"
echo " 日志: $runner_dir/runner.log"
echo " 停止: kill \$(cat $runner_dir/pid)"
else
echo " 容器: $runner_name"
echo " 命令: docker logs -f $runner_name"
echo " docker stop $runner_name"
fi
echo ""
echo "配置文件: $runner_dir/config.yaml"
echo "Labels: $labels"
echo ""