Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5ad180fae | ||
|
|
fa227d9689 | ||
|
|
6153911e46 | ||
|
|
7084e9be35 | ||
|
|
ef67dc7dd2 | ||
|
|
9a39fa4725 | ||
|
|
491a7897c4 | ||
|
|
87b09e0ffd | ||
|
|
26c333dc84 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -18,3 +18,9 @@ config.env
|
|||||||
|
|
||||||
# Gitea 配置(如果不小心复制到这里)
|
# Gitea 配置(如果不小心复制到这里)
|
||||||
gitea-config.env
|
gitea-config.env
|
||||||
|
|
||||||
|
# Superpowers 插件和外部依赖
|
||||||
|
superpowers/
|
||||||
|
plugin/
|
||||||
|
.runner
|
||||||
|
package-lock.json
|
||||||
|
|||||||
@@ -127,3 +127,7 @@
|
|||||||
## 6. skill 和 command
|
## 6. skill 和 command
|
||||||
|
|
||||||
- 默认在oc 的全局配置目录下创建 skill 和 command
|
- 默认在oc 的全局配置目录下创建 skill 和 command
|
||||||
|
|
||||||
|
## 7. 临时文件
|
||||||
|
|
||||||
|
- 临时创建的脚本或文件,使用结束后,主动删除和清理。
|
||||||
|
|||||||
64
README.md
64
README.md
@@ -1,64 +0,0 @@
|
|||||||
# OpenCode
|
|
||||||
|
|
||||||
## 目录
|
|
||||||
|
|
||||||
```
|
|
||||||
opencode/
|
|
||||||
├── command/ # CLI 命令定义
|
|
||||||
│ ├── git-commit.md # 自动生成提交信息并提交
|
|
||||||
│ ├── git-pull.md # 拉取远程最新变更
|
|
||||||
│ ├── git-push.md # 提交+创建标签+推送(一键完成)
|
|
||||||
│ ├── git-push-tags.md # 推送所有标签到远程
|
|
||||||
│ ├── gitea-config.md # 查看 Gitea 配置和 Runner 状态
|
|
||||||
│ ├── gitea-create-repo.md # 在 Gitea 创建新仓库
|
|
||||||
│ ├── gitea-create-runner.md # 创建并启动 Gitea Actions Runner
|
|
||||||
│ ├── gitea-delete-runner.md # 删除已配置的 Runner
|
|
||||||
│ ├── gitea-list-runners.md # 列出所有已配置的 Runners
|
|
||||||
│ ├── gitea-reset.md # 重置 Gitea 配置
|
|
||||||
│ ├── gitea-switch-org.md # 切换默认组织
|
|
||||||
│ └── review.md # 代码审查命令
|
|
||||||
│
|
|
||||||
├── skill/ # 可复用技能库和指南
|
|
||||||
│ ├── git/ # Git 工作流程和版本管理
|
|
||||||
│ │ ├── SKILL.md # 完整的 Git 工作流程指南
|
|
||||||
│ │ └── quick-reference.md # Git 快速参考
|
|
||||||
│ │
|
|
||||||
│ ├── gitea/ # Gitea 平台集成
|
|
||||||
│ │ ├── SKILL.md # Gitea 完整指南
|
|
||||||
│ │ ├── setup-guide.md # 初始化和配置指南
|
|
||||||
│ │ ├── repository-operations.md # 仓库操作指南
|
|
||||||
│ │ ├── runner-management.md # Runner 管理指南
|
|
||||||
│ │ ├── api-reference.md # Gitea API 参考
|
|
||||||
│ │ ├── troubleshooting.md # 常见问题和解决方案
|
|
||||||
│ │ ├── workflow-generator.md # Workflow 自动生成工具
|
|
||||||
│ │ └── workflow-templates/ # CI/CD Workflow 模板库
|
|
||||||
│ │ ├── android-app.md # Android App 构建 Workflow
|
|
||||||
│ │ ├── go-backend.md # Go 后端服务 Workflow
|
|
||||||
│ │ ├── nodejs-frontend.md # Node.js 前端 Workflow
|
|
||||||
│ │ └── wechat-miniprogram.md # 微信小程序 Workflow
|
|
||||||
│ │
|
|
||||||
│ ├── android-developer/ # Android 开发指南
|
|
||||||
│ │ └── SKILL.md # Android 项目开发规范
|
|
||||||
│ │
|
|
||||||
│ ├── ios-developer/ # iOS 开发指南
|
|
||||||
│ │ └── SKILL.md # iOS 项目开发规范
|
|
||||||
│ │
|
|
||||||
│ ├── go-developer/ # Go 后端开发指南
|
|
||||||
│ │ └── SKILL.md # Go 项目开发规范
|
|
||||||
│ │
|
|
||||||
│ ├── electron-developer/ # Electron 桌面应用指南
|
|
||||||
│ │ └── SKILL.md # Electron 项目开发规范
|
|
||||||
│ │
|
|
||||||
│ └── mqtts-developer/ # MQTT over TLS/SSL 开发指南
|
|
||||||
│ ├── SKILL.md # MQTT 完整指南
|
|
||||||
│ ├── README.md # MQTT 项目说明
|
|
||||||
│ ├── setup-mqtts-acme.md # ACME 证书配置
|
|
||||||
│ ├── mqtts-client-config.md # 客户端配置
|
|
||||||
│ ├── mqtts-quick-reference.md # 快速参考
|
|
||||||
│ └── USAGE_EXAMPLES.md # 使用示例
|
|
||||||
│
|
|
||||||
├── README.md # 项目说明文档(当前文件)
|
|
||||||
├── AGENTS.md # 全局开发规范和指南
|
|
||||||
├── opencode.json # 项目配置文件
|
|
||||||
└── .gitignore # Git 忽略文件配置
|
|
||||||
```
|
|
||||||
27
skill/apple/SKILL.md
Normal file
27
skill/apple/SKILL.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
name: apple
|
||||||
|
description: Apple 开发者账号管理、注册最佳实践与风控规避指南
|
||||||
|
---
|
||||||
|
|
||||||
|
# Apple Developer Account Management
|
||||||
|
|
||||||
|
本 Skill 提供了 Apple 开发者账号申请、管理及风控规避的最佳实践指南。
|
||||||
|
|
||||||
|
## 功能文档
|
||||||
|
|
||||||
|
| 文档 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| [注册最佳实践](./registration-guide.md) | Apple 开发者账号注册流程、风控规避与问题诊断指南 |
|
||||||
|
|
||||||
|
## 核心原则 (Core Principles)
|
||||||
|
|
||||||
|
1. **物理与数据隔离**:注册环境必须保持绝对干净(一机、一号、一网、一卡)。
|
||||||
|
2. **真实身份**:必须使用真实的个人身份信息,严禁使用虚假资料。
|
||||||
|
3. **设备纯净**:使用未被 Apple 标记过的设备进行操作。
|
||||||
|
4. **网络安全**:避免使用公共 WiFi 或 VPN,优先使用移动数据网络。
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
- **联系支持 (Contact Support)**:通常由风控系统触发,原因是设备关联、网络污染或账号行为异常。
|
||||||
|
- **支付失败**:通常由于信用卡被关联或账单地址不匹配。
|
||||||
|
- **身份验证失败**:证件照片不清晰或与填写的名字不匹配。
|
||||||
129
skill/apple/registration-guide.md
Normal file
129
skill/apple/registration-guide.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
# Apple 开发者账号注册最佳实践报告 (2025版)
|
||||||
|
|
||||||
|
本指南针对 Apple 开发者账户注册过程中常见的“联系 Apple 支持”风控拦截问题,提供详细的诊断与注册最佳实践。
|
||||||
|
|
||||||
|
## 1. 问题诊断:为什么会提示“联系支持”?
|
||||||
|
|
||||||
|
当 Apple 的风控算法判定注册行为存在“高风险”或“非真人特征”时,会阻断流程。常见触发原因如下:
|
||||||
|
|
||||||
|
* **设备污染(Device Tainting):最常见原因**
|
||||||
|
* 该 iPhone/iPad 曾登录过被封禁的 Apple ID。
|
||||||
|
* 该设备曾频繁切换不同的 Apple ID。
|
||||||
|
* **关键点**:Apple 记录的是设备的 **UDID/序列号**。简单的“退出登录”或“还原设置”通常不足以清洗被列入黑名单的设备指纹。
|
||||||
|
* **网络环境污染**
|
||||||
|
* 使用了公司的公共 Wi-Fi(如果公司内有其他同事的账号被封,该 IP 可能已被标记)。
|
||||||
|
* 使用了 VPN 或代理工具(IP 地址频繁跳变或属于数据中心号段)。
|
||||||
|
* **账号行为异常**
|
||||||
|
* 新注册的 Apple ID **没有任何使用记录**(如下载 App、充值、云同步),直接申请开发者,会被视为“养号/机器注册”。
|
||||||
|
* 填写的身份信息(拼音/英文名)与证件或信用卡持卡人姓名不完全一致。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 核心原则:彻底的物理与数据隔离
|
||||||
|
|
||||||
|
为了确保注册成功,必须严格执行**“独立环境”**原则。请放弃“仅仅退出旧账号”的想法,必须把注册环境视为一个全新的“无菌室”。
|
||||||
|
|
||||||
|
**黄金法则:**
|
||||||
|
> **1 台干净设备 + 1 个独立网络 + 1 套真实身份 + 1 张独立银行卡**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 最佳实践流程指南
|
||||||
|
|
||||||
|
请按照以下步骤重新规划注册流程(建议换人或换全套环境):
|
||||||
|
|
||||||
|
### 第一阶段:环境准备(硬件与网络)
|
||||||
|
|
||||||
|
1. **设备选择(非常重要)**
|
||||||
|
* **最佳**:使用从未申请过开发者账号、未登录过违规账号的 iPhone 或 iPad。
|
||||||
|
* **次选**:如果必须用旧设备,必须进行**DFU模式彻底刷机**(不只是“抹掉所有内容和设置”),但这仍有风险,因为硬件序列号无法改变。
|
||||||
|
2. **网络环境**
|
||||||
|
* **绝对禁止**:使用公司 Wi-Fi、公共场所 Wi-Fi、VPN。
|
||||||
|
* **必须使用**:手机运营商的 **4G/5G 移动数据网络**。这是最干净、最受信任的 IP 来源。
|
||||||
|
3. **手机号码**
|
||||||
|
* 使用该申请人实名认证的手机号。
|
||||||
|
* **严禁**:使用虚拟运营商号码(170/171段)、接码平台号码。
|
||||||
|
4. **邮箱选择**
|
||||||
|
* **个人注册**:
|
||||||
|
* **推荐**:Gmail、iCloud 邮箱、Outlook/Hotmail
|
||||||
|
* **不推荐**:QQ 邮箱(通过率较低,风控系统可能视为高风险)
|
||||||
|
* **关键**:确保邮箱有真实使用记录,非新注册账号
|
||||||
|
* **组织注册(公司账号)**:
|
||||||
|
* **必须**:公司域名邮箱(如 `@company.com`)
|
||||||
|
* **要求**:邮箱域名必须与公司官网域名一致
|
||||||
|
* **验证**:确保邮箱能正常收发邮件(Apple 会发送验证邮件)
|
||||||
|
|
||||||
|
### 第二阶段:账号“养护”(关键缓冲期)
|
||||||
|
|
||||||
|
不要注册完 Apple ID 马上申请开发者,这非常像机器行为。
|
||||||
|
|
||||||
|
1. **注册 Apple ID**:在设备上直接注册,开启双重认证(2FA)。
|
||||||
|
* **邮箱选择**:优先使用 Gmail 或 iCloud 邮箱,避免使用 QQ 邮箱
|
||||||
|
* **身份验证**:填写真实姓名(拼音/英文),与身份证一致
|
||||||
|
2. **模拟真实用户行为(建议 1-3 天)**:
|
||||||
|
* 登录 App Store 下载几个常用 App(微信、抖音等)。
|
||||||
|
* 开启 iCloud 同步(通讯录、照片)。
|
||||||
|
* 设置并使用 Apple Pay(绑定用于支付的信用卡)。
|
||||||
|
* 如果可能,充值少量金额购买一个 App。
|
||||||
|
* **目的**:向 Apple 证明这是一个“活人”在使用的“主力机”。
|
||||||
|
|
||||||
|
### 第三阶段:正式申请
|
||||||
|
|
||||||
|
1. **下载应用**:使用该 Apple ID 登录 App Store 下载 **Apple Developer App**。
|
||||||
|
2. **填写信息**:
|
||||||
|
* **姓名**:必须填写真实姓名(拼音/英文),必须与**身份证**和**信用卡**上的拼音完全一致。不要用英文昵称(如 Peter Zhang)。
|
||||||
|
* **身份证号**:输入无误。
|
||||||
|
3. **人脸识别**:
|
||||||
|
* 确保光线充足,背景简单。
|
||||||
|
* 必须由账号持有人本人进行扫脸。
|
||||||
|
4. **支付环节**:
|
||||||
|
* **信用卡**:必须使用**从未在其他开发者账号使用过**的 Visa/MasterCard 信用卡。
|
||||||
|
* **账单地址**:尽量填写真实账单地址。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 邮箱选择详细指南
|
||||||
|
|
||||||
|
### 官方要求
|
||||||
|
- **个人注册**:可以使用任何邮箱,但**公司域名邮箱**为最佳选择
|
||||||
|
- **组织注册**:**必须**使用公司域名邮箱(如 `@company.com`)
|
||||||
|
|
||||||
|
### 邮箱类型推荐(按优先级)
|
||||||
|
1. **公司域名邮箱**(最佳)
|
||||||
|
- 适用于组织注册,必须使用
|
||||||
|
- 邮箱域名必须与公司官网域名一致
|
||||||
|
2. **Gmail**(次佳)
|
||||||
|
- 国际通用,信誉度高
|
||||||
|
- 风控系统认可度高
|
||||||
|
3. **iCloud 邮箱**(Apple 自家服务)
|
||||||
|
- 最受 Apple 信任
|
||||||
|
- 与 Apple 生态系统无缝集成
|
||||||
|
4. **Outlook/Hotmail**(微软系)
|
||||||
|
- 信誉良好,通过率较高
|
||||||
|
5. **其他主流企业邮箱**
|
||||||
|
- 如企业微信邮箱、阿里云邮箱等
|
||||||
|
|
||||||
|
### 不推荐邮箱类型
|
||||||
|
- **QQ 邮箱**:通过率较低,风控系统可能视为高风险
|
||||||
|
- **163/126 邮箱**:有一定风险,不如 Gmail 可靠
|
||||||
|
- **虚拟邮箱/临时邮箱**:绝对禁止使用
|
||||||
|
- **接码平台邮箱**:会被直接拒绝
|
||||||
|
|
||||||
|
### 关键原则
|
||||||
|
- **邮箱历史**:使用有真实使用记录的邮箱,避免新注册账号
|
||||||
|
- **一致性**:邮箱信息与身份信息保持一致
|
||||||
|
- **可访问性**:确保邮箱能正常收发邮件,Apple 会发送验证邮件
|
||||||
|
|
||||||
|
## 5. 针对已触发风控情况的建议
|
||||||
|
|
||||||
|
如果您的注册尝试已经触发了“联系支持”提示:
|
||||||
|
|
||||||
|
1. **放弃当前环境**:不要继续在已报错的设备上重试,也不要用已报错的 Apple ID 重试。
|
||||||
|
2. **执行“新号新机”方案**:
|
||||||
|
* 找一位**还没有注册过**开发者账号的同事。
|
||||||
|
* 使用该同事**自己的 iPhone**(确保该手机没乱登过别人的 ID)。
|
||||||
|
* 关闭 Wi-Fi,使用 **4G/5G**。
|
||||||
|
* **更换邮箱**:使用 Gmail 或公司域名邮箱
|
||||||
|
* 按照上述“最佳实践”流程操作。
|
||||||
|
3. **关于申诉**:
|
||||||
|
* 除非您确信没有任何违规(设备绝对纯净、身份绝对真实),否则申诉解封的成功率极低,建议直接采用新方案。
|
||||||
@@ -3,9 +3,229 @@ description: 创建并启动 Gitea Actions Runner 的完整脚本
|
|||||||
agent: general
|
agent: general
|
||||||
---
|
---
|
||||||
|
|
||||||
# Create Runner Script
|
# Gitea Actions Runner 创建工具
|
||||||
|
|
||||||
本文档包含用于创建 Gitea Actions Runner 的完整 Bash 脚本。该脚本支持 Host 模式和 Docker 模式。
|
本文档提供了多种创建 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"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法三:完整脚本
|
||||||
|
如果你需要更多自定义选项,可以使用下面的完整脚本:
|
||||||
|
|
||||||
## 脚本文件
|
## 脚本文件
|
||||||
|
|
||||||
@@ -16,13 +236,82 @@ agent: general
|
|||||||
|
|
||||||
# Gitea Runner Creation Script
|
# Gitea Runner Creation Script
|
||||||
# Generated by OpenCode Skill
|
# Generated by OpenCode Skill
|
||||||
|
# 支持命令行参数,无需交互,支持批量创建
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 1. Choose Mode & Check Dependencies
|
# 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 "请选择 Runner 运行模式:"
|
||||||
echo " 1. Host Mode (直接在宿主机运行,支持 macOS/iOS 原生构建)"
|
echo " 1. Host Mode (直接在宿主机运行,支持 macOS/iOS 原生构建)"
|
||||||
echo " 2. Docker Mode (在容器中运行,环境隔离,适合标准 Linux 构建)"
|
echo " 2. Docker Mode (在容器中运行,环境隔离,适合标准 Linux 构建)"
|
||||||
@@ -37,6 +326,76 @@ else
|
|||||||
echo "✅ 已选择: Host Mode"
|
echo "✅ 已选择: Host Mode"
|
||||||
fi
|
fi
|
||||||
echo ""
|
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
|
# Check dependencies based on mode
|
||||||
if [ "$RUNNER_MODE" = "host" ]; then
|
if [ "$RUNNER_MODE" = "host" ]; then
|
||||||
@@ -81,68 +440,384 @@ fi
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 2. Load Gitea Configuration
|
# 3. Load Gitea Configuration
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
|
# 如果未通过命令行提供,尝试从配置文件加载
|
||||||
|
if [ -z "$GITEA_URL" ] || [ -z "$GITEA_TOKEN" ]; then
|
||||||
config_file="$HOME/.config/gitea/config.env"
|
config_file="$HOME/.config/gitea/config.env"
|
||||||
|
|
||||||
if [ ! -f "$config_file" ]; then
|
if [ -f "$config_file" ]; then
|
||||||
echo "❌ Gitea 配置不存在,请先运行 /gitea-reset"
|
echo "从配置文件加载 Gitea 配置..."
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
source "$config_file"
|
source "$config_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$GITEA_URL" ] || [ -z "$GITEA_TOKEN" ]; then
|
# 如果仍然缺少 URL,使用默认值
|
||||||
echo "❌ Gitea 配置不完整 (缺少 URL 或 TOKEN)"
|
if [ -z "$GITEA_URL" ]; then
|
||||||
echo " 请运行 /gitea-reset 重新配置"
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✓ 已加载 Gitea 配置: $GITEA_URL"
|
echo "✓ Gitea 配置:"
|
||||||
|
echo " URL: $GITEA_URL"
|
||||||
|
echo " Token: ${GITEA_TOKEN:0:8}..."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 3. Generate Runner Name
|
# 4. Generate Runner Name
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
if [ -n "$1" ]; then
|
# 如果未指定名称,生成默认名称
|
||||||
runner_name="$1"
|
if [ -z "$RUNNER_NAME" ]; then
|
||||||
echo "使用指定的 Runner 名称: $runner_name"
|
|
||||||
else
|
|
||||||
hostname=$(hostname -s 2>/dev/null || echo "unknown")
|
hostname=$(hostname -s 2>/dev/null || echo "unknown")
|
||||||
runner_name="runner-$hostname-$RUNNER_MODE"
|
if [ "$ALL_MODE" = true ]; then
|
||||||
echo "生成 Runner 名称: $runner_name"
|
# 批量模式会创建两个runner
|
||||||
|
echo "批量模式:将创建 docker 和 host runner"
|
||||||
|
else
|
||||||
|
RUNNER_NAME="runner-$hostname-$RUNNER_MODE"
|
||||||
|
echo "生成 Runner 名称: $RUNNER_NAME"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "使用指定的 Runner 名称: $RUNNER_NAME"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ! "$runner_name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
# 检查名称有效性(如果不是批量模式)
|
||||||
|
if [ "$ALL_MODE" = false ] && [[ ! "$RUNNER_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
||||||
echo "❌ Runner 名称无效 (仅限字母、数字、下划线、连字符)"
|
echo "❌ Runner 名称无效 (仅限字母、数字、下划线、连字符)"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 4. Check Runner Existence
|
# 5. Main Creation Function
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
runners_dir="$HOME/.config/gitea/runners"
|
create_runner() {
|
||||||
runner_dir="$runners_dir/$runner_name"
|
local mode="$1"
|
||||||
|
local name="$2"
|
||||||
|
|
||||||
if [ "$RUNNER_MODE" = "host" ]; then
|
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
|
if [ -d "$runner_dir" ]; then
|
||||||
echo "❌ Runner 目录已存在: $runner_dir"
|
echo "❌ Runner 目录已存在: $runner_dir"
|
||||||
echo " 请使用 /gitea-delete-runner 删除旧 Runner 或指定新名称"
|
return 1
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Docker mode: check directory AND container
|
# Docker mode: check directory AND container
|
||||||
if [ -d "$runner_dir" ]; then
|
if [ -d "$runner_dir" ]; then
|
||||||
echo "❌ Runner 配置目录已存在: $runner_dir"
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
if docker ps -a --format '{{.Names}}' | grep -q "^${runner_name}$"; then
|
|
||||||
echo "❌ Docker 容器已存在: $runner_name"
|
else
|
||||||
echo " 请先删除旧容器: docker rm -f $runner_name"
|
# 单个 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
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -387,30 +1062,4 @@ EOF
|
|||||||
fi
|
fi
|
||||||
echo ""
|
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 ""
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: opencode
|
name: opencode
|
||||||
description: Create and manage OpenCode commands and skills with templates and best practices. Use this skill when users ask to create commands or skills.
|
description: Create and manage OpenCode commands and skills with templates and best practices. Includes skill synchronization to Cursor IDE. Use this skill when users ask to create commands, skills, or sync to Cursor.
|
||||||
---
|
---
|
||||||
|
|
||||||
# OpenCode Command and Skill Management
|
# OpenCode Command and Skill Management
|
||||||
@@ -13,6 +13,8 @@ You are an expert in OpenCode configuration and extension development. This skil
|
|||||||
- "添加command", "添加命令", "add command"
|
- "添加command", "添加命令", "add command"
|
||||||
- "添加skill", "添加技能", "add skill"
|
- "添加skill", "添加技能", "add skill"
|
||||||
- "opcode command", "opencode skill"
|
- "opcode command", "opencode skill"
|
||||||
|
- "同步技能", "sync skills", "sync to cursor", "同步到cursor"
|
||||||
|
- "技能同步", "skill sync", "同步opencode技能"
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -34,6 +36,11 @@ For detailed skill creation guidelines, refer to:
|
|||||||
- `skill/opencode/skill-creation.md` - Complete guide to creating OpenCode skills
|
- `skill/opencode/skill-creation.md` - Complete guide to creating OpenCode skills
|
||||||
- Includes: directory structure, naming rules, templates, testing
|
- Includes: directory structure, naming rules, templates, testing
|
||||||
|
|
||||||
|
### Skill Synchronization (OpenCode → Cursor)
|
||||||
|
For syncing OpenCode skills to Cursor IDE, refer to:
|
||||||
|
- `skill/opencode/sync-to-cursor.md` - Guide to synchronize OpenCode skills to Cursor IDE
|
||||||
|
- Includes: sync strategies, operation steps, error handling, best practices
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### To Create a Command
|
### To Create a Command
|
||||||
|
|||||||
242
skill/opencode/sync-to-cursor.md
Normal file
242
skill/opencode/sync-to-cursor.md
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
# OpenCode 技能同步到 Cursor IDE 指南
|
||||||
|
|
||||||
|
将 OpenCode 技能同步到 Cursor IDE 的技能目录,实现配置一致和统一管理。
|
||||||
|
|
||||||
|
## 目的
|
||||||
|
|
||||||
|
- 保持 OpenCode 和 Cursor 的技能配置一致
|
||||||
|
- 简化技能维护工作流
|
||||||
|
- 支持一键同步操作
|
||||||
|
- 提供错误处理和回滚机制
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
### 源目录(OpenCode 技能)
|
||||||
|
```
|
||||||
|
~/.config/opencode/skill/
|
||||||
|
├── git/
|
||||||
|
├── android/
|
||||||
|
├── electron/
|
||||||
|
└── ...其他技能
|
||||||
|
```
|
||||||
|
|
||||||
|
### 目标目录(Cursor 技能)
|
||||||
|
```
|
||||||
|
~/.cursor/skills/
|
||||||
|
├── git/
|
||||||
|
├── android/
|
||||||
|
├── electron/
|
||||||
|
└── ...其他技能
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**:Cursor 目录中可能包含额外的技能(如 `opencode/`),同步时应保留这些额外技能不被删除。
|
||||||
|
|
||||||
|
## 同步策略
|
||||||
|
|
||||||
|
### 默认同步操作
|
||||||
|
1. **覆盖更新**:用 OpenCode 版本覆盖 Cursor 中的同名技能
|
||||||
|
2. **新增技能**:复制 OpenCode 中有但 Cursor 中没有的技能
|
||||||
|
3. **保留额外**:保留 Cursor 中独有的技能(如 `opencode/`)
|
||||||
|
4. **不删除**:不同步删除操作,仅添加和更新
|
||||||
|
|
||||||
|
### 文件级同步
|
||||||
|
- 递归复制整个技能目录
|
||||||
|
- 保留文件权限和时间戳
|
||||||
|
- 使用 `cp -Rf` 强制覆盖已存在的文件
|
||||||
|
|
||||||
|
## 操作步骤
|
||||||
|
|
||||||
|
### 1. 检查目录结构
|
||||||
|
```bash
|
||||||
|
# 检查源目录
|
||||||
|
ls -la ~/.config/opencode/skill/
|
||||||
|
|
||||||
|
# 检查目标目录
|
||||||
|
ls -la ~/.cursor/skills/ 2>/dev/null || echo "目标目录不存在"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 同步技能
|
||||||
|
```bash
|
||||||
|
# 确保目标目录存在
|
||||||
|
mkdir -p ~/.cursor/skills/
|
||||||
|
|
||||||
|
# 同步所有技能(覆盖更新)
|
||||||
|
cp -Rf ~/.config/opencode/skill/* ~/.cursor/skills/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 验证同步结果
|
||||||
|
```bash
|
||||||
|
# 比较目录结构
|
||||||
|
echo "源目录:"
|
||||||
|
ls ~/.config/opencode/skill/
|
||||||
|
echo "目标目录:"
|
||||||
|
ls ~/.cursor/skills/
|
||||||
|
|
||||||
|
# 检查特定技能文件
|
||||||
|
diff ~/.config/opencode/skill/git/SKILL.md ~/.cursor/skills/git/SKILL.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 完整同步脚本
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 配置路径
|
||||||
|
SOURCE_DIR="$HOME/.config/opencode/skill"
|
||||||
|
TARGET_DIR="$HOME/.cursor/skills"
|
||||||
|
|
||||||
|
echo "开始同步 OpenCode 技能到 Cursor..."
|
||||||
|
echo "源目录: $SOURCE_DIR"
|
||||||
|
echo "目标目录: $TARGET_DIR"
|
||||||
|
|
||||||
|
# 检查源目录
|
||||||
|
if [ ! -d "$SOURCE_DIR" ]; then
|
||||||
|
echo "错误: 源目录不存在 $SOURCE_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建目标目录(如不存在)
|
||||||
|
mkdir -p "$TARGET_DIR"
|
||||||
|
|
||||||
|
# 同步技能
|
||||||
|
echo "正在同步技能..."
|
||||||
|
cp -Rf "$SOURCE_DIR"/* "$TARGET_DIR"/
|
||||||
|
|
||||||
|
# 验证结果
|
||||||
|
echo "同步完成。验证结果:"
|
||||||
|
echo "已同步技能:"
|
||||||
|
ls "$SOURCE_DIR"
|
||||||
|
echo ""
|
||||||
|
echo "目标目录内容:"
|
||||||
|
ls "$TARGET_DIR"
|
||||||
|
|
||||||
|
# 检查文件差异
|
||||||
|
echo ""
|
||||||
|
echo "检查关键文件差异:"
|
||||||
|
for skill in $(ls "$SOURCE_DIR"); do
|
||||||
|
if [ -f "$SOURCE_DIR/$skill/SKILL.md" ] && [ -f "$TARGET_DIR/$skill/SKILL.md" ]; then
|
||||||
|
if ! diff -q "$SOURCE_DIR/$skill/SKILL.md" "$TARGET_DIR/$skill/SKILL.md" >/dev/null; then
|
||||||
|
echo " ⚠️ $skill/SKILL.md 存在差异"
|
||||||
|
else
|
||||||
|
echo " ✓ $skill/SKILL.md 一致"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "同步完成!"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
### 常见问题
|
||||||
|
|
||||||
|
#### 1. 目标目录不存在
|
||||||
|
```bash
|
||||||
|
# 检查并创建目录
|
||||||
|
if [ ! -d ~/.cursor/skills ]; then
|
||||||
|
mkdir -p ~/.cursor/skills
|
||||||
|
echo "已创建目录: ~/.cursor/skills"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 权限不足
|
||||||
|
```bash
|
||||||
|
# 检查权限
|
||||||
|
ls -ld ~/.cursor
|
||||||
|
# 如果需要,调整权限(谨慎操作)
|
||||||
|
# chmod 755 ~/.cursor
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 磁盘空间不足
|
||||||
|
```bash
|
||||||
|
# 检查可用空间
|
||||||
|
df -h ~/.cursor
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. 文件冲突
|
||||||
|
```bash
|
||||||
|
# 备份现有文件后再同步
|
||||||
|
BACKUP_DIR=~/.cursor/skills-backup-$(date +%Y%m%d)
|
||||||
|
cp -R ~/.cursor/skills "$BACKUP_DIR"
|
||||||
|
echo "已备份到: $BACKUP_DIR"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 回滚操作
|
||||||
|
```bash
|
||||||
|
# 从备份恢复
|
||||||
|
BACKUP_DIR=~/.cursor/skills-backup-20250123
|
||||||
|
if [ -d "$BACKUP_DIR" ]; then
|
||||||
|
rm -rf ~/.cursor/skills
|
||||||
|
cp -R "$BACKUP_DIR" ~/.cursor/skills
|
||||||
|
echo "已从备份恢复: $BACKUP_DIR"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
### 定期同步
|
||||||
|
建议在以下时机同步技能:
|
||||||
|
- 添加新技能后
|
||||||
|
- 更新现有技能后
|
||||||
|
- 定期维护时(如每周一次)
|
||||||
|
|
||||||
|
### 版本控制
|
||||||
|
```bash
|
||||||
|
# 将 OpenCode 配置目录加入版本控制
|
||||||
|
cd ~/.config/opencode
|
||||||
|
git status
|
||||||
|
git add skill/
|
||||||
|
git commit -m "feat: 更新技能配置"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自动化脚本
|
||||||
|
将同步脚本保存为 `~/.config/opencode/command/sync-skills.md` 作为 OpenCode 命令:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
description: 同步 OpenCode 技能到 Cursor IDE
|
||||||
|
---
|
||||||
|
#!/bin/bash
|
||||||
|
# 同步脚本内容...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 集成到 OpenCode Workflow
|
||||||
|
|
||||||
|
### 作为技能使用
|
||||||
|
在 OpenCode 技能中引用此文档:
|
||||||
|
```markdown
|
||||||
|
## 相关文档
|
||||||
|
- [技能同步指南](./skill-sync.md) - 同步 OpenCode 技能到 Cursor
|
||||||
|
```
|
||||||
|
|
||||||
|
### 作为命令调用
|
||||||
|
用户可直接请求:"同步技能到 Cursor" 或 "sync skills to cursor"
|
||||||
|
|
||||||
|
### 触发时机
|
||||||
|
- 用户修改技能配置后
|
||||||
|
- 安装新技能后
|
||||||
|
- 系统配置变更时
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **备份重要数据**:同步前建议备份 Cursor 技能目录
|
||||||
|
2. **网络环境**:如果使用网络同步,确保连接稳定
|
||||||
|
3. **兼容性**:确保技能格式与 Cursor 兼容
|
||||||
|
4. **测试验证**:同步后测试关键技能是否正常工作
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
|
||||||
|
| 问题 | 可能原因 | 解决方案 |
|
||||||
|
|------|----------|----------|
|
||||||
|
| 同步后技能不生效 | Cursor 缓存 | 重启 Cursor IDE |
|
||||||
|
| 文件权限错误 | 权限不足 | 检查目录权限 |
|
||||||
|
| 目标目录只读 | 系统限制 | 以管理员权限运行 |
|
||||||
|
| 部分技能缺失 | 同步中断 | 重新执行同步 |
|
||||||
|
|
||||||
|
## 更新记录
|
||||||
|
|
||||||
|
- **2026-01-23**:创建初始版本
|
||||||
|
- **功能**:基础同步、错误处理、最佳实践
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*该文档是 OpenCode 技能管理的一部分,用于维护技能配置的一致性。*
|
||||||
Reference in New Issue
Block a user