Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dffa3fae12 | |||
| 43e138b19e | |||
| 75462f3412 | |||
| 5d9efea7ab |
21
README.md
21
README.md
@@ -1,30 +1,14 @@
|
||||
# OpenCode
|
||||
|
||||
|
||||
## 📊 项目统计
|
||||
|
||||
- **15 个 CLI 命令** - 涵盖 Git 和 Gitea 操作
|
||||
- **7 个开发领域技能库** - Android、iOS、Go、Electron、MQTT 等
|
||||
- **4 套 CI/CD 工作流模板** - 适配多种项目类型
|
||||
|
||||
## 🌟 核心特性
|
||||
|
||||
- **智能命令系统** - 自动生成提交信息、管理版本标签、安全地暂存文件
|
||||
- **可复用技能** - 预定义的开发工作流,如 Git 工作流、Android 开发等
|
||||
- **安全优先** - 自动过滤敏感文件,防止意外泄露凭证
|
||||
- **中文友好** - 完整的中文支持和交互提示
|
||||
|
||||
## 目录
|
||||
|
||||
```
|
||||
opencode/
|
||||
├── command/ # CLI 命令定义
|
||||
│ ├── git-add.md # 智能暂存文件(自动过滤敏感文件)
|
||||
│ ├── git-commit.md # 自动生成提交信息并提交
|
||||
│ ├── git-pull.md # 拉取远程最新变更
|
||||
│ ├── git-push.md # 提交+创建标签+推送(一键完成)
|
||||
│ ├── git-push-tags.md # 推送所有标签到远程
|
||||
│ ├── git-status.md # 查看仓库状态
|
||||
│ ├── gitea-config.md # 查看 Gitea 配置和 Runner 状态
|
||||
│ ├── gitea-create-repo.md # 在 Gitea 创建新仓库
|
||||
│ ├── gitea-create-runner.md # 创建并启动 Gitea Actions Runner
|
||||
@@ -73,13 +57,8 @@ opencode/
|
||||
│ ├── mqtts-quick-reference.md # 快速参考
|
||||
│ └── USAGE_EXAMPLES.md # 使用示例
|
||||
│
|
||||
├── plugin/ # 插件扩展系统
|
||||
│ └── notification.ts # 通知插件(邮件、Slack、钉钉等)
|
||||
│
|
||||
├── README.md # 项目说明文档(当前文件)
|
||||
├── AGENTS.md # 全局开发规范和指南
|
||||
├── opencode.json # 项目配置文件
|
||||
├── package.json # Node.js 依赖配置
|
||||
└── .gitignore # Git 忽略文件配置
|
||||
```
|
||||
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
---
|
||||
description: Stage changes with automatic filtering of sensitive files
|
||||
---
|
||||
|
||||
Intelligently stage changes while automatically filtering security-sensitive files.
|
||||
|
||||
Please perform the following:
|
||||
|
||||
1. **Detect all unstaged changes:**
|
||||
- Modified files (M)
|
||||
- Deleted files (D)
|
||||
- Untracked files (??) - **重要:第一次就显示并提供选项**
|
||||
|
||||
2. **Filter sensitive files** - Automatically exclude:
|
||||
- `.env*` - Environment files
|
||||
- `*.key`, `*.pem`, `*.p8` - Private keys
|
||||
- `.aws/*`, `.gcloud/*`, `.ssh/*` - Cloud/SSH credentials
|
||||
- `credentials.json`, `secrets.json` - Credential files
|
||||
- `node_modules/`, `vendor/`, `.venv/` - Dependencies
|
||||
- `dist/`, `build/`, `.next/` - Build artifacts
|
||||
- `.DS_Store`, `Thumbs.db` - System files
|
||||
|
||||
3. **Categorize files:**
|
||||
- **已跟踪的修改/删除** (tracked modified/deleted)
|
||||
- **未跟踪的安全文件** (untracked safe files)
|
||||
- **敏感文件** (sensitive files - filtered)
|
||||
|
||||
4. **Display preview in Chinese:**
|
||||
```
|
||||
=== 将要暂存的文件 (N) ===
|
||||
M [tracked modified files]
|
||||
D [tracked deleted files]
|
||||
|
||||
=== 未跟踪的文件 (N) ===
|
||||
?? [untracked safe files]
|
||||
|
||||
=== 敏感文件已过滤 (N) ===
|
||||
⚠ [sensitive files with reasons]
|
||||
|
||||
=== 操作选项 ===
|
||||
- 输入 "y" 或 "yes" 暂存已修改/已删除的文件(不包括未跟踪文件)
|
||||
- 输入 "all" 或 "u" 暂存所有安全文件(包括未跟踪文件)✓ 推荐
|
||||
- 输入 "force" 强制暂存所有文件(包括敏感文件)⚠️ 谨慎使用
|
||||
- 输入 "no" 或 "cancel" 取消操作
|
||||
- 输入文件路径 暂存特定文件
|
||||
```
|
||||
|
||||
5. **Wait for user confirmation**
|
||||
|
||||
6. **Execute staging based on user choice:**
|
||||
- `y/yes`: Stage tracked modified/deleted only
|
||||
- `all/u`: Stage all safe files (including untracked)
|
||||
- `force`: Show warning, require "confirm" input, then stage everything
|
||||
- `no/cancel`: Abort
|
||||
- File path: Stage specific file(s)
|
||||
|
||||
7. **Display result in Chinese:**
|
||||
```
|
||||
✓ 暂存成功
|
||||
|
||||
已暂存 N 个文件:
|
||||
[列出已暂存的文件]
|
||||
|
||||
敏感文件已过滤并排除,保护了你的凭证信息。
|
||||
|
||||
下一步:
|
||||
- 运行 /git-commit 生成提交信息并提交
|
||||
- 运行 /git-status 查看暂存区状态
|
||||
```
|
||||
|
||||
**重要:第一次运行就显示所有选项,包括未跟踪文件的处理方式,不需要用户多次运行命令。**
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
description: Commit staged files with auto-generated message and create version tag
|
||||
---
|
||||
|
||||
Auto-generate a commit message for staged files, commit to the local repository, and create a version tag following semantic versioning.
|
||||
|
||||
Please follow the git workflow defined in `@skill/git/SKILL.md`:
|
||||
|
||||
1. **Check staging area** - Verify files are staged with `git diff --cached --name-only`
|
||||
- If empty, inform the user and stop
|
||||
2. **Collect information** - Run these commands in parallel:
|
||||
- `git status`
|
||||
- `git diff --cached`
|
||||
- `git log --oneline -10`
|
||||
- `git tag --list | sort -V | tail -5`
|
||||
- Read `@AGENTS.md` if it exists (repository type, version rules, project structure)
|
||||
3. **Detect repository type** - Polyrepo (tag: `1.2.0`) or Monorepo (tag: `subproject-1.2.0`)
|
||||
4. **Detect project type and version** - Check for version files:
|
||||
- iOS: `*.xcodeproj/project.pbxproj` → `MARKETING_VERSION`
|
||||
- Node.js: `package.json` → `version`
|
||||
- Android: `build.gradle(.kts)` → `versionName`
|
||||
- Go: Git tag only
|
||||
5. **Generate commit message** following Conventional Commits:
|
||||
- Format: `<type>(<scope>): <subject>`
|
||||
- Use Chinese for commit messages (macOS/Linux)
|
||||
- Types: feat, fix, docs, style, refactor, perf, test, chore, ci, build
|
||||
- For monorepo, use subproject as scope if changes affect single subproject
|
||||
6. **Update version number** if needed:
|
||||
- feat: minor +1 (1.2.0 → 1.3.0)
|
||||
- fix/perf: patch +1 (1.2.3 → 1.2.4)
|
||||
- Breaking change: major +1 (1.2.3 → 2.0.0)
|
||||
- Only for user-perceivable changes (feat, fix, perf, breaking)
|
||||
- Add updated version file to staging
|
||||
7. **Commit changes** with generated message
|
||||
8. **Create version tag** if version was updated (unless user specified "skip tag"):
|
||||
- Polyrepo: `git tag -a "1.2.0" -m "commit message"`
|
||||
- Monorepo: `git tag -a "subproject-1.2.0" -m "commit message"`
|
||||
|
||||
**Options:**
|
||||
- User can input "skip tag" or "skip" to skip tag creation
|
||||
|
||||
**Display result in Chinese:**
|
||||
```
|
||||
✓ 提交成功
|
||||
|
||||
提交信息:[commit message]
|
||||
版本标签:[tag] (如果创建了)
|
||||
|
||||
要推送到远程仓库,请运行:/git-push
|
||||
```
|
||||
|
||||
**Important:** This command does NOT push to remote. Use `/git-push` to push commits and tags.
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
description: Pull latest changes from remote repository
|
||||
---
|
||||
|
||||
Pull the latest changes from the remote repository for the current branch.
|
||||
|
||||
Please perform the following tasks:
|
||||
|
||||
1. **Run `git pull`** to fetch and merge remote changes
|
||||
2. **Handle merge conflicts if they occur:**
|
||||
- Check conflict files with `git status`
|
||||
- Guide me through conflict resolution
|
||||
- Help stage resolved files with `git add <file>`
|
||||
- Complete the merge with `git commit`
|
||||
3. **Show the result** with branch status and changes summary
|
||||
|
||||
If there are any errors or conflicts, explain them clearly in Chinese and provide step-by-step guidance for resolution.
|
||||
|
||||
Present the final result showing:
|
||||
- Branch name
|
||||
- Number of commits pulled
|
||||
- Files changed
|
||||
- Any conflicts that need attention
|
||||
@@ -1,55 +0,0 @@
|
||||
---
|
||||
description: Push all local tags to remote repository
|
||||
---
|
||||
|
||||
Push all local tags to the remote repository.
|
||||
|
||||
Please perform the following:
|
||||
|
||||
1. **List local tags:**
|
||||
```bash
|
||||
git tag --list | sort -V
|
||||
```
|
||||
|
||||
2. **Check remote tags:**
|
||||
```bash
|
||||
git ls-remote --tags origin
|
||||
```
|
||||
|
||||
3. **Identify unpushed tags** - Compare local and remote to find tags that need to be pushed
|
||||
|
||||
4. **Display summary in Chinese:**
|
||||
```
|
||||
本地标签总数:N 个
|
||||
远程标签总数:M 个
|
||||
待推送标签:K 个
|
||||
- [list of unpushed tags]
|
||||
|
||||
确认推送所有标签到 origin?
|
||||
```
|
||||
|
||||
5. **Wait for user confirmation** (or proceed if no confirmation needed)
|
||||
|
||||
6. **Push all tags:**
|
||||
```bash
|
||||
git push --tags
|
||||
```
|
||||
|
||||
7. **Verify and display result in Chinese:**
|
||||
```
|
||||
✓ 已成功推送所有标签到 origin
|
||||
|
||||
最近 5 个远程标签:
|
||||
- [list of recent tags]
|
||||
```
|
||||
|
||||
**Use cases:**
|
||||
- After creating multiple local tags
|
||||
- Sync tags with remote repository
|
||||
- Before triggering CI/CD release workflows
|
||||
- Share version tags with team
|
||||
|
||||
**Notes:**
|
||||
- Pushes ALL local tags, not just one
|
||||
- To push a specific tag: `git push origin <tag-name>`
|
||||
- Safe operation: won't overwrite existing tags (no force push)
|
||||
@@ -1,55 +0,0 @@
|
||||
---
|
||||
description: Commit staged files, create version tag, and push to remote repository
|
||||
---
|
||||
|
||||
Complete workflow: auto-generate commit message, create version tag, commit, and push everything to remote repository.
|
||||
|
||||
This is the **all-in-one command** that combines `/git-commit` + push operations.
|
||||
|
||||
Please perform the following:
|
||||
|
||||
1-7. **Follow the same steps as `/git-commit`:**
|
||||
- Check staging area (must not be empty)
|
||||
- Analyze changes and repository type
|
||||
- Detect project type and version
|
||||
- Generate commit message (Conventional Commits, Chinese)
|
||||
- Update version number if needed
|
||||
- Commit changes
|
||||
- Create version tag
|
||||
|
||||
Refer to `/git-commit` for detailed steps or `@skill/git/SKILL.md` for complete workflow.
|
||||
|
||||
8. **Push commit to remote:**
|
||||
```bash
|
||||
git push origin $(git branch --show-current)
|
||||
```
|
||||
|
||||
9. **Push tag to remote** (only if tag was created):
|
||||
- Polyrepo: `git push origin <version>`
|
||||
- Monorepo: `git push origin <subproject>-<version>`
|
||||
|
||||
10. **Display result in Chinese:**
|
||||
```
|
||||
✓ 提交并推送成功
|
||||
|
||||
分支:[branch]
|
||||
提交信息:[commit message]
|
||||
版本标签:[tag] (如果创建了)
|
||||
|
||||
已推送到远程仓库:origin
|
||||
- 提交:[commit hash]
|
||||
- 标签:[tag]
|
||||
```
|
||||
|
||||
**Error handling:**
|
||||
- If staging area is empty: "暂存区为空,请先使用 `git add` 添加文件。"
|
||||
- If push fails (e.g., need to pull first): Show error in Chinese with suggested solutions
|
||||
- If remote rejects tag (already exists): Show error and suggest deleting local tag or updating version
|
||||
|
||||
**Options:**
|
||||
- User can input "skip tag" or "skip" to skip tag creation
|
||||
|
||||
**When to use:**
|
||||
- `/git-commit`: Local only, review before pushing
|
||||
- `/git-push`: Commit and push immediately
|
||||
- `/git-push-tags`: Push tags only (no commits)
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
description: Check git working directory status and file changes
|
||||
---
|
||||
|
||||
Check the current git repository status and display file changes in a clear, organized format.
|
||||
|
||||
Please perform the following tasks:
|
||||
|
||||
1. **Run `git status`** to show the current state of the working tree
|
||||
2. **Display current branch** using `git branch --show-current`
|
||||
3. **List unstaged changes** with `git diff --name-only`
|
||||
4. **List staged changes** with `git diff --cached --name-only`
|
||||
|
||||
Present the information in Chinese with the following structure:
|
||||
|
||||
```
|
||||
当前分支: [branch_name]
|
||||
|
||||
暂存区文件 ([count]):
|
||||
[列出已暂存的文件]
|
||||
|
||||
未暂存的修改 ([count]):
|
||||
[列出未暂存的修改文件]
|
||||
|
||||
未跟踪的文件 ([count]):
|
||||
[列出未跟踪的文件]
|
||||
```
|
||||
|
||||
Use clear formatting and emoji indicators (✓, ⚠️, etc.) to make the output easy to read.
|
||||
@@ -1,72 +0,0 @@
|
||||
---
|
||||
description: View current Gitea configuration and runner status
|
||||
---
|
||||
|
||||
Display the current Gitea configuration and runner status.
|
||||
|
||||
## 工作目录
|
||||
|
||||
**macOS / Linux:**
|
||||
```
|
||||
~/.config/gitea/
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%USERPROFILE%\.config\gitea\
|
||||
```
|
||||
|
||||
Please perform the following:
|
||||
|
||||
1. **Check if configuration exists:**
|
||||
- Config file:
|
||||
- macOS/Linux: `~/.config/gitea/config.env`
|
||||
- Windows: `%USERPROFILE%\.config\gitea\config.env`
|
||||
- If not exists, prompt user to run `/gitea-reset`
|
||||
|
||||
2. **Load and display configuration:**
|
||||
|
||||
**macOS / Linux:**
|
||||
```bash
|
||||
source ~/.config/gitea/config.env
|
||||
```
|
||||
|
||||
**Windows PowerShell:**
|
||||
```powershell
|
||||
Get-Content "$env:USERPROFILE\.config\gitea\config.env" | ForEach-Object {
|
||||
if ($_ -match '^([^=]+)=(.*)$') {
|
||||
[Environment]::SetEnvironmentVariable($matches[1], $matches[2], 'Process')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Show in Chinese:
|
||||
- Gitea URL
|
||||
- Default organization (if set)
|
||||
- Config file path (根据平台显示正确路径)
|
||||
|
||||
3. **Validate token and display user info:**
|
||||
- Call API: `GET ${GITEA_URL}/api/v1/user`
|
||||
- Header: `Authorization: token ${GITEA_TOKEN}`
|
||||
- Show: Token status (✓ 有效 / ✗ 无效), username, email
|
||||
|
||||
4. **Display runner information:**
|
||||
- Runners directory:
|
||||
- macOS/Linux: `~/.config/gitea/runners`
|
||||
- Windows: `%USERPROFILE%\.config\gitea\runners`
|
||||
- Count configured runners
|
||||
- List each runner with status:
|
||||
- 🟢 运行中 (process running)
|
||||
- 🔴 已停止 (process not running)
|
||||
- ⚠️ 配置异常 (config file missing)
|
||||
|
||||
5. **Show management commands:**
|
||||
```
|
||||
管理命令:
|
||||
- 重置配置: /gitea-reset
|
||||
- 切换组织: /gitea-switch-org <org-name>
|
||||
- 列出 Runners: /gitea-list-runners
|
||||
- 创建仓库: /create-gitea-repo <repo-name>
|
||||
```
|
||||
|
||||
Use `jq` to parse JSON responses and `pgrep` to check runner process status.
|
||||
@@ -1,104 +0,0 @@
|
||||
---
|
||||
description: Create a new Git repository on Gitea
|
||||
---
|
||||
|
||||
Create a new Git repository on Gitea via API.
|
||||
|
||||
## 工作目录
|
||||
|
||||
**macOS / Linux:**
|
||||
```
|
||||
~/.config/gitea/
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%USERPROFILE%\.config\gitea\
|
||||
```
|
||||
|
||||
配置文件从该目录加载。
|
||||
|
||||
**User input format:**
|
||||
```
|
||||
$ARGUMENTS = [<owner>/]<repo> [private|public]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
- `my-project` - Private repo under default org or current user
|
||||
- `ai/my-project` - Private repo under ai organization
|
||||
- `ai/my-project public` - Public repo under ai organization
|
||||
- `username/test private` - Private repo under username
|
||||
|
||||
Please perform the following:
|
||||
|
||||
1. **Load configuration:**
|
||||
|
||||
**macOS / Linux:**
|
||||
```bash
|
||||
source ~/.config/gitea/config.env
|
||||
```
|
||||
|
||||
**Windows PowerShell:**
|
||||
```powershell
|
||||
Get-Content "$env:USERPROFILE\.config\gitea\config.env" | ForEach-Object {
|
||||
if ($_ -match '^([^=]+)=(.*)$') {
|
||||
[Environment]::SetEnvironmentVariable($matches[1], $matches[2], 'Process')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- If not exists: prompt to run `/gitea-reset`
|
||||
|
||||
2. **Parse user input from `$ARGUMENTS`:**
|
||||
- Extract: owner (optional), repo (required), visibility (optional, default: private)
|
||||
- If no owner specified:
|
||||
- Use `GITEA_DEFAULT_ORG` if set
|
||||
- Otherwise get current user from API: `GET /api/v1/user`
|
||||
- Validate repo name: only letters, numbers, underscores, hyphens, dots
|
||||
|
||||
3. **Create repository via API:**
|
||||
- Try organization API first:
|
||||
```bash
|
||||
POST ${GITEA_URL}/api/v1/orgs/${owner}/repos
|
||||
Body: {
|
||||
"name": "${repo}",
|
||||
"private": true/false,
|
||||
"auto_init": false,
|
||||
"default_branch": "main"
|
||||
}
|
||||
```
|
||||
- If 404, try user API: `POST /api/v1/user/repos`
|
||||
- Handle response codes:
|
||||
- 201: Success
|
||||
- 409: Repository already exists
|
||||
- 404: Owner not found or no permission
|
||||
- Other: API error
|
||||
|
||||
4. **Extract repository info from response:**
|
||||
- `html_url` - Web URL
|
||||
- `clone_url` - HTTPS URL
|
||||
- `ssh_url` - SSH URL
|
||||
|
||||
5. **Ask user if they want to add remote:**
|
||||
- Check if current directory is a git repo
|
||||
- If not, ask to initialize: `git init`
|
||||
- Check if `origin` remote exists
|
||||
- Add or update remote: `git remote add/set-url origin <clone_url>`
|
||||
|
||||
6. **Display result in Chinese:**
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
仓库创建成功
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
名称: [owner]/[repo]
|
||||
可见性: [private/public]
|
||||
Web URL: [html_url]
|
||||
HTTPS URL: [clone_url]
|
||||
SSH URL: [ssh_url]
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Requires repo creation permission in token
|
||||
- Organization and user repositories use different API endpoints
|
||||
- Default branch is `main` (not `master`)
|
||||
@@ -1,770 +0,0 @@
|
||||
---
|
||||
description: Create and start a Gitea Actions runner (default host mode)
|
||||
agent: general
|
||||
subtask: true
|
||||
---
|
||||
|
||||
# 创建并启动 Gitea Actions Runner
|
||||
|
||||
你的任务是创建并启动一个 Gitea Actions Runner(默认使用 host 模式)。
|
||||
|
||||
## 工作目录
|
||||
|
||||
**macOS / Linux:**
|
||||
```
|
||||
~/.config/gitea/
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%USERPROFILE%\.config\gitea\
|
||||
```
|
||||
|
||||
所有 Runner 配置、缓存、工作区都存储在该目录的 `runners/` 子目录下。
|
||||
|
||||
## 核心功能要求
|
||||
|
||||
请按照以下步骤执行:
|
||||
|
||||
1. **检查 act_runner 安装**
|
||||
- 检查 act_runner 是否已安装(使用 `command -v act_runner`)
|
||||
- 如果未安装,使用 Homebrew 自动安装:`brew install act_runner`
|
||||
- 如果 Homebrew 不存在,提示用户安装 Homebrew
|
||||
- 验证安装成功后显示版本信息
|
||||
|
||||
2. **加载 Gitea 配置**
|
||||
- 从配置文件加载配置:
|
||||
- macOS/Linux: `~/.config/gitea/config.env`
|
||||
- Windows: `%USERPROFILE%\.config\gitea\config.env`
|
||||
- 如果配置文件不存在,提示用户运行 `/gitea-reset` 初始化
|
||||
- 验证必需的配置项:`GITEA_URL` 和 `GITEA_TOKEN`
|
||||
- 显示加载成功的配置信息
|
||||
|
||||
3. **生成 Runner 名称**
|
||||
- 如果用户提供了参数 `$ARGUMENTS`,使用该名称
|
||||
- 否则默认基于主机名生成:`runner-$(hostname -s)`
|
||||
- 验证名称只包含字母、数字、下划线和连字符
|
||||
|
||||
4. **检查 Runner 是否已存在**
|
||||
- 检查 Runner 目录:
|
||||
- macOS/Linux: `~/.config/gitea/runners/$runner_name`
|
||||
- Windows: `%USERPROFILE%\.config\gitea\runners\$runner_name`
|
||||
- 如果已存在,提示用户可选操作:
|
||||
- 使用其他名称
|
||||
- 删除现有 runner(使用 `/gitea-delete-runner`)
|
||||
- 查看所有 runners(使用 `/gitea-list-runners`)
|
||||
|
||||
5. **检测系统环境**
|
||||
- 检测操作系统(macOS/Linux)
|
||||
- 检测架构(ARM64/x64)
|
||||
- 生成 host 模式的 labels:
|
||||
- `self-hosted:host`
|
||||
- `{os_label}:host`(如 `macOS:host`)
|
||||
- `{arch_label}:host`(如 `ARM64:host`)
|
||||
- `{combined}:host`(如 `darwin-arm64:host`)
|
||||
|
||||
6. **创建 Runner 目录结构**
|
||||
- 创建目录结构:
|
||||
- macOS/Linux: `~/.config/gitea/runners/$runner_name/{cache,workspace}`
|
||||
- Windows: `%USERPROFILE%\.config\gitea\runners\$runner_name\{cache,workspace}`
|
||||
- 验证目录创建成功
|
||||
|
||||
7. **生成配置文件**
|
||||
- 创建 `config.yaml`,使用 host 模式配置
|
||||
- 从环境变量读取可选配置:
|
||||
- `GITEA_RUNNER_CAPACITY`(默认 2)
|
||||
- `GITEA_RUNNER_TIMEOUT`(默认 3h)
|
||||
- 配置要点:
|
||||
- log level: info
|
||||
- runner.capacity: 并发任务数
|
||||
- runner.timeout: 任务超时
|
||||
- cache: 启用缓存
|
||||
- host.workdir_parent: 工作目录路径
|
||||
- labels: 使用检测到的系统 labels
|
||||
|
||||
8. **获取注册 Token**
|
||||
- 优先尝试创建全局 Runner(需要管理员权限)
|
||||
- API: `GET ${GITEA_URL}/api/v1/admin/runners/registration-token`
|
||||
- Header: `Authorization: token ${GITEA_TOKEN}`
|
||||
- 如果返回 403/权限不足,自动降级到组织 Runner
|
||||
- 使用 `GITEA_DEFAULT_ORG` 作为组织名
|
||||
- API: `POST ${GITEA_URL}/api/v1/orgs/${org_name}/actions/runners/registration-token`
|
||||
- 从响应中提取 token(JSON 格式)
|
||||
- 记录使用的 runner 级别(global 或 organization)
|
||||
|
||||
9. **注册 Runner**
|
||||
- 执行命令:
|
||||
```bash
|
||||
act_runner register \
|
||||
--config "$runner_dir/config.yaml" \
|
||||
--instance "$GITEA_URL" \
|
||||
--token "$registration_token" \
|
||||
--name "$runner_name" \
|
||||
--labels "$labels" \
|
||||
--no-interactive
|
||||
```
|
||||
- 验证 `.runner` 文件是否创建
|
||||
- 如果失败,提供诊断建议
|
||||
|
||||
10. **后台启动 Runner**
|
||||
- 使用 nohup 后台启动:
|
||||
```bash
|
||||
nohup act_runner daemon --config "$runner_dir/config.yaml" \
|
||||
> "$runner_dir/runner.log" 2>&1 &
|
||||
```
|
||||
- 记录进程 PID
|
||||
- 等待 3 秒初始化
|
||||
- 验证进程仍在运行
|
||||
- 如果失败,显示日志的最后 20 行
|
||||
|
||||
11. **显示创建摘要**
|
||||
- Runner 信息:名称、级别、模式、状态、PID
|
||||
- 如果是组织级别,显示组织名
|
||||
- 配置信息:容量、超时、labels
|
||||
- 目录信息:配置文件、工作目录、缓存目录、日志文件
|
||||
- 管理命令:查看日志、停止 Runner、查看所有、删除
|
||||
- 使用示例:workflow 中的 runs-on 配置
|
||||
|
||||
## 重要配置说明
|
||||
|
||||
- **默认模式**:Host Mode(直接在宿主机执行,支持 Android SDK、iOS 构建等原生工具)
|
||||
- **目录结构**:
|
||||
- macOS/Linux: 所有 runners 位于 `~/.config/gitea/runners/`
|
||||
- Windows: 所有 runners 位于 `%USERPROFILE%\.config\gitea\runners\`
|
||||
- **优雅降级**:全局 runner 权限不足时自动降级到组织 runner
|
||||
- **后台运行**:使用 nohup 后台启动(Unix),或 Start-Process(Windows),日志输出到文件
|
||||
- **进程管理**:需要手动管理进程(系统重启后需重新启动)
|
||||
|
||||
## 可选参数
|
||||
|
||||
- `$ARGUMENTS`: Runner 名称(可选,默认基于主机名生成)
|
||||
|
||||
使用示例:
|
||||
```
|
||||
/gitea-create-runner
|
||||
/gitea-create-runner my-custom-runner
|
||||
```
|
||||
|
||||
## 相关命令
|
||||
|
||||
- `/gitea-config`: 查看 Gitea 配置
|
||||
- `/gitea-reset`: 重置/初始化 Gitea 配置
|
||||
- `/gitea-list-runners`: 列出所有 runners
|
||||
- `/gitea-delete-runner`: 删除指定 runner
|
||||
|
||||
---
|
||||
|
||||
## 详细实现步骤(供 AI 参考)
|
||||
|
||||
### 1. Check act_runner Installation
|
||||
|
||||
**AI 执行**:检查 act_runner 是否已安装,如果没有则自动安装。
|
||||
|
||||
```bash
|
||||
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"
|
||||
echo " 安装命令: /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
brew install act_runner
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
version=$(act_runner --version 2>&1 | head -n1)
|
||||
echo "✓ act_runner 安装成功: $version"
|
||||
else
|
||||
echo "❌ act_runner 安装失败"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
```
|
||||
|
||||
### 2. Load Gitea Configuration
|
||||
|
||||
**AI 执行**:加载 Gitea 配置,如果不存在则提示初始化。
|
||||
|
||||
```bash
|
||||
config_file="$HOME/.config/gitea/config.env"
|
||||
|
||||
if [ ! -f "$config_file" ]; then
|
||||
echo "❌ Gitea 配置不存在"
|
||||
echo ""
|
||||
echo "请先初始化 Gitea 配置:"
|
||||
echo " /gitea-reset"
|
||||
echo ""
|
||||
echo "或使用以下命令查看配置:"
|
||||
echo " /gitea-config"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source "$config_file"
|
||||
|
||||
if [ -z "$GITEA_URL" ] || [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "❌ Gitea 配置不完整"
|
||||
echo " 需要 GITEA_URL 和 GITEA_TOKEN"
|
||||
echo ""
|
||||
echo "请重新初始化配置:"
|
||||
echo " /gitea-reset"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ 已加载 Gitea 配置"
|
||||
echo " URL: $GITEA_URL"
|
||||
echo ""
|
||||
```
|
||||
|
||||
### 3. Generate Runner Name
|
||||
|
||||
**AI 执行**:生成 runner 名称(基于主机名或用户输入)。
|
||||
|
||||
```bash
|
||||
# Check if runner name provided as argument
|
||||
# 如果用户提供了名称参数,使用该参数;否则基于主机名生成
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
runner_name="$1"
|
||||
echo "使用指定的 Runner 名称: $runner_name"
|
||||
else
|
||||
hostname=$(hostname -s 2>/dev/null || echo "unknown")
|
||||
runner_name="runner-$hostname"
|
||||
echo "生成 Runner 名称: $runner_name"
|
||||
echo " (基于主机名: $hostname)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Validate runner name
|
||||
if [[ ! "$runner_name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
||||
echo "❌ Runner 名称只能包含字母、数字、下划线和连字符"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### 4. Check Runner Existence
|
||||
|
||||
**AI 执行**:检查 runner 是否已存在。
|
||||
|
||||
```bash
|
||||
runners_dir="$HOME/.config/gitea/runners"
|
||||
runner_dir="$runners_dir/$runner_name"
|
||||
|
||||
if [ -d "$runner_dir" ]; then
|
||||
echo "❌ Runner '$runner_name' 已存在"
|
||||
echo " 路径: $runner_dir"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " 1. 使用其他名称: /gitea-create-runner <新名称>"
|
||||
echo " 2. 删除现有 runner: /gitea-delete-runner"
|
||||
echo " 3. 查看所有 runners: /gitea-list-runners"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### 5. Detect System Environment
|
||||
|
||||
**AI 执行**:智能检测系统环境并生成 labels。
|
||||
|
||||
```bash
|
||||
echo "检测系统环境..."
|
||||
|
||||
# Detect OS
|
||||
OS=$(uname -s)
|
||||
case "$OS" in
|
||||
Darwin) os_label="macOS" ;;
|
||||
Linux) os_label="ubuntu" ;;
|
||||
*) os_label="unknown" ;;
|
||||
esac
|
||||
|
||||
# Detect architecture
|
||||
ARCH=$(uname -m)
|
||||
case "$ARCH" in
|
||||
arm64|aarch64) arch_label="ARM64" ;;
|
||||
x86_64) arch_label="x64" ;;
|
||||
*) arch_label="unknown" ;;
|
||||
esac
|
||||
|
||||
# Generate combined label
|
||||
combined=$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Generate labels for host mode
|
||||
labels="self-hosted:host,${os_label}:host,${arch_label}:host,${combined}:host"
|
||||
|
||||
echo "✓ 系统信息"
|
||||
echo " 操作系统: $OS ($os_label)"
|
||||
echo " 架构: $ARCH ($arch_label)"
|
||||
echo " 组合标签: $combined"
|
||||
echo ""
|
||||
echo "✓ Runner Labels (Host Mode)"
|
||||
echo " $labels"
|
||||
echo ""
|
||||
```
|
||||
|
||||
### 6. Create Runner Directory
|
||||
|
||||
**AI 执行**:创建 runner 目录结构。
|
||||
|
||||
```bash
|
||||
echo "创建 Runner 目录..."
|
||||
|
||||
mkdir -p "$runner_dir"/{cache,workspace}
|
||||
|
||||
if [ ! -d "$runner_dir" ]; then
|
||||
echo "❌ 创建目录失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ 目录创建成功"
|
||||
echo " 路径: $runner_dir"
|
||||
echo " - cache/"
|
||||
echo " - workspace/"
|
||||
echo ""
|
||||
```
|
||||
|
||||
### 7. Generate Configuration File
|
||||
|
||||
**AI 执行**:生成 host 模式配置文件。
|
||||
|
||||
```bash
|
||||
echo "生成配置文件..."
|
||||
|
||||
# Get environment-specific settings (with defaults)
|
||||
runner_capacity="${GITEA_RUNNER_CAPACITY:-2}"
|
||||
runner_timeout="${GITEA_RUNNER_TIMEOUT:-3h}"
|
||||
|
||||
# Generate config.yaml for host mode
|
||||
cat > "$runner_dir/config.yaml" << EOF
|
||||
log:
|
||||
level: info
|
||||
|
||||
runner:
|
||||
file: $runner_dir/.runner
|
||||
capacity: $runner_capacity
|
||||
timeout: $runner_timeout
|
||||
shutdown_timeout: 30s
|
||||
insecure: false
|
||||
fetch_timeout: 5s
|
||||
fetch_interval: 2s
|
||||
labels:
|
||||
- "self-hosted:host"
|
||||
- "${os_label}:host"
|
||||
- "${arch_label}:host"
|
||||
- "${combined}:host"
|
||||
|
||||
cache:
|
||||
enabled: true
|
||||
dir: "$runner_dir/cache"
|
||||
host: "127.0.0.1"
|
||||
port: 9000
|
||||
|
||||
host:
|
||||
workdir_parent: "$runner_dir/workspace"
|
||||
EOF
|
||||
|
||||
if [ ! -f "$runner_dir/config.yaml" ]; then
|
||||
echo "❌ 配置文件生成失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ 配置文件已生成"
|
||||
echo " 文件: $runner_dir/config.yaml"
|
||||
echo " 模式: Host Mode (原生执行)"
|
||||
echo " 容量: $runner_capacity 并发任务"
|
||||
echo " 超时: $runner_timeout"
|
||||
echo ""
|
||||
```
|
||||
|
||||
### 8. Get Registration Token
|
||||
|
||||
**AI 执行**:获取注册 token(优先全局,失败则降级到组织)。
|
||||
|
||||
```bash
|
||||
echo "获取 Runner 注册 Token..."
|
||||
echo ""
|
||||
|
||||
# Try global runner first (requires admin token)
|
||||
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')
|
||||
|
||||
# Check for admin permission
|
||||
if [ "$http_code" = "200" ]; then
|
||||
echo "✓ 使用全局 Runner"
|
||||
runner_level="global"
|
||||
registration_token=$(echo "$body" | jq -r '.token')
|
||||
else
|
||||
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"
|
||||
echo "使用默认组织: $org_name"
|
||||
else
|
||||
echo "❌ 未配置默认组织"
|
||||
echo " 请在配置文件中设置 GITEA_DEFAULT_ORG"
|
||||
echo " 或重新初始化配置: /gitea-reset"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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')
|
||||
|
||||
if [ "$http_code" != "200" ]; then
|
||||
echo "❌ 获取注册 Token 失败 (HTTP $http_code)"
|
||||
echo " 响应: $body"
|
||||
echo ""
|
||||
echo "可能的原因:"
|
||||
echo " 1. 组织不存在"
|
||||
echo " 2. Token 没有组织管理权限"
|
||||
echo " 3. 网络连接问题"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
registration_token=$(echo "$body" | jq -r '.token')
|
||||
echo "✓ 组织 Runner Token 已获取"
|
||||
fi
|
||||
|
||||
if [ -z "$registration_token" ] || [ "$registration_token" = "null" ]; then
|
||||
echo "❌ 无法解析注册 Token"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ 注册 Token 已准备"
|
||||
echo ""
|
||||
```
|
||||
|
||||
### 9. Register Runner
|
||||
|
||||
**AI 执行**:注册 runner 到 Gitea 服务器。
|
||||
|
||||
```bash
|
||||
echo "注册 Runner 到 Gitea 服务器..."
|
||||
|
||||
act_runner register \
|
||||
--config "$runner_dir/config.yaml" \
|
||||
--instance "$GITEA_URL" \
|
||||
--token "$registration_token" \
|
||||
--name "$runner_name" \
|
||||
--labels "$labels" \
|
||||
--no-interactive
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✓ Runner 注册成功"
|
||||
else
|
||||
echo "❌ Runner 注册失败"
|
||||
echo ""
|
||||
echo "请检查:"
|
||||
echo " 1. Token 是否有效"
|
||||
echo " 2. Gitea 服务器是否可访问"
|
||||
echo " 3. 配置文件是否正确"
|
||||
echo ""
|
||||
echo "查看详细错误,可以手动运行:"
|
||||
echo " act_runner register --config $runner_dir/config.yaml --instance $GITEA_URL --token <token> --name $runner_name --labels \"$labels\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify .runner file was created
|
||||
if [ ! -f "$runner_dir/.runner" ]; then
|
||||
echo "⚠️ 注册文件未创建,但注册可能成功"
|
||||
echo " 请检查: $runner_dir/.runner"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
```
|
||||
|
||||
### 10. Start Runner
|
||||
|
||||
**AI 执行**:后台启动 runner 并验证状态。
|
||||
|
||||
```bash
|
||||
echo "启动 Runner..."
|
||||
|
||||
# Start runner in background
|
||||
nohup act_runner daemon --config "$runner_dir/config.yaml" \
|
||||
> "$runner_dir/runner.log" 2>&1 &
|
||||
|
||||
runner_pid=$!
|
||||
|
||||
echo " 进程已启动 (PID: $runner_pid)"
|
||||
|
||||
# Wait for runner to initialize
|
||||
echo " 等待初始化..."
|
||||
sleep 3
|
||||
|
||||
# Check if process is still running
|
||||
if ps -p $runner_pid > /dev/null 2>&1; then
|
||||
echo "✓ Runner 运行中"
|
||||
else
|
||||
echo "❌ Runner 启动失败"
|
||||
echo ""
|
||||
echo "查看日志以诊断问题:"
|
||||
echo " tail -n 50 $runner_dir/runner.log"
|
||||
echo ""
|
||||
tail -n 20 "$runner_dir/runner.log"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
```
|
||||
|
||||
### 11. Display Summary
|
||||
|
||||
**AI 执行**:显示创建结果和管理命令。
|
||||
|
||||
```bash
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "✅ Runner 创建完成!"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "Runner 信息:"
|
||||
echo " 名称: $runner_name"
|
||||
echo " 级别: $runner_level"
|
||||
if [ "$runner_level" = "organization" ]; then
|
||||
echo " 组织: $org_name"
|
||||
fi
|
||||
echo " 模式: Host Mode (原生执行)"
|
||||
echo " 状态: 🟢 运行中"
|
||||
echo " PID: $runner_pid"
|
||||
echo ""
|
||||
echo "配置:"
|
||||
echo " 容量: $runner_capacity 并发任务"
|
||||
echo " 超时: $runner_timeout"
|
||||
echo " Labels: $labels"
|
||||
echo ""
|
||||
echo "目录:"
|
||||
echo " 配置文件: $runner_dir/config.yaml"
|
||||
echo " 注册信息: $runner_dir/.runner"
|
||||
echo " 工作目录: $runner_dir/workspace"
|
||||
echo " 缓存目录: $runner_dir/cache"
|
||||
echo " 日志文件: $runner_dir/runner.log"
|
||||
echo ""
|
||||
echo "管理命令:"
|
||||
echo " 查看日志: tail -f $runner_dir/runner.log"
|
||||
echo " 停止 Runner: pkill -f 'act_runner daemon --config.*$runner_name'"
|
||||
echo " 查看所有: /gitea-list-runners"
|
||||
echo " 删除 Runner: /gitea-delete-runner"
|
||||
echo ""
|
||||
echo "使用 Runner:"
|
||||
echo " 在 workflow 中使用以下任一 runs-on 值:"
|
||||
echo " - runs-on: $combined"
|
||||
echo " - runs-on: $os_label"
|
||||
echo " - runs-on: [self-hosted, $os_label, $arch_label]"
|
||||
echo ""
|
||||
```
|
||||
|
||||
## Output Example
|
||||
|
||||
### Example 1: Successful Creation (Global Runner)
|
||||
|
||||
```
|
||||
检查 act_runner 安装状态...
|
||||
✓ act_runner 已安装: act_runner version 0.2.13
|
||||
|
||||
✓ 已加载 Gitea 配置
|
||||
URL: https://git.digitevents.com
|
||||
|
||||
生成 Runner 名称: runner-macbook-pro
|
||||
(基于主机名: macbook-pro)
|
||||
|
||||
检测系统环境...
|
||||
✓ 系统信息
|
||||
操作系统: Darwin (macOS)
|
||||
架构: arm64 (ARM64)
|
||||
组合标签: darwin-arm64
|
||||
|
||||
✓ Runner Labels (Host Mode)
|
||||
self-hosted:host,macOS:host,ARM64:host,darwin-arm64:host
|
||||
|
||||
创建 Runner 目录...
|
||||
✓ 目录创建成功
|
||||
路径: /Users/voson/.config/gitea/runners/runner-macbook-pro
|
||||
- cache/
|
||||
- workspace/
|
||||
|
||||
生成配置文件...
|
||||
✓ 配置文件已生成
|
||||
文件: /Users/voson/.config/gitea/runners/runner-macbook-pro/config.yaml
|
||||
模式: Host Mode (原生执行)
|
||||
容量: 2 并发任务
|
||||
超时: 3h
|
||||
|
||||
获取 Runner 注册 Token...
|
||||
|
||||
尝试创建全局 Runner(可用于所有组织和仓库)...
|
||||
✓ 使用全局 Runner
|
||||
✓ 注册 Token 已准备
|
||||
|
||||
注册 Runner 到 Gitea 服务器...
|
||||
✓ Runner 注册成功
|
||||
|
||||
启动 Runner...
|
||||
进程已启动 (PID: 12345)
|
||||
等待初始化...
|
||||
✓ Runner 运行中
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Runner 创建完成!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Runner 信息:
|
||||
名称: runner-macbook-pro
|
||||
级别: global
|
||||
模式: Host Mode (原生执行)
|
||||
状态: 🟢 运行中
|
||||
PID: 12345
|
||||
|
||||
配置:
|
||||
容量: 2 并发任务
|
||||
超时: 3h
|
||||
Labels: self-hosted:host,macOS:host,ARM64:host,darwin-arm64:host
|
||||
|
||||
目录:
|
||||
配置文件: /Users/voson/.config/gitea/runners/runner-macbook-pro/config.yaml
|
||||
注册信息: /Users/voson/.config/gitea/runners/runner-macbook-pro/.runner
|
||||
工作目录: /Users/voson/.config/gitea/runners/runner-macbook-pro/workspace
|
||||
缓存目录: /Users/voson/.config/gitea/runners/runner-macbook-pro/cache
|
||||
日志文件: /Users/voson/.config/gitea/runners/runner-macbook-pro/runner.log
|
||||
|
||||
管理命令:
|
||||
查看日志: tail -f /Users/voson/.config/gitea/runners/runner-macbook-pro/runner.log
|
||||
停止 Runner: pkill -f 'act_runner daemon --config.*runner-macbook-pro'
|
||||
查看所有: /gitea-list-runners
|
||||
删除 Runner: /gitea-delete-runner
|
||||
|
||||
使用 Runner:
|
||||
在 workflow 中使用以下任一 runs-on 值:
|
||||
- runs-on: darwin-arm64
|
||||
- runs-on: macOS
|
||||
- runs-on: [self-hosted, macOS, ARM64]
|
||||
```
|
||||
|
||||
### Example 2: Auto-install act_runner
|
||||
|
||||
```
|
||||
检查 act_runner 安装状态...
|
||||
⚠️ act_runner 未安装
|
||||
正在使用 Homebrew 安装...
|
||||
==> Downloading act_runner...
|
||||
==> Installing act_runner...
|
||||
✓ act_runner 安装成功: act_runner version 0.2.13
|
||||
|
||||
[继续后续步骤...]
|
||||
```
|
||||
|
||||
### Example 3: Downgrade to Organization Runner
|
||||
|
||||
```
|
||||
获取 Runner 注册 Token...
|
||||
|
||||
尝试创建全局 Runner(可用于所有组织和仓库)...
|
||||
⚠️ 全局 Runner 权限不足 (HTTP 403)
|
||||
全局 Runner 需要管理员 Token
|
||||
|
||||
降级到组织 Runner...
|
||||
使用默认组织: ai
|
||||
✓ 组织 Runner Token 已获取
|
||||
✓ 注册 Token 已准备
|
||||
|
||||
[继续后续步骤...]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Runner 创建完成!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Runner 信息:
|
||||
名称: runner-macbook-pro
|
||||
级别: organization
|
||||
组织: ai
|
||||
模式: Host Mode (原生执行)
|
||||
[...]
|
||||
```
|
||||
|
||||
### Example 4: Configuration Not Found
|
||||
|
||||
```
|
||||
检查 act_runner 安装状态...
|
||||
✓ act_runner 已安装: act_runner version 0.2.13
|
||||
|
||||
❌ Gitea 配置不存在
|
||||
|
||||
请先初始化 Gitea 配置:
|
||||
/gitea-reset
|
||||
|
||||
或使用以下命令查看配置:
|
||||
/gitea-config
|
||||
```
|
||||
|
||||
### Example 5: Runner Already Exists
|
||||
|
||||
```
|
||||
[前置检查...]
|
||||
|
||||
生成 Runner 名称: runner-macbook-pro
|
||||
(基于主机名: macbook-pro)
|
||||
|
||||
❌ Runner 'runner-macbook-pro' 已存在
|
||||
路径: /Users/voson/.config/gitea/runners/runner-macbook-pro
|
||||
|
||||
选项:
|
||||
1. 使用其他名称: /gitea-create-runner <新名称>
|
||||
2. 删除现有 runner: /gitea-delete-runner
|
||||
3. 查看所有 runners: /gitea-list-runners
|
||||
```
|
||||
|
||||
## Technical Notes
|
||||
|
||||
- **默认模式**:Host Mode(支持 Android SDK、iOS 等原生工具)
|
||||
- **自动安装**:使用 Homebrew 安装 act_runner
|
||||
- **智能检测**:自动检测 OS 和架构,生成合适的 labels
|
||||
- **优雅降级**:全局 runner 权限不足时自动降级到组织 runner
|
||||
- **后台运行**:自动后台启动,日志记录到文件
|
||||
- **配置环境变量**:
|
||||
- `GITEA_RUNNER_CAPACITY`: 并发任务数(默认 2)
|
||||
- `GITEA_RUNNER_TIMEOUT`: 任务超时时间(默认 3h)
|
||||
- **目录权限**:自动创建,权限继承用户默认权限
|
||||
- **进程管理**:使用 nohup 后台运行,支持系统重启后手动重启
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/gitea-config`: 查看 Gitea 配置
|
||||
- `/gitea-reset`: 重置/初始化 Gitea 配置
|
||||
- `/gitea-list-runners`: 列出所有 runners
|
||||
- `/gitea-delete-runner`: 删除指定 runner
|
||||
- `/create-gitea-repo`: 创建 Gitea 仓库
|
||||
|
||||
## Safety Features
|
||||
|
||||
- **配置验证**:检查必需的配置项
|
||||
- **目录冲突检测**:避免覆盖现有 runner
|
||||
- **启动验证**:等待并验证进程启动成功
|
||||
- **错误提示**:清晰的错误信息和解决方案
|
||||
- **日志记录**:所有输出记录到日志文件
|
||||
|
||||
## Version
|
||||
|
||||
- **Command Version**: 1.0
|
||||
- **Last Updated**: 2026-01-12
|
||||
- **Compatible with**: act_runner 0.2.13+, macOS/Linux
|
||||
@@ -1,174 +0,0 @@
|
||||
---
|
||||
description: Delete a Gitea runner configuration (interactive)
|
||||
agent: general
|
||||
subtask: true
|
||||
---
|
||||
|
||||
Delete Gitea runner configuration with interactive selection. This command requires multiple user interactions.
|
||||
|
||||
**Important:** This is an interactive command. Wait for user input at each step before proceeding.
|
||||
|
||||
## 工作目录
|
||||
|
||||
**macOS / Linux:**
|
||||
```
|
||||
~/.config/gitea/
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%USERPROFILE%\.config\gitea\
|
||||
```
|
||||
|
||||
所有 Runner 配置、进程管理都基于此目录。
|
||||
|
||||
Please perform the following steps:
|
||||
|
||||
## Step 1: Load Configuration and Fetch Runners
|
||||
|
||||
1. **Load Gitea configuration:**
|
||||
|
||||
**macOS / Linux:**
|
||||
```bash
|
||||
source ~/.config/gitea/config.env
|
||||
```
|
||||
|
||||
**Windows PowerShell:**
|
||||
```powershell
|
||||
Get-Content "$env:USERPROFILE\.config\gitea\config.env" | ForEach-Object {
|
||||
if ($_ -match '^([^=]+)=(.*)$') {
|
||||
[Environment]::SetEnvironmentVariable($matches[1], $matches[2], 'Process')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Validate `GITEA_URL` and `GITEA_TOKEN` exist
|
||||
|
||||
2. **Fetch global runners from Gitea server:**
|
||||
- API: `GET ${GITEA_URL}/api/v1/admin/actions/runners`
|
||||
- Requires admin permissions
|
||||
- If fails: show error and check token permissions
|
||||
|
||||
3. **Display runners list in Chinese:**
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Gitea 全局 Runners (共 N 个)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. [runner-name] [ID: XX] 🟢 在线/🔴 离线
|
||||
2. [runner-name] [ID: XX] 🟢 在线/🔴 离线
|
||||
...
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
选择要删除的 Runner
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
输入序号: 删除单个 runner
|
||||
输入 'all': 删除所有 runners
|
||||
输入 'q' 或 'quit': 取消
|
||||
```
|
||||
|
||||
4. **Wait for user selection** → Do NOT proceed until user responds
|
||||
|
||||
## Step 2: Process User Selection
|
||||
|
||||
Based on user input:
|
||||
- If `q` or `quit`: Cancel and exit
|
||||
- If `all`: Prepare to delete all runners
|
||||
- If number: Validate and prepare to delete that runner
|
||||
|
||||
## Step 3: Display Warning and Wait for Confirmation
|
||||
|
||||
Display deletion warning in Chinese:
|
||||
```
|
||||
⚠️ 警告: 此操作将执行以下操作:
|
||||
- 从 Gitea 服务器注销 runner
|
||||
- 停止本地运行的 runner 进程
|
||||
- 删除 runner 配置文件
|
||||
- 删除 cache 和 workspace 目录
|
||||
- 删除所有相关数据
|
||||
|
||||
将删除以下 runners:
|
||||
- [list of runners to be deleted]
|
||||
|
||||
确认删除? 输入 'yes' 继续:
|
||||
```
|
||||
|
||||
**Wait for user confirmation** → Do NOT proceed until user types 'yes'
|
||||
|
||||
## Step 4: Execute Deletion
|
||||
|
||||
If user confirmed with 'yes', for each selected runner:
|
||||
|
||||
### 4.1 Unregister from Gitea Server
|
||||
```bash
|
||||
DELETE ${GITEA_URL}/api/v1/admin/actions/runners/${runner_id}
|
||||
```
|
||||
- Expected: HTTP 204
|
||||
- Show: "✓ 已从服务器注销" or "⚠️ 注销失败"
|
||||
|
||||
### 4.2 Stop Local Process
|
||||
1. Find local runner directory by matching ID in `.runner` file
|
||||
2. Check if process is running: `pgrep -f "act_runner daemon --config ..."`
|
||||
3. If running:
|
||||
- Check if runner is busy (executing jobs) via API
|
||||
- **If busy, wait for user choice:**
|
||||
```
|
||||
⚠️ 警告: Runner 正在执行 job!
|
||||
选项:
|
||||
1. 等待 job 完成后再停止(推荐)
|
||||
2. 强制立即停止
|
||||
```
|
||||
- **Wait for user input** → Proceed based on choice
|
||||
- If waiting: Poll status every 10 seconds, max 5 minutes
|
||||
- Stop process: `kill $pid` (graceful), then `kill -9 $pid` if needed (force)
|
||||
|
||||
### 4.3 Delete Local Directory
|
||||
|
||||
**macOS / Linux:**
|
||||
```bash
|
||||
rm -rf ~/.config/gitea/runners/[runner_name]
|
||||
```
|
||||
|
||||
**Windows PowerShell:**
|
||||
```powershell
|
||||
Remove-Item -Path "$env:USERPROFILE\.config\gitea\runners\[runner_name]" -Recurse -Force
|
||||
```
|
||||
|
||||
## Step 5: Display Result
|
||||
|
||||
Show completion summary in Chinese:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
删除完成
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
成功: N 个
|
||||
失败: M 个 (if any)
|
||||
|
||||
管理命令:
|
||||
查看剩余 runners: /gitea-list-runners
|
||||
创建新 runner: /gitea-create-runner
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Points
|
||||
|
||||
**Interactive checkpoints (wait for user input):**
|
||||
1. Step 1: After displaying runners list → Wait for selection
|
||||
2. Step 3: After displaying warning → Wait for confirmation ('yes')
|
||||
3. Step 4.2: If runner is busy → Wait for stop choice (1 or 2)
|
||||
|
||||
**Safety features:**
|
||||
- Double confirmation required
|
||||
- Busy status detection
|
||||
- Graceful stop before force kill
|
||||
- Three-step deletion process
|
||||
- Clear status reporting
|
||||
|
||||
**Technical notes:**
|
||||
- Requires `jq` for JSON parsing
|
||||
- Requires admin token for global runners
|
||||
- Uses temporary file `/tmp/gitea_runners.txt` for data passing
|
||||
- Cleanup temp file after completion
|
||||
@@ -1,94 +0,0 @@
|
||||
---
|
||||
description: List all global runners on Gitea server
|
||||
---
|
||||
|
||||
List all global runners registered on the Gitea server.
|
||||
|
||||
## 工作目录
|
||||
|
||||
**macOS / Linux:**
|
||||
```
|
||||
~/.config/gitea/
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%USERPROFILE%\.config\gitea\
|
||||
```
|
||||
|
||||
Please perform the following:
|
||||
|
||||
1. **Read Gitea configuration:**
|
||||
- Read from config file:
|
||||
- macOS/Linux: `~/.config/gitea/config.env`
|
||||
- Windows: `%USERPROFILE%\.config\gitea\config.env`
|
||||
- Extract:
|
||||
- `GITEA_URL`: Gitea server URL
|
||||
- `GITEA_TOKEN`: API token (admin permission required)
|
||||
- If config not found: prompt user to run `/gitea-reset` first
|
||||
|
||||
2. **Call Gitea API to list runners:**
|
||||
```bash
|
||||
curl -s -H "Authorization: token <token>" \
|
||||
"<server>/api/v1/admin/actions/runners"
|
||||
```
|
||||
|
||||
3. **Parse JSON response and extract information:**
|
||||
- Response structure:
|
||||
```json
|
||||
{
|
||||
"runners": [...],
|
||||
"total_count": 1
|
||||
}
|
||||
```
|
||||
- Use `jq` to parse JSON
|
||||
- For each runner in `runners` array:
|
||||
- `id`: Runner ID
|
||||
- `name`: Runner name
|
||||
- `status`: Runner status ("online"/"offline")
|
||||
- `busy`: Whether runner is currently busy (true/false)
|
||||
- `ephemeral`: Whether runner is ephemeral (true/false)
|
||||
- `labels`: Array of label objects with `name` and `type`
|
||||
|
||||
4. **Determine runner status:**
|
||||
- 🟢 在线 - `status: "online"`
|
||||
- 🔴 离线 - `status: "offline"`
|
||||
- ⚠️ 未知 - Unable to determine
|
||||
|
||||
5. **Filter global runners:**
|
||||
- The API endpoint `/api/v1/admin/actions/runners` returns all global runners
|
||||
- These are runners registered at the instance level (not org or repo specific)
|
||||
|
||||
6. **Display summary in Chinese:**
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Gitea 全局 Runners
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
服务器: [server_url]
|
||||
总计: N 个全局 runner
|
||||
|
||||
[runner-name]
|
||||
状态: 🟢/🔴 [在线/离线]
|
||||
ID: [id]
|
||||
忙碌: 是/否
|
||||
临时: 是/否
|
||||
标签: [comma-separated labels]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
管理命令
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
查看配置: /gitea-config
|
||||
创建 runner: /gitea-create-runner
|
||||
删除 runner: /gitea-delete-runner
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Requires admin API token to list runners
|
||||
- **Correct API endpoint**: `/api/v1/admin/actions/runners` (not `/api/v1/admin/runners`)
|
||||
- Only shows global runners (instance-level runners)
|
||||
- Uses `jq` for JSON parsing
|
||||
- Response includes: `id`, `name`, `status`, `busy`, `ephemeral`, `labels`
|
||||
- If API call fails, show error message and suggest checking token permissions
|
||||
- Tested with Gitea version 1.25.3
|
||||
@@ -1,132 +0,0 @@
|
||||
---
|
||||
description: Reset Gitea configuration with interactive setup wizard
|
||||
agent: general
|
||||
subtask: true
|
||||
---
|
||||
|
||||
Launch an interactive configuration wizard to set up or reset Gitea configuration.
|
||||
|
||||
## 工作目录
|
||||
|
||||
**macOS / Linux:**
|
||||
```
|
||||
~/.config/gitea/
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%USERPROFILE%\.config\gitea\
|
||||
```
|
||||
|
||||
配置文件和 Runner 目录都将存储在此位置。
|
||||
|
||||
Please perform the following:
|
||||
|
||||
1. **Create configuration directory:**
|
||||
|
||||
**macOS / Linux:**
|
||||
```bash
|
||||
mkdir -p ~/.config/gitea/runners
|
||||
```
|
||||
|
||||
**Windows PowerShell:**
|
||||
```powershell
|
||||
New-Item -Path "$env:USERPROFILE\.config\gitea\runners" -ItemType Directory -Force
|
||||
```
|
||||
|
||||
2. **Interactive input - Gitea URL:**
|
||||
- Prompt: "请输入 Gitea 实例地址 (例如: https://git.digitevents.com):"
|
||||
- Validate: Must start with http:// or https://
|
||||
- Remove trailing slash
|
||||
|
||||
3. **Interactive input - Personal Access Token:**
|
||||
- Prompt: "请输入 Personal Access Token:"
|
||||
- Before input, show help:
|
||||
```
|
||||
提示:获取 Personal Access Token 的步骤:
|
||||
1. 登录 Gitea
|
||||
2. 右上角头像 → 设置 → 应用 → 访问令牌
|
||||
3. 点击 "生成新令牌"
|
||||
4. 设置令牌名称(如 opencode-cli)
|
||||
5. 选择权限:repo, admin:org, write:runner(推荐)
|
||||
6. 点击 "生成令牌"
|
||||
7. 复制生成的 Token
|
||||
```
|
||||
- Validate: Not empty
|
||||
- Use `read -sp` for secure input (password style)
|
||||
|
||||
4. **Test connection:**
|
||||
- API: `GET ${GITEA_URL}/api/v1/user`
|
||||
- If fails: Show error and possible reasons
|
||||
- If success: Show username
|
||||
|
||||
5. **Validate token permissions:**
|
||||
- Check `repo`: `GET /api/v1/user/repos` (required)
|
||||
- Check `admin:org`: `GET /api/v1/user/orgs` (optional, for org runners)
|
||||
- Check `write:runner`: Try to get registration token (optional, for runners)
|
||||
- Warn if missing required permissions
|
||||
|
||||
6. **Input default organization (optional):**
|
||||
- Prompt: "请输入默认组织名称 (回车跳过):"
|
||||
- If provided, validate it exists: `GET /api/v1/orgs/${org_name}`
|
||||
|
||||
7. **Save configuration:**
|
||||
|
||||
**macOS / Linux** - Save to `~/.config/gitea/config.env`:
|
||||
```bash
|
||||
GITEA_URL=...
|
||||
GITEA_TOKEN=...
|
||||
GITEA_DEFAULT_ORG=... (if set)
|
||||
GITEA_RUNNER_CAPACITY=2
|
||||
GITEA_RUNNER_TIMEOUT=3h
|
||||
```
|
||||
- Set permissions: `chmod 600 ~/.config/gitea/config.env`
|
||||
- Create `.gitignore` to exclude sensitive files
|
||||
|
||||
**Windows** - Save to `%USERPROFILE%\.config\gitea\config.env`:
|
||||
```bash
|
||||
GITEA_URL=...
|
||||
GITEA_TOKEN=...
|
||||
GITEA_DEFAULT_ORG=... (if set)
|
||||
GITEA_RUNNER_CAPACITY=2
|
||||
GITEA_RUNNER_TIMEOUT=3h
|
||||
```
|
||||
- Set file permissions to restrict access (see setup-guide.md for PowerShell script)
|
||||
- Create `.gitignore` to exclude sensitive files
|
||||
|
||||
8. **Display completion summary in Chinese:**
|
||||
|
||||
**macOS / Linux:**
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
配置完成
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
配置文件: ~/.config/gitea/config.env
|
||||
Runner 目录: ~/.config/gitea/runners
|
||||
|
||||
下一步:
|
||||
- 查看配置: /gitea-config
|
||||
- 创建 Runner: 告诉 AI '创建一个 runner'
|
||||
- 创建仓库: /create-gitea-repo <repo-name>
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
配置完成
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
配置文件: %USERPROFILE%\.config\gitea\config.env
|
||||
Runner 目录: %USERPROFILE%\.config\gitea\runners
|
||||
|
||||
下一步:
|
||||
- 查看配置: /gitea-config
|
||||
- 创建 Runner: 告诉 AI '创建一个 runner'
|
||||
- 创建仓库: /create-gitea-repo <repo-name>
|
||||
```
|
||||
|
||||
**Security notes:**
|
||||
- Config file permissions: 600 (owner read/write only)
|
||||
- Token is never displayed after initial input
|
||||
- Sensitive files excluded in .gitignore
|
||||
@@ -1,82 +0,0 @@
|
||||
---
|
||||
description: Switch default Gitea organization
|
||||
---
|
||||
|
||||
Switch the default Gitea organization in configuration.
|
||||
|
||||
## 工作目录
|
||||
|
||||
**macOS / Linux:**
|
||||
```
|
||||
~/.config/gitea/
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%USERPROFILE%\.config\gitea\
|
||||
```
|
||||
|
||||
**User input format:** `$ARGUMENTS` = organization name
|
||||
|
||||
**Example usage:**
|
||||
```
|
||||
/gitea-switch-org ai
|
||||
/gitea-switch-org my-team
|
||||
```
|
||||
|
||||
Please perform the following:
|
||||
|
||||
1. **Check configuration exists:**
|
||||
- Config file:
|
||||
- macOS/Linux: `~/.config/gitea/config.env`
|
||||
- Windows: `%USERPROFILE%\.config\gitea\config.env`
|
||||
- If not exists: prompt user to run `/gitea-reset`
|
||||
|
||||
2. **Load configuration:**
|
||||
|
||||
**macOS / Linux:**
|
||||
```bash
|
||||
source ~/.config/gitea/config.env
|
||||
```
|
||||
|
||||
**Windows PowerShell:**
|
||||
```powershell
|
||||
Get-Content "$env:USERPROFILE\.config\gitea\config.env" | ForEach-Object {
|
||||
if ($_ -match '^([^=]+)=(.*)$') {
|
||||
[Environment]::SetEnvironmentVariable($matches[1], $matches[2], 'Process')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Parse user input:**
|
||||
- Organization name from `$ARGUMENTS`
|
||||
- If empty: show usage and examples
|
||||
|
||||
4. **Validate organization exists:**
|
||||
- API: `GET ${GITEA_URL}/api/v1/orgs/${org_name}`
|
||||
- Header: `Authorization: token ${GITEA_TOKEN}`
|
||||
- If 404: Show error and list available organizations:
|
||||
```bash
|
||||
curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||||
"${GITEA_URL}/api/v1/user/orgs" | jq -r '.[].username'
|
||||
```
|
||||
|
||||
5. **Update configuration file:**
|
||||
- If `GITEA_DEFAULT_ORG` exists: update the line
|
||||
- If not exists: add after `GITEA_TOKEN`
|
||||
- Handle macOS (sed -i '') and Linux (sed -i) differences
|
||||
|
||||
6. **Display result in Chinese:**
|
||||
```
|
||||
✓ 默认组织已切换到: [org_name]
|
||||
|
||||
现在可以:
|
||||
- 创建仓库: /create-gitea-repo my-project
|
||||
(将创建到 [org_name]/my-project)
|
||||
- 查看配置: /gitea-config
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Switching organization doesn't affect existing repositories or runners
|
||||
- Only affects default owner for future repository creation
|
||||
- Can switch anytime
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
description: Review code or documentation and provide suggestions
|
||||
agent: plan
|
||||
---
|
||||
|
||||
Review the code or documentation and provide detailed feedback.
|
||||
|
||||
Please perform the following:
|
||||
|
||||
1. **Review** the code or documentation mentioned by the user
|
||||
2. **Identify issues** and areas for improvement
|
||||
3. **Provide suggestions** with clear explanations
|
||||
4. **Ask for confirmation** before making modifications
|
||||
|
||||
If the user hasn't specified what to review, ask them to provide the code or documentation they want reviewed.
|
||||
|
||||
When suggesting changes:
|
||||
- Explain why each change is needed
|
||||
- Prioritize suggestions by importance
|
||||
- Ask if the user wants all changes applied or only specific ones
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"mcp": {},
|
||||
"permission": "allow",
|
||||
"plugin": ["@mohak34/opencode-notifier@latest"],
|
||||
"provider": {
|
||||
"opencode": {
|
||||
"models": {
|
||||
@@ -10,19 +13,13 @@
|
||||
"budgetTokens": 16000
|
||||
}
|
||||
}
|
||||
},
|
||||
"claude-sonnet-4-5": {
|
||||
"options": {
|
||||
"thinking": {
|
||||
"type": "enabled",
|
||||
"budgetTokens": 16000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"zhipuai-coding-plan": {
|
||||
"options": {
|
||||
"apiKey": "0f76aea86295476dbfa98724013b0fe8.o2EaJVqcl4Cf7WLP"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mcp": {
|
||||
},
|
||||
"permission": "allow"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import type { Plugin } from "@opencode-ai/plugin"
|
||||
|
||||
/**
|
||||
* 通知 Plugin
|
||||
* 在会话完成或发生错误时发送系统通知
|
||||
*/
|
||||
export const NotificationPlugin: Plugin = async ({ project, client, $ }) => {
|
||||
// 检测操作系统
|
||||
const platform = process.platform
|
||||
|
||||
/**
|
||||
* 发送系统通知
|
||||
* @param title 通知标题
|
||||
* @param message 通知内容
|
||||
* @param isError 是否为错误通知
|
||||
*/
|
||||
const sendNotification = async (title: string, message: string, isError: boolean = false) => {
|
||||
try {
|
||||
if (platform === "darwin") {
|
||||
// macOS - 使用 osascript
|
||||
const sound = isError ? ' sound name "Basso"' : ''
|
||||
await $`osascript -e 'display notification "${message}" with title "${title}"${sound}'`
|
||||
} else if (platform === "linux") {
|
||||
// Linux - 使用 notify-send
|
||||
const urgency = isError ? "-u critical" : ""
|
||||
await $`notify-send "${title}" "${message}" ${urgency}`
|
||||
} else if (platform === "win32") {
|
||||
// Windows - 使用 PowerShell
|
||||
const script = `Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('${message}', '${title}')`
|
||||
await $`powershell -Command "& {${script}}"`
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
await client.app.log({
|
||||
service: "notification",
|
||||
level: "info",
|
||||
message: `已发送通知: ${title} - ${message}`,
|
||||
})
|
||||
} catch (error) {
|
||||
// 如果通知发送失败,记录错误但不中断流程
|
||||
await client.app.log({
|
||||
service: "notification",
|
||||
level: "error",
|
||||
message: `发送通知失败: ${error}`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
event: async ({ event }) => {
|
||||
const projectName = project?.name || "OpenCode"
|
||||
|
||||
// 会话完成时发送通知
|
||||
if (event.type === "session.idle") {
|
||||
await sendNotification(
|
||||
projectName,
|
||||
"会话已完成!",
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
// 会话错误时发送通知
|
||||
if (event.type === "session.error") {
|
||||
await sendNotification(
|
||||
projectName,
|
||||
"会话发生错误!",
|
||||
true
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
442
skill/agent-browser/SKILL.md
Normal file
442
skill/agent-browser/SKILL.md
Normal file
@@ -0,0 +1,442 @@
|
||||
---
|
||||
name: ab
|
||||
description: 浏览器自动化工具,用于网页测试、表单填写、截图、数据提取等
|
||||
---
|
||||
|
||||
# 浏览器自动化工具 agent-browser
|
||||
|
||||
ab是 agent-browser 的缩写,它是一个为 AI 代理设计的无头浏览器自动化 CLI 工具,基于 Rust 和 Playwright 构建。支持快速导航、元素交互、页面截图、数据提取等功能。
|
||||
|
||||
注意:必须使用 agent-browser 全称作为命令关键字。
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 安装检查
|
||||
agent-browser 默认已安装。如果遇到 "command not found" 错误,请运行以下命令安装:
|
||||
|
||||
```bash
|
||||
# 全局安装
|
||||
npm install -g agent-browser
|
||||
|
||||
# 下载 Chromium 浏览器
|
||||
agent-browser install
|
||||
```
|
||||
|
||||
### 核心工作流程(4步法)
|
||||
```bash
|
||||
# 使用 agent-browser
|
||||
1. agent-browser open <url>
|
||||
2. agent-browser snapshot -i
|
||||
3. agent-browser click @e1
|
||||
4. agent-browser fill @e2 "文本"
|
||||
```
|
||||
|
||||
## 核心工作流程详解
|
||||
|
||||
### 1. 导航到页面
|
||||
```bash
|
||||
agent-browser open https://example.com
|
||||
```
|
||||
|
||||
### 2. 获取页面快照(推荐使用交互模式)
|
||||
```bash
|
||||
agent-browser snapshot -i # 仅显示交互元素(按钮、输入框、链接)
|
||||
```
|
||||
|
||||
**输出示例:**
|
||||
```
|
||||
- link "Learn more" [ref=e1]
|
||||
- textbox "Email" [ref=e2]
|
||||
- button "Submit" [ref=e3]
|
||||
```
|
||||
|
||||
### 3. 使用 Refs 进行交互
|
||||
快照中的 `[ref=e1]`、`[ref=e2]` 等是元素的唯一引用标识,**推荐使用 refs 而非 CSS 选择器**:
|
||||
- **确定性**:ref 指向快照中的确切元素
|
||||
- **快速**:无需重新查询 DOM
|
||||
- **AI 友好**:适合 LLM 处理
|
||||
|
||||
### 4. 页面变化后重新快照
|
||||
页面发生重大变化(导航、表单提交等)后,需要重新获取快照:
|
||||
```bash
|
||||
agent-browser snapshot -i # 重新获取新的 refs
|
||||
```
|
||||
|
||||
## 命令参考
|
||||
|
||||
### 导航命令
|
||||
```bash
|
||||
agent-browser open <url> # 导航到 URL(或:ab open <url>)
|
||||
agent-browser back # 后退(或:ab back)
|
||||
agent-browser forward # 前进(或:ab forward)
|
||||
agent-browser reload # 重新加载页面(或:ab reload)
|
||||
agent-browser close # 关闭浏览器(或:ab close)
|
||||
```
|
||||
|
||||
### 快照命令(页面分析)
|
||||
```bash
|
||||
agent-browser snapshot # 完整可访问性树(或:ab snapshot)
|
||||
agent-browser snapshot -i # 仅交互元素(推荐)(或:ab snapshot -i)
|
||||
agent-browser snapshot -c # 紧凑输出(移除空结构元素)(或:ab snapshot -c)
|
||||
agent-browser snapshot -d 3 # 限制深度为 3 级(或:ab snapshot -d 3)
|
||||
agent-browser snapshot -s "#main" # 限定到 CSS 选择器范围(或:ab snapshot -s "#main")
|
||||
```
|
||||
|
||||
### 交互命令(使用 @refs)
|
||||
```bash
|
||||
# 基础交互
|
||||
agent-browser click @e1 # 单击
|
||||
agent-browser dblclick @e1 # 双击
|
||||
agent-browser fill @e2 "文本" # 清空并填写
|
||||
agent-browser type @e2 "文本" # 不清空直接输入
|
||||
agent-browser press Enter # 按键(支持组合键 Control+a)
|
||||
agent-browser hover @e1 # 悬停
|
||||
agent-browser focus @e1 # 聚焦
|
||||
|
||||
# 表单操作
|
||||
agent-browser check @e1 # 勾选复选框
|
||||
agent-browser uncheck @e1 # 取消勾选
|
||||
agent-browser select @e1 "值" # 选择下拉选项
|
||||
agent-browser upload @e1 file1.jpg # 上传文件
|
||||
|
||||
# 滚动
|
||||
agent-browser scroll down 500 # 向下滚动 500px
|
||||
agent-browser scrollintoview @e1 # 滚动元素到可见区域
|
||||
```
|
||||
|
||||
### 获取信息
|
||||
```bash
|
||||
agent-browser get text @e1 # 获取元素文本
|
||||
agent-browser get value @e1 # 获取输入框值
|
||||
agent-browser get html @e1 # 获取 innerHTML
|
||||
agent-browser get attr @e1 id # 获取属性
|
||||
agent-browser get title # 获取页面标题
|
||||
agent-browser get url # 获取当前 URL
|
||||
agent-browser get count ".item" # 计数匹配元素
|
||||
agent-browser get box @e1 # 获取边界框
|
||||
```
|
||||
|
||||
### 检查状态
|
||||
```bash
|
||||
agent-browser is visible @e1 # 检查是否可见
|
||||
agent-browser is enabled @e1 # 检查是否启用
|
||||
agent-browser is checked @e1 # 检查是否勾选
|
||||
```
|
||||
|
||||
### 截图命令
|
||||
```bash
|
||||
agent-browser screenshot # 截图到标准输出
|
||||
agent-browser screenshot page.png # 保存到文件
|
||||
agent-browser screenshot --full # 完整页面截图
|
||||
```
|
||||
|
||||
### 等待命令
|
||||
```bash
|
||||
agent-browser wait @e1 # 等待元素可见
|
||||
agent-browser wait 2000 # 等待毫秒数
|
||||
agent-browser wait --text "成功" # 等待文本出现
|
||||
agent-browser wait --url "*/dashboard" # 等待 URL 模式匹配
|
||||
agent-browser wait --load networkidle # 等待网络空闲
|
||||
|
||||
# 加载状态选项:load, domcontentloaded, networkidle
|
||||
```
|
||||
|
||||
### 语义化定位器(替代 refs)
|
||||
```bash
|
||||
agent-browser find role button click --name "提交"
|
||||
agent-browser find text "登录" click
|
||||
agent-browser find label "邮箱" fill "user@test.com"
|
||||
agent-browser find placeholder "请输入" fill "内容"
|
||||
agent-browser find first ".item" click
|
||||
agent-browser find nth 2 "a" text
|
||||
```
|
||||
|
||||
## 认证与状态管理
|
||||
|
||||
### 方法1:手动登录流程(推荐用于首次)
|
||||
```bash
|
||||
# 导航到登录页面
|
||||
agent-browser open https://example.com/login
|
||||
|
||||
# 填写表单
|
||||
agent-browser snapshot -i
|
||||
# 输出显示:textbox "邮箱" [ref=e1], textbox "密码" [ref=e2], button "登录" [ref=e3]
|
||||
|
||||
agent-browser fill @e1 "user@example.com"
|
||||
agent-browser fill @e2 "password123"
|
||||
agent-browser click @e3
|
||||
|
||||
# 等待登录成功
|
||||
agent-browser wait --url "*/dashboard"
|
||||
|
||||
# 保存认证状态(重要!)
|
||||
agent-browser state save auth.json
|
||||
```
|
||||
|
||||
### 方法2:加载已保存状态(后续使用)
|
||||
```bash
|
||||
# 加载保存的认证状态
|
||||
agent-browser state load auth.json
|
||||
|
||||
# 直接访问受保护页面
|
||||
agent-browser open https://example.com/dashboard
|
||||
```
|
||||
|
||||
### 方法3:HTTP Headers 认证(跳过登录界面)
|
||||
```bash
|
||||
# 为特定域名设置认证头
|
||||
agent-browser open https://api.example.com --headers '{"Authorization": "Bearer <token>"}'
|
||||
```
|
||||
|
||||
### 方法4:Cookies 管理
|
||||
```bash
|
||||
# 设置 cookie
|
||||
agent-browser cookies set session_id "abc123"
|
||||
|
||||
# 查看所有 cookies
|
||||
agent-browser cookies
|
||||
```
|
||||
|
||||
## 高级功能
|
||||
|
||||
### 无头/有头模式
|
||||
```bash
|
||||
# 默认:无头模式(无界面)
|
||||
agent-browser open example.com
|
||||
|
||||
# 有头模式:显示浏览器窗口(适合调试)
|
||||
agent-browser --headed open example.com
|
||||
```
|
||||
|
||||
**无头模式特点**:
|
||||
- 后台运行 Chromium
|
||||
- 不占用屏幕空间
|
||||
- 更快的执行速度
|
||||
- 适合自动化测试和 AI Agent 使用
|
||||
|
||||
### 会话隔离
|
||||
```bash
|
||||
# 创建独立会话
|
||||
agent-browser --session user1 open site-a.com
|
||||
agent-browser --session user2 open site-b.com
|
||||
|
||||
# 查看活动会话
|
||||
agent-browser session list
|
||||
|
||||
# 每个会话独立:
|
||||
# - 浏览器实例
|
||||
# - Cookies 和存储
|
||||
# - 导航历史
|
||||
# - 认证状态
|
||||
```
|
||||
|
||||
### JSON 输出(适合机器解析)
|
||||
```bash
|
||||
agent-browser snapshot -i --json
|
||||
agent-browser get text @e1 --json
|
||||
agent-browser is visible @e2 --json
|
||||
```
|
||||
|
||||
### 自定义浏览器可执行文件
|
||||
```bash
|
||||
# 使用自定义 Chromium 路径
|
||||
agent-browser --executable-path /path/to/chromium open example.com
|
||||
|
||||
# 环境变量方式
|
||||
AGENT_BROWSER_EXECUTABLE_PATH=/path/to/chromium agent-browser open example.com
|
||||
```
|
||||
|
||||
### CDP 模式(连接现有浏览器)
|
||||
```bash
|
||||
# 连接通过 Chrome DevTools Protocol 运行的浏览器
|
||||
agent-browser --cdp 9222 snapshot
|
||||
|
||||
# 可控制:Electron 应用、启用远程调试的 Chrome、WebView2 等
|
||||
```
|
||||
|
||||
### 调试工具
|
||||
```bash
|
||||
agent-browser console # 查看控制台消息
|
||||
agent-browser errors # 查看页面错误
|
||||
agent-browser console --clear # 清空控制台
|
||||
agent-browser errors --clear # 清空错误
|
||||
agent-browser highlight @e1 # 高亮显示元素
|
||||
```
|
||||
|
||||
## 示例场景
|
||||
|
||||
### 示例1:表单提交
|
||||
```bash
|
||||
# 1. 导航到表单页面
|
||||
agent-browser open https://example.com/form
|
||||
|
||||
# 2. 获取交互元素快照
|
||||
agent-browser snapshot -i
|
||||
# 输出:textbox "邮箱" [ref=e1], textbox "密码" [ref=e2], button "提交" [ref=e3]
|
||||
|
||||
# 3. 填写并提交表单
|
||||
agent-browser fill @e1 "user@example.com"
|
||||
agent-browser fill @e2 "password123"
|
||||
agent-browser click @e3
|
||||
|
||||
# 4. 等待结果并验证
|
||||
agent-browser wait --load networkidle
|
||||
agent-browser snapshot -i # 检查结果
|
||||
```
|
||||
|
||||
### 示例2:带认证的完整流程
|
||||
```bash
|
||||
# 首次:登录并保存状态
|
||||
agent-browser open https://app.example.com/login
|
||||
agent-browser fill "#email" "username"
|
||||
agent-browser fill "#password" "password123"
|
||||
agent-browser click "#submit"
|
||||
agent-browser wait --url "**/dashboard"
|
||||
agent-browser state save app-auth.json
|
||||
|
||||
# 后续使用:加载状态
|
||||
agent-browser state load app-auth.json
|
||||
agent-browser open https://app.example.com/dashboard
|
||||
|
||||
# 执行操作
|
||||
agent-browser snapshot -i
|
||||
agent-browser click @e1
|
||||
```
|
||||
|
||||
### 示例3:数据提取
|
||||
```bash
|
||||
# 提取列表数据
|
||||
agent-browser open https://example.com/products
|
||||
agent-browser wait --load networkidle
|
||||
|
||||
# 获取所有产品标题
|
||||
for i in $(seq 1 10); do
|
||||
agent-browser get text "@e$i" --json
|
||||
done
|
||||
|
||||
# 或使用 CSS 选择器
|
||||
agent-browser get text ".product-title"
|
||||
```
|
||||
|
||||
### 示例4:并行测试
|
||||
```bash
|
||||
# 会话1:用户A
|
||||
agent-browser --session userA open site.com
|
||||
agent-browser --session userA fill "#email" "userA@test.com"
|
||||
agent-browser --session userA fill "#password" "passA123"
|
||||
|
||||
# 会话2:用户B(同时运行)
|
||||
agent-browser --session userB open site.com
|
||||
agent-browser --session userB fill "#email" "userB@test.com"
|
||||
agent-browser --session userB fill "#password" "passB123"
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 优先使用 Refs
|
||||
- 从 `snapshot -i` 获取 refs
|
||||
- 使用 `@e1`、`@e2` 而非 CSS 选择器
|
||||
- 页面变化后重新快照获取新 refs
|
||||
|
||||
### 2. 合理使用等待
|
||||
- 页面加载:`wait --load networkidle`
|
||||
- 元素出现:`wait @e1`
|
||||
- 文本出现:`wait --text "成功"`
|
||||
- 避免硬编码 sleep,使用条件等待
|
||||
|
||||
### 3. 状态管理策略
|
||||
- 首次登录后使用 `state save`
|
||||
- 后续使用 `state load` 避免重复登录
|
||||
- 敏感操作使用 `--session` 隔离
|
||||
|
||||
### 4. 错误处理
|
||||
- 检查元素是否存在:`is visible @e1`
|
||||
- 查看控制台日志:`console`
|
||||
- 调试时使用 `--headed` 模式
|
||||
|
||||
### 5. 性能优化
|
||||
- 使用 `-i` 参数限制快照输出
|
||||
- 批量操作使用循环
|
||||
- 合理使用等待避免超时
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
**1. "command not found: agent-browser" 错误**
|
||||
```bash
|
||||
# 错误原因:agent-browser 未安装
|
||||
# 解决方案:运行安装命令
|
||||
npm install -g agent-browser
|
||||
agent-browser install
|
||||
```
|
||||
|
||||
**2. "Unsupported token @e1" 错误**
|
||||
```bash
|
||||
# 错误原因:快照后页面已刷新,refs 失效
|
||||
# 解决方案:重新获取快照
|
||||
agent-browser snapshot -i
|
||||
agent-browser click @e1 # 使用新的 ref
|
||||
```
|
||||
|
||||
**3. 元素找不到**
|
||||
```bash
|
||||
# 检查元素是否存在
|
||||
agent-browser is visible @e1
|
||||
|
||||
# 等待元素出现
|
||||
agent-browser wait @e1
|
||||
|
||||
# 使用语义化查找
|
||||
agent-browser find role button click --name "提交"
|
||||
```
|
||||
|
||||
**4. 登录状态丢失**
|
||||
```bash
|
||||
# 重新登录并保存状态
|
||||
agent-browser state save auth.json
|
||||
|
||||
# 或设置 cookies
|
||||
agent-browser cookies set session_id "value"
|
||||
```
|
||||
|
||||
**5. 页面加载缓慢**
|
||||
```bash
|
||||
# 增加等待时间
|
||||
agent-browser wait --load networkidle
|
||||
agent-browser wait 5000
|
||||
|
||||
# 或设置超时
|
||||
agent-browser set timeout 30000
|
||||
```
|
||||
|
||||
## 平台支持
|
||||
|
||||
| 平台 | 原生二进制 | 回退方案 |
|
||||
|------|-----------|----------|
|
||||
| macOS ARM64 | ✓ | Node.js |
|
||||
| macOS x64 | ✓ | Node.js |
|
||||
| Linux ARM64 | ✓ | Node.js |
|
||||
| Linux x64 | ✓ | Node.js |
|
||||
| Windows x64 | ✓ | Node.js |
|
||||
|
||||
## 技术架构
|
||||
|
||||
agent-browser 采用客户端-守护进程架构:
|
||||
1. **Rust CLI**(原生二进制)- 解析命令,与守护进程通信
|
||||
2. **Node.js 守护进程** - 管理 Playwright 浏览器实例
|
||||
3. **回退方案** - 如果原生二进制不可用,直接使用 Node.js
|
||||
|
||||
守护进程在首次命令时自动启动,并在命令间持久化以实现快速后续操作。
|
||||
|
||||
**浏览器引擎**:默认使用 Chromium。守护进程也支持通过 Playwright 协议使用 Firefox 和 WebKit。
|
||||
|
||||
## 相关资源
|
||||
|
||||
- 官方仓库:https://github.com/vercel-labs/agent-browser
|
||||
- 官方文档:https://agent-browser.dev
|
||||
- npm 包:https://www.npmjs.com/package/agent-browser
|
||||
|
||||
## 许可证
|
||||
|
||||
Apache-2.0
|
||||
@@ -7,6 +7,13 @@ description: Git workflow best practices with commit conventions, tagging, and c
|
||||
|
||||
You are an expert in Git version control and repository management.
|
||||
|
||||
## 功能文档
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [Commit Workflow](./commit-workflow.md) | 提交暂存文件,自动生成提交信息并创建版本标签 |
|
||||
| [Push Workflow](./push-workflow.md) | 提交并推送到远程仓库的完整工作流 |
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. **Default Main Branch**: Use `main` as the primary branch (not `master`)
|
||||
|
||||
163
skill/git/commit-workflow.md
Normal file
163
skill/git/commit-workflow.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# Git Commit Workflow
|
||||
|
||||
提交暂存文件,自动生成提交信息并创建版本标签的完整工作流。
|
||||
|
||||
## 概述
|
||||
|
||||
此工作流用于:
|
||||
- 自动分析暂存区变更
|
||||
- 根据 Conventional Commits 规范生成提交信息
|
||||
- 检测项目类型并更新版本号
|
||||
- 创建语义化版本标签
|
||||
|
||||
## 执行步骤
|
||||
|
||||
### 步骤 1: 检查暂存区
|
||||
|
||||
```bash
|
||||
git diff --cached --name-only
|
||||
```
|
||||
|
||||
- 如果暂存区为空,通知用户并停止
|
||||
- 如果有文件,继续下一步
|
||||
|
||||
### 步骤 2: 收集信息(并行执行)
|
||||
|
||||
```bash
|
||||
# 并行执行以下命令
|
||||
git status
|
||||
git diff --cached
|
||||
git log --oneline -10
|
||||
git tag --list | sort -V | tail -5
|
||||
```
|
||||
|
||||
同时检查 `AGENTS.md` 文件获取:
|
||||
- 仓库类型(polyrepo/monorepo)
|
||||
- 版本规则
|
||||
- 项目结构信息
|
||||
|
||||
### 步骤 3: 检测仓库类型
|
||||
|
||||
**Polyrepo(单仓库)**
|
||||
- Tag 格式:`<version>`(如 `1.2.0`)
|
||||
|
||||
**Monorepo(多项目)**
|
||||
- Tag 格式:`<subproject>-<version>`(如 `ios-1.2.0`)
|
||||
- 检测特征:`packages/`、`apps/`、`services/` 目录
|
||||
|
||||
### 步骤 4: 检测项目类型和版本
|
||||
|
||||
| 项目类型 | 版本文件 | 版本字段 |
|
||||
|---------|---------|---------|
|
||||
| iOS | `*.xcodeproj/project.pbxproj` | `MARKETING_VERSION` |
|
||||
| Node.js | `package.json` | `version` |
|
||||
| Android (Groovy) | `app/build.gradle` | `versionName` |
|
||||
| Android (Kotlin) | `app/build.gradle.kts` | `versionName` |
|
||||
| Go | Git tag only | - |
|
||||
| Python | `pyproject.toml` / `setup.py` | `version` |
|
||||
| Rust | `Cargo.toml` | `version` |
|
||||
|
||||
### 步骤 5: 生成提交信息
|
||||
|
||||
遵循 Conventional Commits 格式:
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
```
|
||||
|
||||
**提交类型**:
|
||||
|
||||
| Type | 描述 | 版本影响 |
|
||||
|------|------|---------|
|
||||
| `feat` | 新功能 | minor +1 |
|
||||
| `fix` | Bug 修复 | patch +1 |
|
||||
| `perf` | 性能优化 | patch +1 |
|
||||
| `BREAKING CHANGE` | 破坏性变更 | major +1 |
|
||||
| `docs` | 文档更新 | 无 |
|
||||
| `style` | 代码格式 | 无 |
|
||||
| `refactor` | 重构 | 无 |
|
||||
| `test` | 测试 | 无 |
|
||||
| `chore` | 维护任务 | 无 |
|
||||
| `ci` | CI/CD 变更 | 无 |
|
||||
| `build` | 构建配置 | 无 |
|
||||
|
||||
**提交语言**:
|
||||
- macOS/Linux:使用中文
|
||||
- Windows:使用英文(避免编码问题)
|
||||
|
||||
**Monorepo Scope**:
|
||||
- 单项目变更:`feat(ios): 添加上传功能`
|
||||
- 多项目变更:`chore: 更新共享依赖`
|
||||
|
||||
### 步骤 6: 确定新版本号
|
||||
|
||||
根据提交类型计算新版本:
|
||||
|
||||
**版本递增规则**:
|
||||
- `feat`:minor +1(如 `1.2.0` → `1.3.0`)
|
||||
- `fix`/`perf`:patch +1(如 `1.2.3` → `1.2.4`)
|
||||
- Breaking change:major +1(如 `1.2.3` → `2.0.0`)
|
||||
|
||||
**不更新版本**:
|
||||
- `docs`、`test`、`refactor`、`style`、`build`、`ci`、`chore`
|
||||
|
||||
### 步骤 7: 更新版本文件并暂存
|
||||
|
||||
如果需要更新版本(步骤 6 确定了新版本号):
|
||||
|
||||
1. **更新版本文件**:将版本号写入对应的版本文件
|
||||
2. **添加到暂存区**:
|
||||
```bash
|
||||
git add <version-file>
|
||||
```
|
||||
3. **验证暂存**:
|
||||
```bash
|
||||
git diff --cached --name-only
|
||||
```
|
||||
确认版本文件已在暂存区
|
||||
|
||||
### 步骤 8: 执行提交
|
||||
|
||||
```bash
|
||||
git commit -m "<commit-message>"
|
||||
```
|
||||
|
||||
### 步骤 9: 创建版本标签
|
||||
|
||||
仅在版本更新时创建(除非用户指定 "skip tag"):
|
||||
|
||||
**Polyrepo**:
|
||||
```bash
|
||||
git tag -a "1.2.0" -m "<commit-message>"
|
||||
```
|
||||
|
||||
**Monorepo**:
|
||||
```bash
|
||||
git tag -a "ios-1.2.0" -m "<commit-message>"
|
||||
```
|
||||
|
||||
## 选项
|
||||
|
||||
- `skip tag` / `skip`:跳过标签创建
|
||||
|
||||
## 输出格式
|
||||
|
||||
```
|
||||
✓ 提交成功
|
||||
|
||||
提交信息:[commit message]
|
||||
版本标签:[tag](如果创建了)
|
||||
|
||||
要推送到远程仓库,请运行:/git-push
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 此命令**不会推送**到远程,使用 `/git-push` 推送
|
||||
- 暂存区为空时会提示用户先 `git add`
|
||||
- 标签注释使用与提交相同的消息内容
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Git Workflow Best Practices](./SKILL.md)
|
||||
- [Push Workflow](./push-workflow.md)
|
||||
107
skill/git/push-workflow.md
Normal file
107
skill/git/push-workflow.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Git Push Workflow
|
||||
|
||||
提交暂存文件,创建版本标签并推送到远程仓库的完整工作流。
|
||||
|
||||
## 概述
|
||||
|
||||
此工作流是 **All-in-One** 命令,组合了:
|
||||
- `/git-commit` 的所有功能
|
||||
- 推送提交到远程
|
||||
- 推送标签到远程
|
||||
|
||||
## 执行步骤
|
||||
|
||||
### 步骤 1-9: 与 Commit Workflow 相同
|
||||
|
||||
参考 [Commit Workflow](./commit-workflow.md):
|
||||
|
||||
1. 检查暂存区(不能为空)
|
||||
2. 收集变更信息和仓库状态
|
||||
3. 检测仓库类型(polyrepo/monorepo)
|
||||
4. 检测项目类型和版本
|
||||
5. 生成提交信息(Conventional Commits,中文)
|
||||
6. 确定新版本号
|
||||
7. 更新版本文件并添加到暂存区
|
||||
8. 执行提交
|
||||
9. 创建版本标签
|
||||
|
||||
### 步骤 10: 推送提交到远程
|
||||
|
||||
```bash
|
||||
git push origin $(git branch --show-current)
|
||||
```
|
||||
|
||||
### 步骤 11: 推送标签到远程
|
||||
|
||||
仅在创建了标签时执行:
|
||||
|
||||
**Polyrepo**:
|
||||
```bash
|
||||
git push origin <version>
|
||||
```
|
||||
|
||||
**Monorepo**:
|
||||
```bash
|
||||
git push origin <subproject>-<version>
|
||||
```
|
||||
|
||||
## 选项
|
||||
|
||||
- `skip tag` / `skip`:跳过标签创建
|
||||
|
||||
## 输出格式
|
||||
|
||||
```
|
||||
✓ 提交并推送成功
|
||||
|
||||
分支:[branch]
|
||||
提交信息:[commit message]
|
||||
版本标签:[tag](如果创建了)
|
||||
|
||||
已推送到远程仓库:origin
|
||||
- 提交:[commit hash]
|
||||
- 标签:[tag]
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
### 暂存区为空
|
||||
|
||||
```
|
||||
暂存区为空,请先使用 `git add` 添加文件。
|
||||
```
|
||||
|
||||
### 推送失败
|
||||
|
||||
```
|
||||
❌ 推送失败:[error message]
|
||||
|
||||
可能的解决方案:
|
||||
1. 先拉取远程变更:git pull origin <branch>
|
||||
2. 检查网络连接
|
||||
3. 检查远程仓库权限
|
||||
```
|
||||
|
||||
### 标签已存在
|
||||
|
||||
```
|
||||
❌ 标签推送失败:tag already exists
|
||||
|
||||
解决方案:
|
||||
1. 删除本地标签:git tag -d <tag>
|
||||
2. 更新版本号后重新提交
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
| 场景 | 推荐命令 |
|
||||
|------|---------|
|
||||
| 本地提交,稍后审查 | `/git-commit` |
|
||||
| 提交并立即推送 | `/git-push` |
|
||||
| 仅推送已有提交 | `git push origin <branch>` |
|
||||
| 仅推送标签 | `git push origin <tag>` |
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Git Workflow Best Practices](./SKILL.md)
|
||||
- [Commit Workflow](./commit-workflow.md)
|
||||
@@ -1,663 +0,0 @@
|
||||
# Git Quick Reference
|
||||
|
||||
Quick reference guide for common Git operations.
|
||||
|
||||
## File Changes and Status
|
||||
|
||||
### View Changed Files
|
||||
|
||||
```bash
|
||||
# Show working directory status
|
||||
git status
|
||||
|
||||
# Show short status
|
||||
git status -s
|
||||
|
||||
# List changed files only (unstaged)
|
||||
git diff --name-only
|
||||
|
||||
# List changed files only (staged)
|
||||
git diff --cached --name-only
|
||||
# or
|
||||
git diff --staged --name-only
|
||||
|
||||
# Show file change statistics
|
||||
git diff --stat
|
||||
git diff --cached --stat
|
||||
```
|
||||
|
||||
### View Detailed Changes
|
||||
|
||||
```bash
|
||||
# View unstaged changes
|
||||
git diff
|
||||
|
||||
# View staged changes
|
||||
git diff --cached
|
||||
# or
|
||||
git diff --staged
|
||||
|
||||
# View specific file changes
|
||||
git diff <file-path>
|
||||
git diff --cached <file-path>
|
||||
|
||||
# View changes between commits
|
||||
git diff <commit1>..<commit2>
|
||||
git diff HEAD~1..HEAD
|
||||
|
||||
# View changes between branches
|
||||
git diff main..feature-branch
|
||||
```
|
||||
|
||||
## Staging and Committing
|
||||
|
||||
### Add Files to Staging
|
||||
|
||||
```bash
|
||||
# Add specific file
|
||||
git add <file-path>
|
||||
|
||||
# Add all files in directory
|
||||
git add .
|
||||
|
||||
# Add all files in repository
|
||||
git add -A
|
||||
|
||||
# Add files interactively
|
||||
git add -p
|
||||
|
||||
# Add only modified files (not new files)
|
||||
git add -u
|
||||
```
|
||||
|
||||
### Check Staging Area
|
||||
|
||||
```bash
|
||||
# List files in staging area
|
||||
git diff --cached --name-only
|
||||
|
||||
# Show detailed staged changes
|
||||
git diff --cached
|
||||
```
|
||||
|
||||
### Commit Changes
|
||||
|
||||
```bash
|
||||
# Simple commit
|
||||
git commit -m "feat: add user authentication"
|
||||
|
||||
# Multi-line commit (macOS/Linux)
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat: add user authentication
|
||||
|
||||
- Add OAuth2 support
|
||||
- Implement JWT tokens
|
||||
- Add login/logout endpoints
|
||||
EOF
|
||||
)"
|
||||
|
||||
# Multi-line commit (Windows)
|
||||
git commit -m "feat: add user authentication" \
|
||||
-m "" \
|
||||
-m "- Add OAuth2 support" \
|
||||
-m "- Implement JWT tokens" \
|
||||
-m "- Add login/logout endpoints"
|
||||
|
||||
# Commit with automatic staging
|
||||
git commit -am "fix: resolve issue"
|
||||
|
||||
# Amend last commit (before push only!)
|
||||
git commit --amend -m "new message"
|
||||
```
|
||||
|
||||
## Tag Management
|
||||
|
||||
### Create Tags
|
||||
|
||||
```bash
|
||||
# Create annotated tag
|
||||
git tag -a "1.2.0" -m "feat: add new feature"
|
||||
|
||||
# Create lightweight tag
|
||||
git tag "1.2.0"
|
||||
|
||||
# Create tag with multi-line message
|
||||
git tag -a "1.2.1" \
|
||||
-m "fix: resolve connection issue" \
|
||||
-m "" \
|
||||
-m "- Increase timeout to 30s" \
|
||||
-m "- Add retry mechanism"
|
||||
|
||||
# Create tag for specific commit
|
||||
git tag -a "1.2.0" <commit-hash> -m "message"
|
||||
|
||||
# Monorepo tag
|
||||
git tag -a "ios-1.2.0" -m "feat(ios): add feature"
|
||||
```
|
||||
|
||||
### List Tags
|
||||
|
||||
```bash
|
||||
# List all tags
|
||||
git tag
|
||||
|
||||
# List tags with pattern
|
||||
git tag -l "v1.*"
|
||||
|
||||
# List recent tags (sorted)
|
||||
git tag --list | sort -V | tail -5
|
||||
|
||||
# Show tag details
|
||||
git show <tag-name>
|
||||
```
|
||||
|
||||
### Push Tags
|
||||
|
||||
```bash
|
||||
# Push single tag
|
||||
git push origin <tag-name>
|
||||
|
||||
# Push all tags
|
||||
git push --tags
|
||||
# or
|
||||
git push origin --tags
|
||||
|
||||
# Push commit and tag together
|
||||
git push origin main && git push origin 1.2.0
|
||||
```
|
||||
|
||||
### Delete Tags
|
||||
|
||||
```bash
|
||||
# Delete local tag
|
||||
git tag -d <tag-name>
|
||||
|
||||
# Delete remote tag
|
||||
git push origin --delete <tag-name>
|
||||
# or
|
||||
git push origin :refs/tags/<tag-name>
|
||||
|
||||
# Delete multiple tags
|
||||
git tag -d tag1 tag2 tag3
|
||||
```
|
||||
|
||||
## Branch Operations
|
||||
|
||||
### View Branches
|
||||
|
||||
```bash
|
||||
# Show current branch
|
||||
git branch --show-current
|
||||
|
||||
# List local branches
|
||||
git branch
|
||||
|
||||
# List all branches (local + remote)
|
||||
git branch -a
|
||||
|
||||
# List remote branches only
|
||||
git branch -r
|
||||
|
||||
# Show branch with last commit
|
||||
git branch -v
|
||||
```
|
||||
|
||||
### Create and Switch Branches
|
||||
|
||||
```bash
|
||||
# Create new branch
|
||||
git branch <branch-name>
|
||||
|
||||
# Create and switch to new branch (old way)
|
||||
git checkout -b <branch-name>
|
||||
|
||||
# Create and switch to new branch (modern)
|
||||
git switch -c <branch-name>
|
||||
|
||||
# Switch to existing branch (old way)
|
||||
git checkout <branch-name>
|
||||
|
||||
# Switch to existing branch (modern)
|
||||
git switch <branch-name>
|
||||
|
||||
# Switch to previous branch
|
||||
git switch -
|
||||
```
|
||||
|
||||
### Delete Branches
|
||||
|
||||
```bash
|
||||
# Delete local branch (safe)
|
||||
git branch -d <branch-name>
|
||||
|
||||
# Delete local branch (force)
|
||||
git branch -D <branch-name>
|
||||
|
||||
# Delete remote branch
|
||||
git push origin --delete <branch-name>
|
||||
# or
|
||||
git push origin :<branch-name>
|
||||
```
|
||||
|
||||
## Pushing and Pulling
|
||||
|
||||
### Push Changes
|
||||
|
||||
```bash
|
||||
# Push current branch
|
||||
git push
|
||||
|
||||
# Push to specific remote and branch
|
||||
git push origin main
|
||||
|
||||
# Push current branch to remote
|
||||
git push origin $(git branch --show-current)
|
||||
|
||||
# Push with upstream tracking
|
||||
git push -u origin <branch-name>
|
||||
|
||||
# Push all branches
|
||||
git push --all
|
||||
|
||||
# Push all tags
|
||||
git push --tags
|
||||
|
||||
# Force push (dangerous!)
|
||||
git push --force
|
||||
# Better: force push with lease
|
||||
git push --force-with-lease
|
||||
```
|
||||
|
||||
### Pull Changes
|
||||
|
||||
```bash
|
||||
# Pull from tracked remote
|
||||
git pull
|
||||
|
||||
# Pull from specific remote and branch
|
||||
git pull origin main
|
||||
|
||||
# Pull with rebase
|
||||
git pull --rebase
|
||||
|
||||
# Pull and prune deleted remote branches
|
||||
git pull --prune
|
||||
```
|
||||
|
||||
### Fetch Changes
|
||||
|
||||
```bash
|
||||
# Fetch from all remotes
|
||||
git fetch
|
||||
|
||||
# Fetch from specific remote
|
||||
git fetch origin
|
||||
|
||||
# Fetch and prune deleted remote branches
|
||||
git fetch --prune
|
||||
|
||||
# Fetch all branches and tags
|
||||
git fetch --all --tags
|
||||
```
|
||||
|
||||
## History and Logs
|
||||
|
||||
### View Commit History
|
||||
|
||||
```bash
|
||||
# View recent commits
|
||||
git log
|
||||
|
||||
# View compact history
|
||||
git log --oneline
|
||||
|
||||
# View recent 10 commits
|
||||
git log --oneline -10
|
||||
|
||||
# View history with graph
|
||||
git log --graph --oneline --all
|
||||
|
||||
# View history with stats
|
||||
git log --stat
|
||||
|
||||
# View history with patches
|
||||
git log -p
|
||||
```
|
||||
|
||||
### Search History
|
||||
|
||||
```bash
|
||||
# Search commits by message
|
||||
git log --grep="feature"
|
||||
|
||||
# Search by author
|
||||
git log --author="John"
|
||||
|
||||
# Search by date
|
||||
git log --since="2024-01-01"
|
||||
git log --after="2 weeks ago"
|
||||
git log --before="yesterday"
|
||||
|
||||
# Search by file
|
||||
git log -- <file-path>
|
||||
|
||||
# Search code changes
|
||||
git log -S "function_name"
|
||||
```
|
||||
|
||||
### View Commit Details
|
||||
|
||||
```bash
|
||||
# Show specific commit
|
||||
git show <commit-hash>
|
||||
|
||||
# Show specific tag
|
||||
git show <tag-name>
|
||||
|
||||
# Show HEAD commit
|
||||
git show HEAD
|
||||
|
||||
# Show previous commit
|
||||
git show HEAD~1
|
||||
git show HEAD^
|
||||
```
|
||||
|
||||
## Undoing Changes
|
||||
|
||||
### Discard Changes
|
||||
|
||||
```bash
|
||||
# Discard unstaged changes in file
|
||||
git checkout -- <file-path>
|
||||
# or (modern)
|
||||
git restore <file-path>
|
||||
|
||||
# Discard all unstaged changes
|
||||
git checkout -- .
|
||||
# or (modern)
|
||||
git restore .
|
||||
|
||||
# Unstage file (keep changes)
|
||||
git reset HEAD <file-path>
|
||||
# or (modern)
|
||||
git restore --staged <file-path>
|
||||
|
||||
# Unstage all files
|
||||
git reset HEAD
|
||||
# or (modern)
|
||||
git restore --staged .
|
||||
```
|
||||
|
||||
### Reset Commits
|
||||
|
||||
```bash
|
||||
# Undo last commit, keep changes staged
|
||||
git reset --soft HEAD~1
|
||||
|
||||
# Undo last commit, keep changes unstaged
|
||||
git reset HEAD~1
|
||||
# or
|
||||
git reset --mixed HEAD~1
|
||||
|
||||
# Undo last commit, discard changes (dangerous!)
|
||||
git reset --hard HEAD~1
|
||||
|
||||
# Reset to specific commit
|
||||
git reset --hard <commit-hash>
|
||||
```
|
||||
|
||||
### Revert Commits
|
||||
|
||||
```bash
|
||||
# Create new commit that undoes a commit
|
||||
git revert <commit-hash>
|
||||
|
||||
# Revert without committing
|
||||
git revert -n <commit-hash>
|
||||
|
||||
# Revert multiple commits
|
||||
git revert <commit1>..<commit2>
|
||||
```
|
||||
|
||||
## Stash Operations
|
||||
|
||||
### Save Changes
|
||||
|
||||
```bash
|
||||
# Stash current changes
|
||||
git stash
|
||||
|
||||
# Stash with message
|
||||
git stash save "work in progress"
|
||||
|
||||
# Stash including untracked files
|
||||
git stash -u
|
||||
|
||||
# Stash including untracked and ignored files
|
||||
git stash -a
|
||||
```
|
||||
|
||||
### Apply Stash
|
||||
|
||||
```bash
|
||||
# Apply most recent stash
|
||||
git stash apply
|
||||
|
||||
# Apply and remove from stash list
|
||||
git stash pop
|
||||
|
||||
# Apply specific stash
|
||||
git stash apply stash@{2}
|
||||
```
|
||||
|
||||
### Manage Stash
|
||||
|
||||
```bash
|
||||
# List all stashes
|
||||
git stash list
|
||||
|
||||
# Show stash changes
|
||||
git stash show
|
||||
git stash show -p
|
||||
|
||||
# Drop specific stash
|
||||
git stash drop stash@{1}
|
||||
|
||||
# Clear all stashes
|
||||
git stash clear
|
||||
```
|
||||
|
||||
## Remote Operations
|
||||
|
||||
### View Remotes
|
||||
|
||||
```bash
|
||||
# List remotes
|
||||
git remote
|
||||
|
||||
# List remotes with URLs
|
||||
git remote -v
|
||||
|
||||
# Show remote details
|
||||
git remote show origin
|
||||
```
|
||||
|
||||
### Manage Remotes
|
||||
|
||||
```bash
|
||||
# Add remote
|
||||
git remote add <name> <url>
|
||||
|
||||
# Remove remote
|
||||
git remote remove <name>
|
||||
|
||||
# Rename remote
|
||||
git remote rename <old-name> <new-name>
|
||||
|
||||
# Change remote URL
|
||||
git remote set-url <name> <new-url>
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### View Configuration
|
||||
|
||||
```bash
|
||||
# View all config
|
||||
git config --list
|
||||
|
||||
# View global config
|
||||
git config --global --list
|
||||
|
||||
# View local config
|
||||
git config --local --list
|
||||
|
||||
# View specific config
|
||||
git config user.name
|
||||
git config user.email
|
||||
```
|
||||
|
||||
### Set Configuration
|
||||
|
||||
```bash
|
||||
# Set user name
|
||||
git config --global user.name "Your Name"
|
||||
|
||||
# Set user email
|
||||
git config --global user.email "your.email@example.com"
|
||||
|
||||
# Set default branch name
|
||||
git config --global init.defaultBranch main
|
||||
|
||||
# Set default editor
|
||||
git config --global core.editor "code --wait"
|
||||
|
||||
# Set credential helper
|
||||
git config --global credential.helper store
|
||||
```
|
||||
|
||||
## Workflow Examples
|
||||
|
||||
### Standard Commit and Tag Workflow
|
||||
|
||||
```bash
|
||||
# 1. Check status
|
||||
git status
|
||||
git diff --cached --name-only
|
||||
|
||||
# 2. Stage changes
|
||||
git add .
|
||||
|
||||
# 3. Commit
|
||||
git commit -m "feat: add user authentication"
|
||||
|
||||
# 4. Create tag
|
||||
git tag -a "1.2.0" -m "feat: add user authentication"
|
||||
|
||||
# 5. Push commit and tag
|
||||
git push origin main
|
||||
git push origin 1.2.0
|
||||
```
|
||||
|
||||
### Complete Staging to Push Workflow
|
||||
|
||||
```bash
|
||||
# Check what files changed
|
||||
git status
|
||||
|
||||
# View changes
|
||||
git diff
|
||||
|
||||
# Stage specific files
|
||||
git add src/auth.js src/api.js
|
||||
|
||||
# Verify staging
|
||||
git diff --cached --name-only
|
||||
|
||||
# Commit with message
|
||||
git commit -m "feat: implement OAuth2 authentication"
|
||||
|
||||
# Push to remote
|
||||
git push origin main
|
||||
```
|
||||
|
||||
### Push All Tags Workflow
|
||||
|
||||
```bash
|
||||
# List local tags
|
||||
git tag
|
||||
|
||||
# View recent tags
|
||||
git tag --list | sort -V | tail -5
|
||||
|
||||
# Push all tags to remote
|
||||
git push --tags
|
||||
|
||||
# Verify tags on remote
|
||||
git ls-remote --tags origin
|
||||
```
|
||||
|
||||
### Quick Status Check
|
||||
|
||||
```bash
|
||||
# Full status
|
||||
git status
|
||||
|
||||
# Changed files only
|
||||
git diff --name-only
|
||||
git diff --cached --name-only
|
||||
|
||||
# Recent commits and tags
|
||||
git log --oneline -5
|
||||
git tag --list | sort -V | tail -5
|
||||
|
||||
# Current branch
|
||||
git branch --show-current
|
||||
```
|
||||
|
||||
## Tips and Tricks
|
||||
|
||||
### Aliases
|
||||
|
||||
Add to `~/.gitconfig`:
|
||||
|
||||
```ini
|
||||
[alias]
|
||||
st = status
|
||||
co = checkout
|
||||
br = branch
|
||||
ci = commit
|
||||
unstage = restore --staged
|
||||
last = log -1 HEAD
|
||||
lg = log --graph --oneline --all
|
||||
tags = tag -l --sort=-v:refname
|
||||
```
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
git st
|
||||
git co main
|
||||
git lg
|
||||
```
|
||||
|
||||
### Useful One-Liners
|
||||
|
||||
```bash
|
||||
# Delete all merged branches
|
||||
git branch --merged | grep -v "\*" | xargs -n 1 git branch -d
|
||||
|
||||
# View file in specific commit
|
||||
git show <commit>:<file-path>
|
||||
|
||||
# Count commits by author
|
||||
git shortlog -sn
|
||||
|
||||
# Find when a line was changed
|
||||
git blame <file-path>
|
||||
|
||||
# Show what changed in each commit for a file
|
||||
git log -p <file-path>
|
||||
|
||||
# List files in a commit
|
||||
git diff-tree --no-commit-id --name-only -r <commit>
|
||||
```
|
||||
@@ -58,6 +58,7 @@ C:\Users\YourUsername\.config\gitea\
|
||||
|---------|------|------|
|
||||
| 环境配置 | [setup-guide.md](./setup-guide.md) | 首次使用引导,配置 Gitea URL 和 Token |
|
||||
| Runner 管理 | [runner-management.md](./runner-management.md) | 创建、注册、管理 Gitea Act Runner |
|
||||
| 自动创建脚本 | [create-runner.md](./create-runner.md) | 包含完整的 Runner 创建 Bash 脚本 |
|
||||
| Workflow 生成 | [workflow-generator.md](./workflow-generator.md) | 根据项目类型生成 CI/CD workflow |
|
||||
| 仓库操作 | [repository-operations.md](./repository-operations.md) | 创建和配置 Gitea 仓库 |
|
||||
| API 参考 | [api-reference.md](./api-reference.md) | Gitea API 常用接口 |
|
||||
|
||||
416
skill/gitea/create-runner.md
Normal file
416
skill/gitea/create-runner.md
Normal file
@@ -0,0 +1,416 @@
|
||||
---
|
||||
description: 创建并启动 Gitea Actions Runner 的完整脚本
|
||||
agent: general
|
||||
---
|
||||
|
||||
# Create Runner Script
|
||||
|
||||
本文档包含用于创建 Gitea Actions Runner 的完整 Bash 脚本。该脚本支持 Host 模式和 Docker 模式。
|
||||
|
||||
## 脚本文件
|
||||
|
||||
你可以将以下内容保存为 `create_runner.sh` 并赋予执行权限 (`chmod +x create_runner.sh`)。
|
||||
|
||||
```bash
|
||||
#!/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 ""
|
||||
```
|
||||
303
skill/gitea/delete-runner.md
Normal file
303
skill/gitea/delete-runner.md
Normal file
@@ -0,0 +1,303 @@
|
||||
---
|
||||
description: 交互式批量删除 Gitea Runners 脚本
|
||||
agent: general
|
||||
---
|
||||
|
||||
# Delete Runner Script
|
||||
|
||||
本文档包含用于交互式批量删除 Gitea Actions Runner 的完整 Bash 脚本。
|
||||
|
||||
## 功能特点
|
||||
- **多选支持**:支持输入多个序号(如 `1,3` 或 `1 3`)或 `all` 进行批量删除。
|
||||
- **双重清理**:同时从 Gitea 服务器注销 Runner 和删除本地配置/容器。
|
||||
- **智能识别**:自动关联远程 Runner 状态与本地 Runner 目录。
|
||||
- **安全检查**:删除前强制二次确认,防止误删。
|
||||
|
||||
## 脚本文件
|
||||
|
||||
你可以将以下内容保存为 `delete_runner.sh` 并赋予执行权限 (`chmod +x delete_runner.sh`)。
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Gitea Runner Deletion Script
|
||||
# Generated by OpenCode Skill
|
||||
|
||||
set -e
|
||||
|
||||
# ==========================================
|
||||
# 1. Setup & Config
|
||||
# ==========================================
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Config
|
||||
CONFIG_FILE="$HOME/.config/gitea/config.env"
|
||||
RUNNERS_BASE_DIR="$HOME/.config/gitea/runners"
|
||||
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo -e "${RED}❌ 配置文件不存在: $CONFIG_FILE${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
if [ -z "$GITEA_URL" ] || [ -z "$GITEA_TOKEN" ]; then
|
||||
echo -e "${RED}❌ 配置无效: 缺少 URL 或 Token${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check requirements
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo -e "${RED}❌ 需要安装 jq 工具来解析 JSON${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "正在获取 Runner 列表..."
|
||||
|
||||
# ==========================================
|
||||
# 2. Data Collection
|
||||
# ==========================================
|
||||
|
||||
# Temporary files
|
||||
REMOTE_LIST=$(mktemp)
|
||||
LOCAL_MAP=$(mktemp)
|
||||
FINAL_LIST=$(mktemp)
|
||||
|
||||
# 2.1 Fetch Remote Runners (Try Admin first, then Org)
|
||||
# Note: Admin endpoint /api/v1/admin/runners lists all runners
|
||||
HTTP_CODE=$(curl -s -w "%{http_code}" -o "$REMOTE_LIST" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
"${GITEA_URL}/api/v1/admin/runners?page=1&limit=100")
|
||||
|
||||
if [ "$HTTP_CODE" != "200" ]; then
|
||||
# Fallback to Org level if defined
|
||||
if [ -n "$GITEA_DEFAULT_ORG" ]; then
|
||||
HTTP_CODE=$(curl -s -w "%{http_code}" -o "$REMOTE_LIST" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
"${GITEA_URL}/api/v1/orgs/${GITEA_DEFAULT_ORG}/actions/runners?page=1&limit=100")
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$HTTP_CODE" != "200" ]; then
|
||||
echo -e "${RED}❌ 无法获取 Runner 列表 (HTTP $HTTP_CODE)${NC}"
|
||||
cat "$REMOTE_LIST"
|
||||
rm "$REMOTE_LIST" "$LOCAL_MAP" "$FINAL_LIST"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2.2 Scan Local Directories to map UUID -> Path
|
||||
# We need to find which local directory corresponds to which runner ID/UUID
|
||||
echo "{}" > "$LOCAL_MAP"
|
||||
|
||||
if [ -d "$RUNNERS_BASE_DIR" ]; then
|
||||
for d in "$RUNNERS_BASE_DIR"/*; do
|
||||
if [ -d "$d" ]; then
|
||||
# Check Host mode .runner
|
||||
if [ -f "$d/.runner" ]; then
|
||||
uuid=$(jq -r '.uuid' "$d/.runner" 2>/dev/null)
|
||||
if [ -n "$uuid" ] && [ "$uuid" != "null" ]; then
|
||||
# Add to JSON map
|
||||
tmp=$(mktemp)
|
||||
jq --arg uuid "$uuid" --arg path "$d" '.[$uuid] = $path' "$LOCAL_MAP" > "$tmp" && mv "$tmp" "$LOCAL_MAP"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check Docker mode data/.runner
|
||||
if [ -f "$d/data/.runner" ]; then
|
||||
uuid=$(jq -r '.uuid' "$d/data/.runner" 2>/dev/null)
|
||||
if [ -n "$uuid" ] && [ "$uuid" != "null" ]; then
|
||||
tmp=$(mktemp)
|
||||
jq --arg uuid "$uuid" --arg path "$d" '.[$uuid] = $path' "$LOCAL_MAP" > "$tmp" && mv "$tmp" "$LOCAL_MAP"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 3. Display Interface
|
||||
# ==========================================
|
||||
|
||||
# Combine Remote and Local info
|
||||
# Output format: index | id | name | status | local_path
|
||||
jq -r --slurpfile local "$LOCAL_MAP" '
|
||||
.runners[] |
|
||||
[.id, .uuid, .name, .status, ($local[0][.uuid] // "")] |
|
||||
@tsv
|
||||
' "$REMOTE_LIST" > "$FINAL_LIST"
|
||||
|
||||
count=$(wc -l < "$FINAL_LIST" | tr -d ' ')
|
||||
|
||||
if [ "$count" -eq 0 ]; then
|
||||
echo "没有发现任何 Runners。"
|
||||
rm "$REMOTE_LIST" "$LOCAL_MAP" "$FINAL_LIST"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}Gitea Runners 列表 (共 $count 个)${NC}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
printf "%-4s | %-8s | %-20s | %-10s | %-30s\n" "序号" "ID" "名称" "状态" "本地目录"
|
||||
echo "----------------------------------------------------------------------------"
|
||||
|
||||
i=1
|
||||
declare -a runner_ids
|
||||
declare -a runner_names
|
||||
declare -a runner_paths
|
||||
|
||||
while IFS=$'\t' read -r id uuid name status local_path; do
|
||||
status_icon="🔴"
|
||||
if [ "$status" = "online" ] || [ "$status" = "idle" ] || [ "$status" = "active" ]; then
|
||||
status_icon="🟢"
|
||||
fi
|
||||
|
||||
local_mark=""
|
||||
if [ -n "$local_path" ]; then
|
||||
local_mark="$(basename "$local_path")"
|
||||
else
|
||||
local_mark="-"
|
||||
fi
|
||||
|
||||
printf "%-4d | %-8s | %-20s | %s %-8s | %-30s\n" "$i" "$id" "${name:0:20}" "$status_icon" "$status" "$local_mark"
|
||||
|
||||
runner_ids[$i]=$id
|
||||
runner_names[$i]=$name
|
||||
runner_paths[$i]=$local_path
|
||||
|
||||
i=$((i+1))
|
||||
done < "$FINAL_LIST"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# ==========================================
|
||||
# 4. User Selection
|
||||
# ==========================================
|
||||
|
||||
echo "请输入要删除的序号:"
|
||||
echo " - 单个: 1"
|
||||
echo " - 多选: 1,3,5 或 1 3 5"
|
||||
echo " - 全部: all"
|
||||
echo " - 退出: q"
|
||||
echo ""
|
||||
read -p "选择 > " selection
|
||||
|
||||
if [[ "$selection" =~ ^[qQ] ]]; then
|
||||
echo "已取消。"
|
||||
rm "$REMOTE_LIST" "$LOCAL_MAP" "$FINAL_LIST"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
target_indices=()
|
||||
|
||||
if [ "$selection" = "all" ]; then
|
||||
for ((j=1; j<=count; j++)); do
|
||||
target_indices+=($j)
|
||||
done
|
||||
else
|
||||
# Replace commas with spaces and iterate
|
||||
for idx in ${selection//,/ }; do
|
||||
# Validate number
|
||||
if [[ "$idx" =~ ^[0-9]+$ ]] && [ "$idx" -ge 1 ] && [ "$idx" -le "$count" ]; then
|
||||
target_indices+=($idx)
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ 忽略无效序号: $idx${NC}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ ${#target_indices[@]} -eq 0 ]; then
|
||||
echo -e "${RED}未选择任何有效 Runner。${NC}"
|
||||
rm "$REMOTE_LIST" "$LOCAL_MAP" "$FINAL_LIST"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 5. Confirmation
|
||||
# ==========================================
|
||||
|
||||
echo ""
|
||||
echo -e "${RED}⚠️ 警告: 即将删除以下 ${#target_indices[@]} 个 Runner:${NC}"
|
||||
for idx in "${target_indices[@]}"; do
|
||||
echo " - [${runner_ids[$idx]}] ${runner_names[$idx]}"
|
||||
if [ -n "${runner_paths[$idx]}" ]; then
|
||||
echo " └─ 本地目录: ${runner_paths[$idx]}"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
echo "此操作将从服务器注销 Runner 并删除本地文件/容器。"
|
||||
read -p "确认删除? (输入 yes 继续): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "操作已取消。"
|
||||
rm "$REMOTE_LIST" "$LOCAL_MAP" "$FINAL_LIST"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# 6. Execution
|
||||
# ==========================================
|
||||
|
||||
echo ""
|
||||
echo "开始执行删除..."
|
||||
|
||||
for idx in "${target_indices[@]}"; do
|
||||
r_id="${runner_ids[$idx]}"
|
||||
r_name="${runner_names[$idx]}"
|
||||
r_path="${runner_paths[$idx]}"
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "正在处理: $r_name (ID: $r_id)"
|
||||
|
||||
# 6.1 Delete from Server
|
||||
echo -n " 1. 从服务器注销... "
|
||||
del_code=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
"${GITEA_URL}/api/v1/admin/actions/runners/${r_id}")
|
||||
|
||||
if [ "$del_code" = "204" ] || [ "$del_code" = "404" ]; then
|
||||
echo -e "${GREEN}成功${NC}"
|
||||
else
|
||||
echo -e "${RED}失败 (HTTP $del_code)${NC}"
|
||||
# Continue cleanup anyway
|
||||
fi
|
||||
|
||||
# 6.2 Cleanup Local
|
||||
if [ -n "$r_path" ] && [ -d "$r_path" ]; then
|
||||
dir_name=$(basename "$r_path")
|
||||
|
||||
# Stop Docker container if name matches folder name (common convention)
|
||||
if docker ps -a --format '{{.Names}}' | grep -q "^${dir_name}$"; then
|
||||
echo -n " 2. 停止并删除 Docker 容器 ($dir_name)... "
|
||||
docker rm -f "$dir_name" >/dev/null 2>&1
|
||||
echo -e "${GREEN}完成${NC}"
|
||||
fi
|
||||
|
||||
# Stop Host process (if PID file exists)
|
||||
if [ -f "$r_path/pid" ]; then
|
||||
pid=$(cat "$r_path/pid")
|
||||
echo -n " 2. 停止本地进程 (PID: $pid)... "
|
||||
kill "$pid" >/dev/null 2>&1 || true
|
||||
echo -e "${GREEN}完成${NC}"
|
||||
fi
|
||||
|
||||
echo -n " 3. 删除本地目录... "
|
||||
rm -rf "$r_path"
|
||||
echo -e "${GREEN}完成${NC}"
|
||||
else
|
||||
echo " - 本地目录未找到或已清理"
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo -e "${GREEN}✅ 批量删除操作完成${NC}"
|
||||
|
||||
# Cleanup temps
|
||||
rm "$REMOTE_LIST" "$LOCAL_MAP" "$FINAL_LIST"
|
||||
```
|
||||
@@ -267,6 +267,8 @@ act_runner --version
|
||||
7. 获取注册 token(优先全局)
|
||||
8. 注册并启动 runner
|
||||
|
||||
> **提示**:你可以从 [create-runner.md](./create-runner.md) 获取完整的 Bash 脚本。
|
||||
|
||||
### 详细创建流程
|
||||
|
||||
当运行 `/gitea-create-runner` 命令时,会执行以下步骤:
|
||||
@@ -640,23 +642,58 @@ log:
|
||||
level: info
|
||||
|
||||
runner:
|
||||
file: /path/to/.runner
|
||||
file: /data/.runner # 容器内路径
|
||||
capacity: 2
|
||||
timeout: 3h
|
||||
labels:
|
||||
- "ubuntu-latest:docker://catthehacker/ubuntu:act-latest"
|
||||
shutdown_timeout: 30s
|
||||
insecure: false
|
||||
fetch_timeout: 5s
|
||||
fetch_interval: 2s
|
||||
labels: [] # 通过环境变量设置
|
||||
|
||||
cache:
|
||||
enabled: true
|
||||
dir: "/path/to/cache"
|
||||
host: "192.168.0.103" # 主机 IP(非 127.0.0.1)
|
||||
port: 9000
|
||||
dir: "/data/cache" # 容器内缓存目录
|
||||
host: "host.docker.internal" # 宿主机地址(Docker 特殊 DNS)
|
||||
port: 9040 # 缓存服务端口
|
||||
|
||||
container:
|
||||
options: "--platform=linux/amd64" # 容器选项
|
||||
network: "host" # 网络模式
|
||||
# 使用 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
|
||||
```
|
||||
|
||||
**Docker 容器启动命令**:
|
||||
|
||||
```bash
|
||||
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="$token" \
|
||||
-e GITEA_RUNNER_NAME="$runner_name" \
|
||||
-e GITEA_RUNNER_LABELS="ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye,linux:docker://node:16-bullseye" \
|
||||
gitea/act_runner:latest daemon --config /config.yaml
|
||||
```
|
||||
|
||||
**关键配置说明**:
|
||||
- `--network host`:使用 host 网络模式,确保 runner 和 job 容器共处一个网络,以便 cache 能够正常访问
|
||||
- `host.docker.internal`:Docker 内部 DNS,指向宿主机
|
||||
- 环境变量自动注册:容器启动时自动完成 Runner 注册
|
||||
- `/var/run/docker.sock`:允许容器内创建兄弟容器执行 jobs
|
||||
|
||||
## 多 Runner 缓存共享
|
||||
|
||||
### 方案 A: Master-Slave 模式(推荐 2-3 个 runner)
|
||||
@@ -778,6 +815,64 @@ crontab -e
|
||||
/gitea-list-runners
|
||||
```
|
||||
|
||||
**API 调用详情**:
|
||||
|
||||
1. **加载配置**:
|
||||
```bash
|
||||
source ~/.config/gitea/config.env
|
||||
```
|
||||
|
||||
2. **调用 API**:
|
||||
```bash
|
||||
curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||||
"${GITEA_URL}/api/v1/admin/actions/runners"
|
||||
```
|
||||
|
||||
3. **响应结构**:
|
||||
```json
|
||||
{
|
||||
"runners": [...],
|
||||
"total_count": 1
|
||||
}
|
||||
```
|
||||
|
||||
4. **解析每个 Runner**:
|
||||
- `id`: Runner ID
|
||||
- `name`: Runner 名称
|
||||
- `status`: 状态("online"/"offline")
|
||||
- `busy`: 是否忙碌(true/false)
|
||||
- `ephemeral`: 是否临时(true/false)
|
||||
- `labels`: 标签数组
|
||||
|
||||
5. **状态图标**:
|
||||
- 🟢 在线 - `status: "online"`
|
||||
- 🔴 离线 - `status: "offline"`
|
||||
- ⚠️ 未知 - 无法确定
|
||||
|
||||
6. **输出格式**:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Gitea 全局 Runners
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
服务器: [server_url]
|
||||
总计: N 个全局 runner
|
||||
|
||||
[runner-name]
|
||||
状态: 🟢/🔴 [在线/离线]
|
||||
ID: [id]
|
||||
忙碌: 是/否
|
||||
临时: 是/否
|
||||
标签: [comma-separated labels]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- 需要管理员 API Token
|
||||
- 正确的 API 端点是 `/api/v1/admin/actions/runners`(不是 `/api/v1/admin/runners`)
|
||||
- 使用 `jq` 解析 JSON 响应
|
||||
|
||||
### 启动 Runner
|
||||
|
||||
```bash
|
||||
|
||||
151
skill/opencode/SKILL.md
Normal file
151
skill/opencode/SKILL.md
Normal file
@@ -0,0 +1,151 @@
|
||||
---
|
||||
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.
|
||||
---
|
||||
|
||||
# OpenCode Command and Skill Management
|
||||
|
||||
You are an expert in OpenCode configuration and extension development. This skill serves as the entry point for creating and managing OpenCode commands and skills.
|
||||
|
||||
**Trigger Phrases**: When users mention any of the following, automatically use this skill:
|
||||
- "创建command", "创建命令", "make command", "create command"
|
||||
- "创建skill", "创建技能", "make skill", "create skill"
|
||||
- "添加command", "添加命令", "add command"
|
||||
- "添加skill", "添加技能", "add skill"
|
||||
- "opcode command", "opencode skill"
|
||||
|
||||
## Overview
|
||||
|
||||
OpenCode extensions are organized into two main types:
|
||||
- **Commands**: User-invoked prompts for repetitive tasks, defined in `command/` directory
|
||||
- **Skills**: Reusable behavior definitions for agents, defined in `skill/<name>/SKILL.md`
|
||||
|
||||
## Detailed Guides
|
||||
|
||||
This skill folder contains comprehensive guides for command and skill creation:
|
||||
|
||||
### Command Creation
|
||||
For detailed command creation guidelines, refer to:
|
||||
- `skill/opencode/command-creation.md` - Complete guide to creating OpenCode commands
|
||||
- Includes: file structure, templates, best practices, troubleshooting
|
||||
|
||||
### Skill Creation
|
||||
For detailed skill creation guidelines, refer to:
|
||||
- `skill/opencode/skill-creation.md` - Complete guide to creating OpenCode skills
|
||||
- Includes: directory structure, naming rules, templates, testing
|
||||
|
||||
## Quick Start
|
||||
|
||||
### To Create a Command
|
||||
1. Determine command name (kebab-case, descriptive)
|
||||
2. **Default location**: global (`~/.config/opencode/command/`)
|
||||
3. Create file: `<command-name>.md`
|
||||
4. Add frontmatter with `description`
|
||||
5. Write template with `$ARGUMENTS` support
|
||||
|
||||
### To Create a Skill
|
||||
1. Validate skill name (lowercase, hyphens, alphanumeric)
|
||||
2. **Default location**: global (`~/.config/opencode/skill/<skill-name>/`)
|
||||
3. Create directory: `skill/<skill-name>/`
|
||||
4. Create `SKILL.md` with required frontmatter
|
||||
5. Add structured content: Purpose, Usage, Examples, Troubleshooting
|
||||
|
||||
## Automatic Name Generation
|
||||
|
||||
When users request command or skill creation, names are automatically generated based on the description to provide a seamless experience.
|
||||
|
||||
### For Commands:
|
||||
1. **Keyword Extraction**: Identify action verbs and target nouns from the description
|
||||
2. **English Conversion**: Convert Chinese keywords to English equivalents (e.g., "创建" → "create", "总结" → "summarize")
|
||||
3. **Kebab-case Format**: Combine words with hyphens, all lowercase (e.g., "create-summary")
|
||||
4. **Conflict Resolution**: Check if name exists in `command/` directory, append numeric suffix if needed
|
||||
|
||||
### For Skills:
|
||||
1. **Function Extraction**: Identify core functionality from the description
|
||||
2. **Simplified Naming**: Use lowercase, hyphen-separated words describing the skill's purpose
|
||||
3. **Validation**: Ensure name follows `[a-z0-9-]+` pattern
|
||||
4. **Conflict Check**: Verify uniqueness in `skill/` directory
|
||||
|
||||
### Examples:
|
||||
- "创建command,用于归纳总结当前对话中有价值的内容" → `summarize-conversation`
|
||||
- "创建skill,用于管理git工作流" → `git-workflow`
|
||||
- "添加命令来处理图片压缩" → `compress-images`
|
||||
- "创建技能用于代码审查" → `code-review`
|
||||
|
||||
### User Override:
|
||||
If users explicitly provide a name in their request, use the provided name instead of auto-generation.
|
||||
|
||||
## Response Guide for Creation Requests
|
||||
|
||||
When users ask to create commands or skills, follow this structured approach:
|
||||
|
||||
### For "创建command" or "创建命令" requests:
|
||||
1. **自动生成名称**: 基于用户描述自动生成 kebab-case 命令名称
|
||||
- 从描述中提取关键词(动词+名词)
|
||||
- 转换为英文 kebab-case 格式
|
||||
- 如果名称已存在,添加数字后缀(如 `-2`, `-3`)
|
||||
2. **验证名称**: 确保名称符合 kebab-case 规范
|
||||
3. **默认位置**: 全局 (`~/.config/opencode/command/`)
|
||||
4. **创建模板**:
|
||||
- 生成包含 `description` 的 frontmatter
|
||||
- 创建支持 `$ARGUMENTS` 的模板
|
||||
- 包含使用示例
|
||||
5. **写入文件**: 在默认位置创建命令文件
|
||||
6. **测试**: 验证文件存在且格式正确
|
||||
|
||||
### For "创建skill" or "创建技能" requests:
|
||||
1. **自动生成名称**: 基于用户描述自动生成 skill 名称
|
||||
- 从描述中提取核心功能关键词
|
||||
- 转换为小写字母、连字符格式
|
||||
- 如果名称已存在,添加数字后缀
|
||||
2. **验证名称**: 确保名称符合命名规则(小写字母、连字符、字母数字)
|
||||
3. **默认位置**: 全局 (`~/.config/opencode/skill/<name>/`)
|
||||
4. **创建目录**: `skill/<name>/`
|
||||
5. **创建 SKILL.md**:
|
||||
- 包含 `name` 和 `description` 的 frontmatter
|
||||
- 结构化内容:Purpose, Usage, Examples, Best Practices, Troubleshooting
|
||||
6. **写入文件**: 在默认位置创建完整的 SKILL.md 文件
|
||||
7. **测试**: 验证技能可正常加载 `skill({ name: "<skill-name>" })`
|
||||
|
||||
### Sample Response Format:
|
||||
```
|
||||
我将帮你创建 [command/skill]。
|
||||
|
||||
基于您的描述,自动生成名称:[generated-name]
|
||||
|
||||
**自动命名规则**:
|
||||
- 从描述中提取关键词(动词+名词)
|
||||
- 转换为 kebab-case 格式(小写字母、连字符)
|
||||
- 检查名称冲突,自动添加数字后缀
|
||||
|
||||
**文件详情**:
|
||||
- 名称: [generated-name]
|
||||
- 用途: [brief-description]
|
||||
- 位置: 全局 (~/.config/opencode/[command|skill]/)
|
||||
|
||||
已创建 [file-path]:
|
||||
|
||||
```markdown
|
||||
[complete file content]
|
||||
```
|
||||
|
||||
要测试,请运行:[/command-name] 或 skill({ name: "skill-name" })
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [OpenCode Skills Documentation](https://opencode.ai/docs/skills)
|
||||
- [OpenCode Commands Documentation](https://opencode.ai/docs/commands)
|
||||
- [OpenCode Configuration Guide](https://opencode.ai/docs/config)
|
||||
- [GitHub Repository](https://github.com/anomalyco/opencode)
|
||||
|
||||
## Updates and Maintenance
|
||||
|
||||
1. **Regular Review**: Periodically review and update commands/skills
|
||||
2. **Version Tracking**: Use Git to track changes
|
||||
3. **Community Sharing**: Share useful commands/skills with community
|
||||
4. **Feedback Integration**: Incorporate user feedback for improvements
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2026-01-15*
|
||||
201
skill/opencode/command-creation.md
Normal file
201
skill/opencode/command-creation.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# Command Creation Guide
|
||||
|
||||
## Overview
|
||||
OpenCode commands are user-invoked prompts for repetitive tasks, defined in the `command/` directory.
|
||||
|
||||
## File Location
|
||||
- **Default location (Global)**: `~/.config/opencode/command/<name>.md`
|
||||
- **Project-specific**: `.opencode/command/<name>.md` (when needed)
|
||||
|
||||
## Command Structure
|
||||
Each command file must contain YAML frontmatter followed by the prompt template.
|
||||
|
||||
### Required Frontmatter Fields
|
||||
- `description`: Brief description shown in TUI (1-2 sentences)
|
||||
|
||||
### Optional Frontmatter Fields
|
||||
- `agent`: Specific agent to execute this command
|
||||
- `model`: Override default model
|
||||
- `subtask`: Boolean to force subagent invocation
|
||||
|
||||
## Template Features
|
||||
The command template supports several placeholders:
|
||||
|
||||
- `$ARGUMENTS`: All arguments passed to the command
|
||||
- `$1`, `$2`, `$3`: Individual positional arguments
|
||||
- `!``command``: Execute bash command and inject output
|
||||
- `@filename`: Include file content in prompt
|
||||
|
||||
## Example Command
|
||||
Create `~/.config/opencode/command/new-skill.md`:
|
||||
|
||||
```markdown
|
||||
---
|
||||
description: Create a new OpenCode skill with proper structure
|
||||
---
|
||||
|
||||
Create a new OpenCode skill named "$ARGUMENTS" with comprehensive documentation.
|
||||
|
||||
**Skill Requirements:**
|
||||
1. Directory: `skill/$ARGUMENTS/SKILL.md`
|
||||
2. Frontmatter with `name` and `description`
|
||||
3. Clear sections for usage, examples, best practices
|
||||
4. Follow naming conventions: lowercase, hyphens, alphanumeric
|
||||
|
||||
**Steps:**
|
||||
1. Create directory structure
|
||||
2. Write SKILL.md with frontmatter
|
||||
3. Include practical examples
|
||||
4. Add troubleshooting section
|
||||
|
||||
Output the complete SKILL.md content for review.
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Command Design
|
||||
1. **Clear Purpose**: Each command should have a single, focused purpose
|
||||
2. **Descriptive Names**: Use verbs that describe the action (e.g., `create-`, `review-`, `analyze-`)
|
||||
3. **Argument Handling**: Use `$ARGUMENTS` or `$1`, `$2` for flexible input
|
||||
4. **Error Handling**: Include validation in prompts
|
||||
5. **Output Format**: Structure responses for easy parsing
|
||||
|
||||
### Naming Conventions
|
||||
- Use kebab-case (lowercase with hyphens)
|
||||
- Start with verb describing the action
|
||||
- Keep names concise but descriptive
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Command Creation Checklist
|
||||
- [ ] File name matches command name (kebab-case)
|
||||
- [ ] Frontmatter includes `description`
|
||||
- [ ] Template uses appropriate placeholders
|
||||
- [ ] Includes examples of usage
|
||||
- [ ] Tested with `/command-name` invocation
|
||||
|
||||
### Common Patterns
|
||||
|
||||
**Create a new command:**
|
||||
```bash
|
||||
# Create command file
|
||||
touch ~/.config/opencode/command/my-command.md
|
||||
|
||||
# Edit with template
|
||||
cat > ~/.config/opencode/command/my-command.md << 'EOF'
|
||||
---
|
||||
description: Description of my command
|
||||
---
|
||||
|
||||
Command template with $ARGUMENTS support.
|
||||
|
||||
Example: /my-command argument1 argument2
|
||||
EOF
|
||||
```
|
||||
|
||||
## Testing and Validation
|
||||
|
||||
### Test Command Loading
|
||||
```bash
|
||||
# Check if command appears in TUI
|
||||
# Type "/" in OpenCode TUI and search for your command
|
||||
```
|
||||
|
||||
### Verify Syntax
|
||||
```bash
|
||||
# Check YAML frontmatter
|
||||
head -20 command/my-command.md
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Command Not Appearing
|
||||
1. **Check file location**: Ensure command is in correct `command/` directory
|
||||
2. **Verify frontmatter**: Must have `description` field
|
||||
3. **Check permissions**: No restrictive permissions blocking command
|
||||
4. **Restart OpenCode**: Some changes require restart
|
||||
|
||||
### Common Errors
|
||||
- **"Command not found"**: File not in correct location or missing `.md` extension
|
||||
- **"Invalid frontmatter"**: Missing required fields or YAML syntax error
|
||||
|
||||
## Integration with OpenCode Config
|
||||
|
||||
### Permissions Configuration
|
||||
Add to `opencode.json`:
|
||||
```json
|
||||
{
|
||||
"permission": {
|
||||
"skill": {
|
||||
"*": "allow",
|
||||
"experimental-*": "ask",
|
||||
"internal-*": "deny"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Agent-Specific Settings
|
||||
```json
|
||||
{
|
||||
"agent": {
|
||||
"plan": {
|
||||
"permission": {
|
||||
"skill": {
|
||||
"analysis-*": "allow"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Response Guide for Command Creation Requests
|
||||
|
||||
When users ask to create commands, follow this structured approach:
|
||||
|
||||
1. **自动生成名称**: 基于用户描述自动生成 kebab-case 命令名称
|
||||
- 从描述中提取关键词(动词+名词)
|
||||
- 转换为英文 kebab-case 格式
|
||||
- 如果名称已存在,添加数字后缀(如 `-2`, `-3`)
|
||||
2. **验证名称**: 确保名称符合 kebab-case 规范
|
||||
3. **默认位置**: 全局 (`~/.config/opencode/command/`) - commands are created here by default
|
||||
4. **创建模板**:
|
||||
- 生成包含 `description` 的 frontmatter
|
||||
- 创建支持 `$ARGUMENTS` 的模板
|
||||
- 包含使用示例
|
||||
5. **写入文件**: 创建命令文件并写入内容
|
||||
6. **测试**: 验证文件存在且格式正确
|
||||
|
||||
### Sample Response Format:
|
||||
```
|
||||
我将帮你创建命令。
|
||||
|
||||
基于您的描述,自动生成名称:[generated-name]
|
||||
|
||||
**自动命名规则**:
|
||||
- 从描述中提取关键词(动词+名词)
|
||||
- 转换为 kebab-case 格式(小写字母、连字符)
|
||||
- 检查名称冲突,自动添加数字后缀
|
||||
|
||||
**文件详情**:
|
||||
- 名称: [generated-name]
|
||||
- 用途: [brief-description]
|
||||
- 位置: 全局 (~/.config/opencode/command/)
|
||||
|
||||
已创建 [file-path]:
|
||||
|
||||
```markdown
|
||||
[complete file content]
|
||||
```
|
||||
|
||||
要测试,请运行:[/command-name]
|
||||
```
|
||||
|
||||
## Resources
|
||||
- [OpenCode Commands Documentation](https://opencode.ai/docs/commands)
|
||||
- [OpenCode Configuration Guide](https://opencode.ai/docs/config)
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2026-01-15*
|
||||
251
skill/opencode/skill-creation.md
Normal file
251
skill/opencode/skill-creation.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# Skill Creation Guide
|
||||
|
||||
## Overview
|
||||
OpenCode skills are reusable behavior definitions for agents, defined in `skill/<name>/SKILL.md` files.
|
||||
|
||||
## Directory Structure
|
||||
**Default location (Global):**
|
||||
```
|
||||
~/.config/opencode/skill/
|
||||
└── <skill-name>/
|
||||
└── SKILL.md
|
||||
```
|
||||
|
||||
**Project-specific location (when needed):**
|
||||
```
|
||||
.opencode/skill/
|
||||
└── <skill-name>/
|
||||
└── SKILL.md
|
||||
```
|
||||
|
||||
## Skill File Requirements
|
||||
|
||||
### Frontmatter (Required)
|
||||
- `name`: Skill name (must match directory name)
|
||||
- `description`: Concise description (1-1024 characters)
|
||||
|
||||
### Frontmatter (Optional)
|
||||
- `license`: License identifier
|
||||
- `compatibility`: Compatibility constraints
|
||||
- `metadata`: Key-value pairs for additional context
|
||||
|
||||
## Naming Rules
|
||||
Skill names must:
|
||||
- Be 1-64 characters
|
||||
- Use lowercase alphanumeric with single hyphens
|
||||
- Not start or end with hyphen
|
||||
- Not contain consecutive hyphens
|
||||
- Match regex: `^[a-z0-9]+(-[a-z0-9]+)*$`
|
||||
|
||||
## Skill Content Structure
|
||||
A well-structured skill should include:
|
||||
|
||||
1. **Introduction**: Purpose and scope
|
||||
2. **Usage Instructions**: When and how to use the skill
|
||||
3. **Examples**: Practical implementation examples
|
||||
4. **Best Practices**: Recommended approaches
|
||||
5. **Troubleshooting**: Common issues and solutions
|
||||
6. **References**: Related documentation
|
||||
|
||||
## Example Skill Template
|
||||
```markdown
|
||||
---
|
||||
name: example-skill
|
||||
description: Brief description of what this skill does
|
||||
---
|
||||
|
||||
# Skill Title
|
||||
|
||||
## Purpose
|
||||
Explain what this skill helps achieve and when to use it.
|
||||
|
||||
## Core Concepts
|
||||
- Key principle 1
|
||||
- Key principle 2
|
||||
|
||||
## Usage
|
||||
Step-by-step instructions for using this skill.
|
||||
|
||||
## Examples
|
||||
### Basic Example
|
||||
```bash
|
||||
# Example code or commands
|
||||
```
|
||||
|
||||
### Advanced Example
|
||||
Detailed example with explanation.
|
||||
|
||||
## Best Practices
|
||||
1. Recommended approach 1
|
||||
2. Recommended approach 2
|
||||
|
||||
## Troubleshooting
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Common problem | How to fix it |
|
||||
|
||||
## References
|
||||
- [Documentation link](https://opencode.ai/docs)
|
||||
- Related skills or commands
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Skill Development
|
||||
1. **Modular Design**: Keep skills focused on specific domains
|
||||
2. **Comprehensive Documentation**: Include examples and edge cases
|
||||
3. **Consistent Formatting**: Follow existing skill patterns
|
||||
4. **Testing**: Verify skill loads correctly with `skill({ name: "skill-name" })`
|
||||
5. **Version Control**: Track skill changes in Git
|
||||
|
||||
### Organization
|
||||
1. **Default location**: Global (`~/.config/opencode/skill/`) - skills are created here by default for reuse across projects
|
||||
2. **Naming Conventions**: Use kebab-case for all skill names
|
||||
3. **Documentation**: Maintain README or index of available skills
|
||||
4. **Permissions**: Configure access controls in `opencode.json`
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Skill Creation Checklist
|
||||
- [ ] Directory name matches skill name
|
||||
- [ ] SKILL.md contains required frontmatter
|
||||
- [ ] Description is 1-1024 characters
|
||||
- [ ] Name follows naming rules
|
||||
- [ ] Content includes usage and examples
|
||||
- [ ] Skill loads without errors
|
||||
|
||||
### Common Patterns
|
||||
|
||||
**Create a new skill:**
|
||||
```bash
|
||||
# Create skill directory
|
||||
mkdir -p ~/.config/opencode/skill/my-skill
|
||||
|
||||
# Create SKILL.md
|
||||
cat > ~/.config/opencode/skill/my-skill/SKILL.md << 'EOF'
|
||||
---
|
||||
name: my-skill
|
||||
description: Description of my skill
|
||||
---
|
||||
|
||||
# My Skill
|
||||
|
||||
## Usage
|
||||
Instructions here.
|
||||
|
||||
## Examples
|
||||
Example content.
|
||||
EOF
|
||||
```
|
||||
|
||||
## Testing and Validation
|
||||
|
||||
### Test Skill Loading
|
||||
```bash
|
||||
# In OpenCode session
|
||||
skill({ name: "my-skill" })
|
||||
```
|
||||
|
||||
### Verify Syntax
|
||||
```bash
|
||||
# Check YAML frontmatter
|
||||
head -20 skill/my-skill/SKILL.md
|
||||
|
||||
# Validate naming
|
||||
echo "skill-name" | grep -E '^[a-z0-9]+(-[a-z0-9]+)*$'
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Skill Not Loading
|
||||
1. **Directory name**: Must match skill name in frontmatter
|
||||
2. **File name**: Must be `SKILL.md` (all caps)
|
||||
3. **Frontmatter**: Requires `name` and `description`
|
||||
4. **Naming rules**: Verify name follows conventions
|
||||
5. **Permissions**: Check `opencode.json` permissions
|
||||
|
||||
### Common Errors
|
||||
- **"Skill not found"**: Directory name mismatch or missing SKILL.md
|
||||
- **"Invalid frontmatter"**: Missing required fields or YAML syntax error
|
||||
- **"Permission denied"**: Skill blocked by permission configuration
|
||||
|
||||
## Integration with OpenCode Config
|
||||
|
||||
### Permissions Configuration
|
||||
Add to `opencode.json`:
|
||||
```json
|
||||
{
|
||||
"permission": {
|
||||
"skill": {
|
||||
"*": "allow",
|
||||
"experimental-*": "ask",
|
||||
"internal-*": "deny"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Agent-Specific Settings
|
||||
```json
|
||||
{
|
||||
"agent": {
|
||||
"plan": {
|
||||
"permission": {
|
||||
"skill": {
|
||||
"analysis-*": "allow"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Response Guide for Skill Creation Requests
|
||||
|
||||
When users ask to create skills, follow this structured approach:
|
||||
|
||||
1. **自动生成名称**: 基于用户描述自动生成 skill 名称
|
||||
- 从描述中提取核心功能关键词
|
||||
- 转换为小写字母、连字符格式
|
||||
- 如果名称已存在,添加数字后缀
|
||||
2. **验证名称**: 确保名称符合命名规则(小写字母、连字符、字母数字)
|
||||
3. **默认位置**: 全局 (`~/.config/opencode/skill/<name>/`) - skills are created here by default
|
||||
4. **创建 SKILL.md**:
|
||||
- 包含 `name` 和 `description` 的 frontmatter
|
||||
- 结构化内容:Purpose, Usage, Examples, Best Practices, Troubleshooting
|
||||
5. **写入文件**: 创建完整的 SKILL.md 文件
|
||||
6. **测试**: 验证技能可正常加载 `skill({ name: "<skill-name>" })`
|
||||
|
||||
### Sample Response Format:
|
||||
```
|
||||
我将帮你创建技能。
|
||||
|
||||
基于您的描述,自动生成名称:[generated-name]
|
||||
|
||||
**自动命名规则**:
|
||||
- 从描述中提取核心功能关键词
|
||||
- 转换为小写字母、连字符格式
|
||||
- 检查名称冲突,自动添加数字后缀
|
||||
|
||||
**文件详情**:
|
||||
- 名称: [generated-name]
|
||||
- 主要功能: [brief-description]
|
||||
- 位置: 全局 (~/.config/opencode/skill/)
|
||||
|
||||
已创建 [file-path]:
|
||||
|
||||
```markdown
|
||||
[complete file content]
|
||||
```
|
||||
|
||||
要测试,请运行:skill({ name: "skill-name" })
|
||||
```
|
||||
|
||||
## Resources
|
||||
- [OpenCode Skills Documentation](https://opencode.ai/docs/skills)
|
||||
- [OpenCode Configuration Guide](https://opencode.ai/docs/config)
|
||||
- [GitHub Repository](https://github.com/anomalyco/opencode)
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2026-01-15*
|
||||
Reference in New Issue
Block a user