chore: 重构 OpenCode 命令和技能文档体系

- 新增:统一的 git 命令文档(add/commit/push/pull 等)
- 新增:整合的 Gitea 技能文档(API、运行器、工作流等)
- 新增:工作流模板(Android、Go、Node.js 等)
- 移除:已弃用的旧命令脚本和发布脚本
- 改进:.gitignore 添加敏感文件保护规则
- 改进:AGENTS.md 完善了开发规范和示例

此次重组统一了命令和技能的文档结构,便于后续维护和扩展。
This commit is contained in:
Voson
2026-01-13 00:27:14 +08:00
parent 84a3b48d43
commit 5a05d5ab53
35 changed files with 9658 additions and 1609 deletions

View File

@@ -0,0 +1,595 @@
---
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 完成后停止(推荐)
- 强制立即停止(有风险)
- **优雅停止**:先尝试 SIGTERM2 秒后才使用 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 正在执行 jobbusy** → **等待用户选择**(等待完成 or 强制停止)
- 5.3: 删除本地配置
6. **步骤 6**:显示操作结果摘要
**重要**:这是一个交互式命令,可能需要在以下位置等待用户输入:
- 步骤 2: 选择要删除的 runner
- 步骤 4: 确认删除操作
- 步骤 5.2: 如果 runner 正在执行 job选择等待或强制停止
不要一次性执行所有步骤。