--- description: Delete a Gitea runner configuration (interactive) --- # gitea-delete-runner 删除 Gitea Runner 配置(交互式选择)。 ## 命令说明 此命令用于从 Gitea 服务器删除 runner 配置。支持交互式选择单个或所有 runners。 **重要**:这是一个可执行命令,AI 应该按照以下步骤执行操作,并与用户进行交互。 ## Features - 显示 Gitea 服务器上的全局 runners - 交互式选择要删除的 runner - 支持删除单个或所有 runners - 删除 runner 配置目录(包括 cache 和 workspace) - 停止运行中的 runner 进程 - 自动从 Gitea 服务器注销 runner - 安全确认机制(需要用户输入 'yes') ## User Input Format 无需参数,运行后交互式选择。 ```bash /gitea-delete-runner ``` ## AI 执行指导 当用户调用此命令时,AI 应该: 1. **获取并展示 runner 列表**:调用 Gitea API 获取服务器上的全局 runners,并以清晰的格式展示给用户 2. **等待用户选择**:让用户选择要删除的 runner(序号、'all' 或 'q') 3. **确认操作**:在删除前要求用户输入 'yes' 确认 4. **执行删除**:按照三步流程删除选中的 runners 5. **显示结果**:展示删除操作的详细结果 **注意**:此命令需要与用户进行多次交互,不要一次性执行所有步骤。 ## Implementation Steps ### 1. Load Configuration ```bash config_file_main="$HOME/.config/gitea/config.env" if [ ! -f "$config_file_main" ]; then echo "❌ 未找到 Gitea 配置文件" echo " 路径: $config_file_main" exit 1 fi source "$config_file_main" if [ -z "$GITEA_URL" ] || [ -z "$GITEA_TOKEN" ]; then echo "❌ Gitea 配置不完整" echo " 需要 GITEA_URL 和 GITEA_TOKEN" exit 1 fi ``` ### 2. Fetch and Display Runners **AI 执行**:运行以下命令获取并显示 runners 列表,然后等待用户选择。 ```bash source "$HOME/.config/gitea/config.env" echo "正在从 Gitea 服务器获取全局 runners..." echo "" # Fetch global runners from Gitea API api_endpoint="${GITEA_URL}/api/v1/admin/actions/runners" response=$(curl -s -H "Authorization: token $GITEA_TOKEN" "$api_endpoint") if [ $? -ne 0 ] || [ -z "$response" ]; then echo "❌ 无法连接到 Gitea 服务器" exit 1 fi # Parse runners using jq if ! command -v jq &> /dev/null; then echo "❌ 需要安装 jq 工具" echo " 安装: brew install jq" exit 1 fi # Check if response is valid JSON if ! echo "$response" | jq empty 2>/dev/null; then echo "❌ API 返回数据格式错误" echo " 请检查 Token 权限(需要 admin 权限)" exit 1 fi # Extract runner information (注意:API 返回格式是 {"runners": [...], "total_count": N}) runner_count=$(echo "$response" | jq '.runners | length') if [ "$runner_count" -eq 0 ]; then echo "⚠️ 服务器上没有全局 runners" exit 0 fi echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Gitea 全局 Runners (共 $runner_count 个)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # Display runners with formatted output echo "$response" | jq -r '.runners[] | [.id, .name, .status] | @tsv' | \ awk 'BEGIN{i=1} { printf "%2d. %-30s [ID: %-5s] %s\n", i++, $2, $1, ($3=="online"?"🟢 在线":"🔴 离线") }' echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "选择要删除的 Runner" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo " 输入序号: 删除单个 runner" echo " 输入 'all': 删除所有 runners" echo " 输入 'q' 或 'quit': 取消" echo "" # Save runner data to temp file for later use echo "$response" | jq -r '.runners[] | "\(.id)|\(.name)"' > /tmp/gitea_runners.txt ``` **AI 指导**:执行完上述命令后,等待用户输入选择(序号、'all' 或 'q')。 ### 3. Process User Selection **AI 指导**:根据用户的输入(从步骤 2 获取),执行相应的处理逻辑。 - 如果用户输入 'q' 或 'quit':取消操作,清理临时文件 - 如果用户输入 'all':准备删除所有 runners,进入步骤 4 确认 - 如果用户输入序号:验证并准备删除对应的 runner,进入步骤 4 确认 **选择处理脚本**(AI 根据用户输入执行相应部分): ```bash # 假设用户选择存储在变量 $USER_SELECTION 中 if [ "$USER_SELECTION" = "q" ] || [ "$USER_SELECTION" = "quit" ]; then echo "已取消" rm -f /tmp/gitea_runners.txt exit 0 fi # Read runner data from temp file mapfile -t runner_data < /tmp/gitea_runners.txt runner_count=${#runner_data[@]} if [ "$USER_SELECTION" = "all" ]; then # Prepare to delete all runners echo "" echo "⚠️ 将删除所有 $runner_count 个 runners" echo "" # 继续到步骤 4 显示警告并确认 else # Validate selection is a number if ! [[ "$USER_SELECTION" =~ ^[0-9]+$ ]]; then echo "❌ 无效的选择" rm -f /tmp/gitea_runners.txt exit 1 fi # Validate selection is in range if [ "$USER_SELECTION" -lt 1 ] || [ "$USER_SELECTION" -gt "$runner_count" ]; then echo "❌ 选择超出范围 (1-$runner_count)" rm -f /tmp/gitea_runners.txt exit 1 fi # Get selected runner info selected_data="${runner_data[$((USER_SELECTION-1))]}" IFS='|' read -r id name <<< "$selected_data" echo "" echo "已选择: $name (ID: $id)" echo "" # 继续到步骤 4 显示警告并确认 fi ``` ### 4. Display Warning and Get Confirmation **AI 执行**:显示删除警告,列出将要删除的 runners,然后等待用户输入 'yes' 确认。 ```bash echo "⚠️ 警告: 此操作将执行以下操作:" echo " - 从 Gitea 服务器注销 runner" echo " - 停止本地运行的 runner 进程" echo " - 删除 runner 配置文件" echo " - 删除 cache 和 workspace 目录" echo " - 删除所有相关数据" echo "" # List runners to be deleted if [ "$USER_SELECTION" = "all" ]; then echo "将删除以下 runners:" cat /tmp/gitea_runners.txt | while IFS='|' read -r id name; do echo " - $name (ID: $id)" done else # Single runner already displayed in step 3 : fi echo "" ``` **AI 指导**:显示完警告后,等待用户输入确认('yes' 继续,其他取消)。 ### 5. Execute Deletion **AI 执行**:如果用户确认(输入 'yes'),执行删除操作;否则取消。 ```bash # 假设用户确认输入存储在 $USER_CONFIRM 中 if [ "$USER_CONFIRM" != "yes" ]; then echo "已取消" rm -f /tmp/gitea_runners.txt exit 0 fi source "$HOME/.config/gitea/config.env" echo "开始删除..." echo "" success_count=0 fail_count=0 runners_dir="$HOME/.config/gitea/runners" # Process all selected runners while IFS='|' read -r runner_id runner_name; do echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "删除: $runner_name (ID: $runner_id)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # Find local runner directory by ID (处理 runners/* 不存在的情况) runner_dir="" if [ -d "$runners_dir" ]; then shopt -s nullglob # 避免 * 无匹配时报错 for dir in "$runners_dir"/*; do if [ -f "$dir/.runner" ]; then local_id=$(jq -r '.id // ""' "$dir/.runner" 2>/dev/null) if [ "$local_id" = "$runner_id" ]; then runner_dir="$dir" break fi fi done shopt -u nullglob fi # Step 1: Unregister from Gitea server echo "[1/3] 从 Gitea 服务器注销..." api_endpoint="${GITEA_URL}/api/v1/admin/actions/runners/${runner_id}" http_code=$(curl -s -w "%{http_code}" -o /dev/null \ -X DELETE \ -H "Authorization: token $GITEA_TOKEN" \ "$api_endpoint") if [ "$http_code" = "204" ]; then echo " ✓ 已从服务器注销" else echo " ⚠️ 注销失败 (HTTP $http_code)" fi # Step 2: Stop local process if running echo "[2/3] 检查本地进程..." if [ -n "$runner_dir" ] && [ -f "$runner_dir/config.yaml" ]; then config_file="$runner_dir/config.yaml" if pgrep -f "act_runner daemon --config $config_file" > /dev/null 2>&1; then pid=$(pgrep -f "act_runner daemon --config $config_file") # Check if runner is busy (executing jobs) # 从服务器获取 runner 的 busy 状态 runner_info=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ "${GITEA_URL}/api/v1/admin/actions/runners/${runner_id}") is_busy=$(echo "$runner_info" | jq -r '.busy // false') if [ "$is_busy" = "true" ]; then echo " ⚠️ 警告: Runner 正在执行 job!" echo " 强制停止可能导致 job 中断和数据不一致" echo "" echo " 选项:" echo " 1. 等待 job 完成后再停止(推荐)" echo " 2. 强制立即停止" echo "" # AI 应在此处等待用户选择 # 假设用户选择存储在 $STOP_CHOICE 中 if [ "$STOP_CHOICE" = "1" ]; then echo " 等待 job 完成..." # 轮询检查 runner 是否仍然 busy max_wait=300 # 最多等待 5 分钟 waited=0 while [ $waited -lt $max_wait ]; do sleep 10 waited=$((waited + 10)) runner_info=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ "${GITEA_URL}/api/v1/admin/actions/runners/${runner_id}") is_busy=$(echo "$runner_info" | jq -r '.busy // false') if [ "$is_busy" = "false" ]; then echo " ✓ Job 已完成" break fi echo " 仍在执行... (已等待 ${waited}s)" done if [ $waited -ge $max_wait ]; then echo " ⚠️ 等待超时,将强制停止" fi else echo " ⚠️ 用户选择强制停止" fi fi # 优雅停止 runner echo " 正在停止进程 (PID: $pid)..." kill "$pid" 2>/dev/null sleep 2 # 如果进程还在运行,强制杀死 if pgrep -f "act_runner daemon --config $config_file" > /dev/null 2>&1; then echo " 进程未响应,强制终止..." kill -9 "$pid" 2>/dev/null sleep 1 fi # 验证进程已停止 if pgrep -f "act_runner daemon --config $config_file" > /dev/null 2>&1; then echo " ❌ 无法停止进程" else echo " ✓ 进程已停止" fi else echo " ✓ 未运行" fi else echo " ⓘ 未找到本地配置" fi # Step 3: Delete local directory echo "[3/3] 删除本地配置..." if [ -n "$runner_dir" ] && [ -d "$runner_dir" ]; then rm -rf "$runner_dir" if [ ! -d "$runner_dir" ]; then echo " ✓ 本地配置已删除" ((success_count++)) else echo " ❌ 删除失败" ((fail_count++)) fi else # Server runner deleted, but no local config ((success_count++)) echo " ⓘ 无本地配置需要删除" fi echo "" done < /tmp/gitea_runners.txt # Cleanup temp file rm -f /tmp/gitea_runners.txt ``` ### 6. Display Summary **AI 执行**:显示删除操作的最终结果。 ```bash echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "删除完成" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "成功: $success_count 个" if [ $fail_count -gt 0 ]; then echo "失败: $fail_count 个" fi echo "" echo "管理命令:" echo " 查看剩余 runners: /gitea-list-runners" echo " 创建新 runner: /gitea-create-runner" echo "" ``` **AI 指导**:删除完成后,可以选择性地验证服务器上的 runner 数量,确认删除成功。 ## Output Example ### Example 1: Delete Single Runner ``` 正在从 Gitea 服务器获取全局 runners... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Gitea 全局 Runners (共 3 个) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1. runner-mac-mini4 [ID: 42 ] 🟢 在线 系统: darwin 架构: arm64 最后在线: 2026-01-12 10:30 2. runner-macbook-pro [ID: 43 ] 🔴 离线 系统: darwin 架构: arm64 最后在线: 2026-01-10 18:45 3. runner-linux-server [ID: 44 ] 🟢 在线 系统: linux 架构: amd64 最后在线: 2026-01-12 10:25 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 选择要删除的 Runner ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 输入序号: 删除单个 runner 输入 'all': 删除所有 runners 输入 'q' 或 'quit': 取消 请选择: 2 已选择: runner-macbook-pro (ID: 43) ⚠️ 警告: 此操作将执行以下操作: - 从 Gitea 服务器注销 runner - 停止本地运行的 runner 进程 - 删除 runner 配置文件 - 删除 cache 和 workspace 目录 - 删除所有相关数据 确认删除? 输入 'yes' 继续: yes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 删除: runner-macbook-pro (ID: 43) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [1/3] 从 Gitea 服务器注销... ✓ 已从服务器注销 [2/3] 检查本地进程... ✓ 未运行 [3/3] 删除本地配置... ✓ 本地配置已删除 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 删除完成 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 成功: 1 个 管理命令: 查看剩余 runners: /gitea-list-runners 创建新 runner: /gitea-create-runner ``` ### Example 2: Delete All Runners ``` 正在从 Gitea 服务器获取全局 runners... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Gitea 全局 Runners (共 2 个) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1. runner-mac-mini4 [ID: 42 ] 🟢 在线 系统: darwin 架构: arm64 最后在线: 2026-01-12 10:30 2. runner-linux-server [ID: 44 ] 🟢 在线 系统: linux 架构: amd64 最后在线: 2026-01-12 10:25 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 选择要删除的 Runner ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 输入序号: 删除单个 runner 输入 'all': 删除所有 runners 输入 'q' 或 'quit': 取消 请选择: all ⚠️ 将删除所有 2 个 runners ⚠️ 警告: 此操作将执行以下操作: - 从 Gitea 服务器注销 runner - 停止本地运行的 runner 进程 - 删除 runner 配置文件 - 删除 cache 和 workspace 目录 - 删除所有相关数据 将删除 2 个 runners: - runner-mac-mini4 (ID: 42) - runner-linux-server (ID: 44) 确认删除? 输入 'yes' 继续: yes ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 删除: runner-mac-mini4 (ID: 42) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [1/3] 从 Gitea 服务器注销... ✓ 已从服务器注销 [2/3] 检查本地进程... 正在停止进程 (PID: 12345)... ✓ 进程已停止 [3/3] 删除本地配置... ✓ 本地配置已删除 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 删除: runner-linux-server (ID: 44) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ [1/3] 从 Gitea 服务器注销... ✓ 已从服务器注销 [2/3] 检查本地进程... ⓘ 未找到本地配置 [3/3] 删除本地配置... ⓘ 无本地配置需要删除 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 删除完成 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 成功: 2 个 管理命令: 查看剩余 runners: /gitea-list-runners 创建新 runner: /gitea-create-runner ``` ## Safety Features - **服务器数据优先**:直接从 Gitea 服务器获取 runner 列表,确保准确性 - **交互式选择**:支持选择单个或所有 runners,避免误删 - **双重确认**:需要输入 'yes' 进行最终确认(防止误删) - **Busy 状态检测**:检查 runner 是否正在执行 job - 如果 runner 正在执行 job,给用户选择: - 等待 job 完成后停止(推荐) - 强制立即停止(有风险) - **优雅停止**:先尝试 SIGTERM,2 秒后才使用 SIGKILL - **服务器注销**:自动从 Gitea 服务器注销 runner - **三步删除流程**: 1. 从服务器注销 2. 停止本地进程(检测 busy 状态) 3. 删除本地配置 - **清晰提示**:显示每个步骤的执行状态和警告信息 - **批量删除支持**:可一次性删除所有 runners - **错误处理**:各步骤独立执行,部分失败不影响其他操作 - **超时保护**:等待 job 完成最多 5 分钟,超时后强制停止 ## Technical Notes - **必需工具**:需要安装 `jq` 工具(`brew install jq`) - **权限要求**:需要 Gitea admin 权限(全局 runner 管理) - **配置文件**:读取 `~/.config/gitea/config.env` 获取 API 配置 - **本地匹配**:通过 runner ID 匹配本地配置目录 - **服务器同步**:从服务器注销后再删除本地配置 - **无本地配置**:如果只存在服务器记录,仅注销服务器端 - **取消操作**:输入 'q' 或 'quit' 可随时取消 - **临时文件**:使用 `/tmp/gitea_runners.txt` 存储临时数据,操作完成后自动清理 - **API 格式**:Gitea API 返回格式为 `{"runners": [...], "total_count": N}`,不是直接的数组 - **Busy 检测**:通过 API 的 `busy` 字段判断 runner 是否正在执行 job - **停止信号**: - `kill $pid` (SIGTERM): 优雅停止,给进程清理资源的机会 - `kill -9 $pid` (SIGKILL): 强制终止,无法被捕获或忽略 - **等待策略**:如果 runner busy,最多等待 5 分钟(300 秒),每 10 秒检查一次状态 ## AI 执行流程总结 当用户调用 `/gitea-delete-runner` 时,AI 应该按以下流程执行: 1. **步骤 1**:加载并验证 Gitea 配置 2. **步骤 2**:获取并展示 runners 列表 → **等待用户选择** 3. **步骤 3**:处理用户选择,验证输入 4. **步骤 4**:显示删除警告和列表 → **等待用户确认** 5. **步骤 5**:执行删除操作 - 5.1: 从服务器注销 - 5.2: 检查并停止本地进程 - **如果 runner 正在执行 job(busy)** → **等待用户选择**(等待完成 or 强制停止) - 5.3: 删除本地配置 6. **步骤 6**:显示操作结果摘要 **重要**:这是一个交互式命令,可能需要在以下位置等待用户输入: - 步骤 2: 选择要删除的 runner - 步骤 4: 确认删除操作 - 步骤 5.2: 如果 runner 正在执行 job,选择等待或强制停止 不要一次性执行所有步骤。