From ab5e59e9996c873bd195a64bde5f5c5dc3ea16e1 Mon Sep 17 00:00:00 2001 From: yanghuajun336 Date: Tue, 31 Mar 2026 22:35:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=20gitea-actor=20?= =?UTF-8?q?=E6=8A=80=E8=83=BD=EF=BC=8C=E6=96=B0=E5=A2=9E=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E5=92=8C=E5=AE=8C=E6=95=B4=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 setup.sh 配置脚本 - 新增 api-examples.sh API 函数库 - 新增 gitea-cli.py Python CLI 工具 - 完善文档结构 (1640 行) - 添加实际用例和工作流示例 - 增强故障排除和最佳实践指南 --- skills/gitea-actor/SKILL.md | 1640 ++++++++++++++++++++ skills/gitea-actor/scripts/api-examples.sh | 290 ++++ skills/gitea-actor/scripts/gitea-cli.py | 340 ++++ skills/gitea-actor/scripts/setup.sh | 185 +++ 4 files changed, 2455 insertions(+) create mode 100644 skills/gitea-actor/SKILL.md create mode 100755 skills/gitea-actor/scripts/api-examples.sh create mode 100755 skills/gitea-actor/scripts/gitea-cli.py create mode 100755 skills/gitea-actor/scripts/setup.sh diff --git a/skills/gitea-actor/SKILL.md b/skills/gitea-actor/SKILL.md new file mode 100644 index 0000000..0023e45 --- /dev/null +++ b/skills/gitea-actor/SKILL.md @@ -0,0 +1,1640 @@ +--- +name: gitea-actor +description: 类似 GitHub Copilot 的 Gitea 操作技能,支持仓库管理、PR/Issue 操作、代码审查等 +--- + +# Gitea-Actor + +此技能提供类似 GitHub Copilot 的 Gitea 操作功能,支持自托管 Gitea 实例的完整 Git 工作流。 + +## 何时使用此技能 + +当用户需要操作 Gitea 仓库时使用此技能,包括: +- 创建、克隆、管理 Gitea 仓库 +- 处理 Pull Request(合并请求) +- 管理 Issue 和任务 +- 进行代码审查和评论 +- 管理分支、标签和版本 +- 配置 Gitea 实例和身份验证 + +**触发关键词**:gitea, git, repository, repo, pull request, pr, issue, code review, merge, branch, tag, clone, push, pull + +## 前置要求 + +### 1. Gitea 实例配置 +用户需要提供 Gitea 实例的 URL 和个人访问令牌。 + +### 2. 环境变量配置 +```bash +# 设置 Gitea 实例 URL(默认为 https://gitea.com) +export GITEA_URL="https://your-gitea-instance.com" + +# 设置个人访问令牌 +export GITEA_TOKEN="your_personal_access_token_here" +``` + +### 3. 验证配置 +```bash +# 验证 Gitea 连接 +curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" | jq . +``` + +## 快速开始 + +### 方法一:使用配置脚本(推荐) +```bash +# 下载并运行配置脚本 +curl -s https://raw.githubusercontent.com/your-repo/gitea-actor/main/scripts/setup.sh | bash + +# 或手动下载后运行 +chmod +x setup.sh +./setup.sh +``` + +### 方法二:手动配置 +```bash +# 设置环境变量 +export GITEA_URL="https://your-gitea-instance.com" +export GITEA_TOKEN="your_personal_access_token_here" + +# 验证连接 +curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" | jq . +``` + +### 方法三:使用 Python CLI 工具 +```bash +# 安装依赖 +pip install requests + +# 使用 CLI +python gitea-cli.py config test +``` + +## 核心功能指令 + +### 1. 仓库管理 + +#### 1.1 创建仓库 +```bash +# 创建新仓库 +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name":"my-new-repo", "description":"My new repository", "private":false}' \ + "$GITEA_URL/api/v1/user/repos" +``` + +#### 1.2 克隆仓库 +```bash +# 克隆仓库 +git clone "$GITEA_URL//.git" +cd +``` + +#### 1.3 列出用户仓库 +```bash +# 列出用户的所有仓库 +curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/user/repos" | jq '.[] | .full_name' +``` + +### 2. Pull Request 管理 + +#### 2.1 创建 Pull Request +```bash +# 创建 PR +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "title":"Feature: Add new functionality", + "body":"This PR adds new functionality to improve performance.", + "head":"feature-branch", + "base":"main" + }' \ + "$GITEA_URL/api/v1/repos///pulls" +``` + +#### 2.2 列出 Pull Requests +```bash +# 列出仓库的所有 PR +curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/repos///pulls" | jq '.[] | .number, .title' +``` + +#### 2.3 合并 Pull Request +```bash +# 合并 PR +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"Do":"merge"}' \ + "$GITEA_URL/api/v1/repos///pulls//merge" +``` + +### 3. Issue 管理 + +#### 3.1 创建 Issue +```bash +# 创建新 Issue +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "title":"Bug: Fix memory leak", + "body":"There is a memory leak in the cache module.", + "labels":["bug", "high-priority"] + }' \ + "$GITEA_URL/api/v1/repos///issues" +``` + +#### 3.2 分配 Issue +```bash +# 分配 Issue 给用户 +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"assignees":["username"]}' \ + "$GITEA_URL/api/v1/repos///issues//assignees" +``` + +### 4. 代码审查 + +#### 4.1 添加 PR 评论 +```bash +# 在 PR 中添加评论 +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "body":"This looks good! Just one small suggestion...", + "path":"src/main.js", + "line":42 + }' \ + "$GITEA_URL/api/v1/repos///pulls//comments" +``` + +#### 4.2 查看代码差异 +```bash +# 获取 PR 的差异 +curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/repos///pulls/.diff" +``` + +### 5. 分支和标签管理 + +#### 5.1 创建分支 +```bash +# 从现有分支创建新分支 +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "new_branch_name":"feature/new-feature", + "old_branch_name":"main" + }' \ + "$GITEA_URL/api/v1/repos///branches" +``` + +#### 5.2 创建标签 +```bash +# 创建新标签 +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "tag_name":"v1.0.0", + "message":"Release version 1.0.0", + "target":"main" + }' \ + "$GITEA_URL/api/v1/repos///tags" +``` + +## 工作流程示例 + +### 示例 1:完整的 PR 工作流 + +```bash +# 1. 创建功能分支 +git checkout -b feature/new-feature + +# 2. 进行更改并提交 +git add . +git commit -m "Add new feature" + +# 3. 推送到远程 +git push origin feature/new-feature + +# 4. 创建 Pull Request +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "title":"Feature: Add new feature", + "body":"## Description\nThis PR adds a new feature to improve user experience.\n\n## Changes\n- Added new API endpoint\n- Updated documentation\n- Added tests", + "head":"feature/new-feature", + "base":"main" + }' \ + "$GITEA_URL/api/v1/repos///pulls" +``` + +### 示例 2:Issue 跟踪工作流 + +```bash +# 1. 创建 Bug Issue +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "title":"Bug: Application crashes on startup", + "body":"## Steps to reproduce\n1. Start the application\n2. Click on settings\n3. Application crashes\n\n## Expected behavior\nApplication should not crash", + "labels":["bug", "critical"], + "assignees":["developer1"] + }' \ + "$GITEA_URL/api/v1/repos///issues" + +# 2. 修复后关闭 Issue +curl -X PATCH -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"state":"closed"}' \ + "$GITEA_URL/api/v1/repos///issues/" +``` + +## 实际用例 + +以下是一些真实场景下的 Gitea 自动化用例: + +### 用例 1:自动化项目初始化 +**场景**: 新项目启动需要快速创建仓库、配置 CI/CD、添加基础文件 + +```bash +#!/bin/bash +# init-project.sh +set -e + +# 配置参数 +PROJECT_NAME="my-project" +DESCRIPTION="一个新的微服务项目" +TEAM="backend-developers" + +# 1. 创建仓库 +echo "创建仓库: $PROJECT_NAME" +REPO_JSON=$(curl -s -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg name "$PROJECT_NAME" \ + --arg desc "$DESCRIPTION" \ + --argjson private true \ + '{name: $name, description: $desc, private: $private, auto_init: true}')" \ + "$GITEA_URL/api/v1/user/repos") + +REPO_OWNER=$(echo "$REPO_JSON" | jq -r '.owner.login') +REPO_NAME=$(echo "$REPO_JSON" | jq -r '.name') + +# 2. 添加团队访问 +echo "配置团队权限" +curl -X PUT -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/teams/$TEAM/repos/$REPO_OWNER/$REPO_NAME" + +# 3. 添加基础文件 +echo "添加基础文件" +add_file() { + local path=$1 + local content=$2 + local message=$3 + + curl -s -X PUT -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg content "$(echo "$content" | base64)" \ + --arg message "$message" \ + --arg branch "main" \ + '{content: $content, message: $message, branch: $branch}')" \ + "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/contents/$path" > /dev/null +} + +# 添加 README +add_file "README.md" "# $PROJECT_NAME\n\n$DESCRIPTION" "添加 README" + +# 添加 .gitignore +add_file ".gitignore" "node_modules/\n.env\n*.log\n" "添加 .gitignore" + +# 添加 CI 配置 +add_file ".gitea/workflows/ci.yml" "$(cat <<'EOF' +name: CI +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: echo 'CI 流水线就绪' +EOF +)" "添加 CI 配置" + +# 4. 创建开发分支 +echo "创建开发分支" +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"new_branch_name": "develop", "old_branch_name": "main"}' \ + "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/branches" + +# 5. 创建初始 Issue +echo "创建初始任务" +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg title "项目初始化" \ + --arg body "## 待办事项\n- [ ] 配置开发环境\n- [ ] 编写 API 文档\n- [ ] 设置测试框架" \ + '{title: $title, body: $body, labels: ["enhancement", "documentation"]}')" \ + "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/issues" + +echo "项目初始化完成: $GITEA_URL/$REPO_OWNER/$REPO_NAME" +``` + +### 用例 2:自动化代码审查工作流 +**场景**: 团队需要标准化代码审查流程 + +```bash +#!/bin/bash +# code-review-automation.sh + +# 检查所有开放的 PR,自动分配评审者并添加评论 +check_open_prs() { + local owner=$1 + local repo=$2 + + echo "检查 $owner/$repo 的开放 PR..." + + prs=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/repos/$owner/$repo/pulls?state=open") + + echo "$prs" | jq -c '.[]' | while read pr; do + pr_number=$(echo "$pr" | jq -r '.number') + pr_title=$(echo "$pr" | jq -r '.title') + pr_author=$(echo "$pr" | jq -r '.user.login') + + echo "处理 PR #$pr_number: $pr_title (作者: $pr_author)" + + # 检查是否已分配评审者 + reviewers=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/repos/$owner/$repo/pulls/$pr_number/requested_reviewers") + + if [[ $(echo "$reviewers" | jq '.users | length') -eq 0 ]]; then + # 自动分配评审者(基于轮询或专业知识) + assign_reviewer "$owner" "$repo" "$pr_number" "$pr_author" + fi + + # 检查是否缺少描述 + pr_body=$(echo "$pr" | jq -r '.body') + if [[ -z "$pr_body" || "$pr_body" == "null" ]]; then + add_pr_comment "$owner" "$repo" "$pr_number" \ + "⚠️ 请添加 PR 描述,说明更改内容和原因。" + fi + + # 检查是否关联 Issue + if ! echo "$pr_body" | grep -q -E "(close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved) #[0-9]+"; then + add_pr_comment "$owner" "$repo" "$pr_number" \ + "💡 建议关联相关 Issue,使用 'fixes #123' 语法。" + fi + done +} + +assign_reviewer() { + local owner=$1 repo=$2 pr_number=$3 author=$4 + + # 排除作者自己,从团队中随机选择评审者 + reviewers=("alice" "bob" "charlie") + for reviewer in "${reviewers[@]}"; do + if [[ "$reviewer" != "$author" ]]; then + selected_reviewer=$reviewer + break + fi + done + + echo "分配评审者: $selected_reviewer" + + curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg reviewer "$selected_reviewer" \ + '{reviewers: [$reviewer]}')" \ + "$GITEA_URL/api/v1/repos/$owner/$repo/pulls/$pr_number/requested_reviewers" > /dev/null +} + +add_pr_comment() { + local owner=$1 repo=$2 pr_number=$3 comment=$4 + + curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg body "$comment" \ + '{body: $body}')" \ + "$GITEA_URL/api/v1/repos/$owner/$repo/pulls/$pr_number/comments" > /dev/null +} + +# 定时运行(例如通过 cron) +# */30 * * * * /path/to/code-review-automation.sh owner repo +``` + +### 用例 3:监控和报告生成 +**场景**: 定期生成团队活动报告 + +```bash +#!/bin/bash +# generate-team-report.sh + +generate_weekly_report() { + local team=$1 + local start_date=$(date -d "7 days ago" +%Y-%m-%d) + local end_date=$(date +%Y-%m-%d) + + echo "# 团队周报: $team" + echo "时间段: $start_date 至 $end_date" + echo "生成时间: $(date)" + echo "" + + # 获取团队仓库 + team_repos=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/teams/$team/repos") + + echo "## 仓库活动汇总" + echo "" + + echo "$team_repos" | jq -c '.[]' | while read repo; do + repo_name=$(echo "$repo" | jq -r '.full_name') + + # 获取仓库活动 + events=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/repos/$repo_name/events?since=$start_date") + + push_count=$(echo "$events" | jq '[.[] | select(.type == "push")] | length') + pr_count=$(echo "$events" | jq '[.[] | select(.type == "pull_request")] | length') + issue_count=$(echo "$events" | jq '[.[] | select(.type == "issues")] | length') + + echo "### $repo_name" + echo "- 推送次数: $push_count" + echo "- PR 数量: $pr_count" + echo "- Issue 数量: $issue_count" + echo "" + + # 列出新创建的 PR + if [[ $pr_count -gt 0 ]]; then + echo "#### 新 PR:" + echo "$events" | jq -r '.[] | select(.type == "pull_request" and .payload.action == "opened") | "- #\(.payload.number): \(.payload.pull_request.title) (\(.actor.login))"' | head -5 + echo "" + fi + + # 列出关闭的 Issue + if [[ $issue_count -gt 0 ]]; then + echo "#### 已关闭 Issue:" + echo "$events" | jq -r '.[] | select(.type == "issues" and .payload.action == "closed") | "- #\(.payload.issue.number): \(.payload.issue.title) (\(.actor.login))"' | head -5 + echo "" + fi + done + + # 保存报告 + report_file="/tmp/team-report-$team-$(date +%Y%m%d).md" + echo "报告已保存到: $report_file" + + # 可选:发送到团队频道 + # send_to_slack "$(cat $report_file)" "#team-reports" +} + +# 生成报告 +generate_weekly_report "developers" +``` + +### 用例 4:自动化发布管理 +**场景**: 自动化版本发布和变更日志生成 + +```bash +#!/bin/bash +# release-manager.sh + +create_release() { + local owner=$1 + local repo=$2 + local version=$3 + local release_notes=$4 + + echo "创建发布: $owner/$repo v$version" + + # 1. 创建发布分支 + branch_name="release/v$version" + curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg branch "$branch_name" \ + --arg base "main" \ + '{new_branch_name: $branch, old_branch_name: $base}')" \ + "$GITEA_URL/api/v1/repos/$owner/$repo/branches" + + # 2. 更新版本文件 + update_version_file "$owner" "$repo" "$branch_name" "$version" + + # 3. 创建 PR + pr_title="Release v$version" + pr_body="## 版本 $version\n\n$release_notes\n\n### 变更内容\n$(generate_changelog $owner $repo $version)" + + curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg title "$pr_title" \ + --arg body "$pr_body" \ + --arg head "$branch_name" \ + --arg base "main" \ + '{title: $title, body: $body, head: $head, base: $base}')" \ + "$GITEA_URL/api/v1/repos/$owner/$repo/pulls" + + # 4. 合并 PR 后创建标签 + echo "请合并 PR 后运行:" + echo " create_tag \"$owner\" \"$repo\" \"v$version\" \"Release version $version\" \"main\"" +} + +generate_changelog() { + local owner=$1 repo=$2 version=$3 + + # 获取上一个版本 + prev_tag=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/repos/$owner/$repo/tags" | \ + jq -r '.[0].name' 2>/dev/null || echo "v0.0.0") + + # 获取提交历史 + commits=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/repos/$owner/$repo/compare/$prev_tag...main") + + echo "$commits" | jq -r '.commits[] | "- \(.commit.message | split("\n")[0]) (\(.author.login))"' | head -20 +} + +# 使用示例 +# create_release "owner" "repo" "1.2.0" "功能更新和错误修复" +``` + +## 工具和脚本 + +本技能提供以下实用脚本,简化 Gitea 操作: + +### 1. 配置脚本 (`setup.sh`) +交互式配置向导,帮助设置 Gitea 环境变量和测试连接。 + +**功能:** +- 交互式输入 Gitea URL 和访问令牌 +- 自动验证连接 +- 保存配置到 `~/.gitea-actor.env` +- 支持颜色输出和错误处理 + +**使用方法:** +```bash +# 授予执行权限 +chmod +x scripts/setup.sh + +# 运行配置向导 +./scripts/setup.sh + +# 加载配置 +source ~/.gitea-actor.env +``` + +### 2. API 示例脚本 (`api-examples.sh`) +包含常用 Gitea API 操作的 Bash 函数库。 + +**功能:** +- 20+ 个常用 API 函数 +- 统一的错误处理 +- 友好的输出格式 +- 可直接在 Shell 中使用 + +**使用方法:** +```bash +# 加载函数到当前 Shell +source scripts/api-examples.sh + +# 使用函数 +get_current_user +list_user_repos +create_repository "my-repo" "描述" false +``` + +### 3. Python CLI 工具 (`gitea-cli.py`) +功能完整的命令行工具,支持所有主要 Gitea 操作。 + +**功能:** +- 完整的命令行界面 +- 支持所有主要 Gitea API +- JSON 输出格式 +- 错误处理和验证 + +**安装和使用:** +```bash +# 安装依赖 +pip install requests + +# 查看帮助 +python scripts/gitea-cli.py --help + +# 示例命令 +python scripts/gitea-cli.py user info +python scripts/gitea-cli.py repo create --name myrepo --description "测试仓库" +python scripts/gitea-cli.py pr create --owner admin --repo myrepo --title "测试PR" --head feature-branch +``` + +## 故障排除 + +### 常见问题及解决方案 + +#### 1. 认证失败 (`401 Unauthorized`) +**症状**: API 返回 401 状态码 +**可能原因**: +- 令牌无效或已过期 +- 令牌权限不足 +- URL 格式错误 + +**解决方案**: +```bash +# 1. 重新生成令牌(Gitea 设置 -> 应用) +# 2. 验证令牌权限(需要 repo、user 等权限) +# 3. 更新环境变量 +export GITEA_TOKEN="new_token_here" +# 4. 测试连接 +curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" | jq . +``` + +#### 2. 连接超时 +**症状**: `Connection timed out` 或 `Could not resolve host` +**可能原因**: +- 网络不可达 +- DNS 解析失败 +- 防火墙阻止 + +**解决方案**: +```bash +# 1. 测试网络连接 +ping -c 3 $(echo $GITEA_URL | sed 's|https://||' | sed 's|http://||' | cut -d/ -f1) + +# 2. 检查 DNS 解析 +nslookup $(echo $GITEA_URL | sed 's|https://||' | sed 's|http://||' | cut -d/ -f1) + +# 3. 使用 curl 调试 +curl -v $GITEA_URL +``` + +#### 3. API 版本不匹配 (`404 Not Found`) +**症状**: API 返回 404 状态码 +**可能原因**: +- API 路径错误 +- Gitea 版本过旧 +- 资源不存在 + +**解决方案**: +```bash +# 1. 检查 Gitea 版本 +curl -s "$GITEA_URL/api/v1/version" | jq . + +# 2. 验证 API 端点是否存在 +curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" + +# 3. 检查 Gitea 文档确认 API 兼容性 +``` + +#### 4. 权限不足 (`403 Forbidden`) +**症状**: API 返回 403 状态码 +**可能原因**: +- 令牌缺少必要权限 +- 用户无权访问资源 +- 仓库权限设置限制 + +**解决方案**: +```bash +# 1. 检查令牌权限范围 +# 2. 在 Gitea 中检查仓库权限 +# 3. 确保令牌有对应操作的权限 +``` + +#### 5. 速率限制 (`429 Too Many Requests`) +**症状**: API 返回 429 状态码 +**可能原因**: +- API 请求过于频繁 +- Gitea 实例设置了速率限制 + +**解决方案**: +```bash +# 1. 添加请求延迟 +sleep 1 # 每次请求后等待1秒 + +# 2. 检查速率限制头 +curl -s -I -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" + +# 3. 批量处理请求,减少请求次数 +``` + +#### 6. JSON 解析错误 +**症状**: `jq: parse error` 或 JSON 格式错误 +**可能原因**: +- API 响应不是有效的 JSON +- 网络问题导致响应不完整 +- Gitea 返回错误页面 + +**解决方案**: +```bash +# 1. 不使用 jq,先查看原始响应 +curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" + +# 2. 检查响应内容类型 +curl -s -I -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" | grep -i "content-type" + +# 3. 添加错误处理到脚本 +response=$(curl -s -w "\n%{http_code}" ...) +http_code=$(echo "$response" | tail -n1) +body=$(echo "$response" | sed '$d') +``` + +#### 7. SSL/TLS 证书问题 +**症状**: `SSL certificate problem` 或 `self-signed certificate` +**可能原因**: +- 自签名证书 +- 证书过期 +- 证书链不完整 + +**解决方案**: +```bash +# 1. 跳过证书验证(不推荐用于生产) +curl -k -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" + +# 2. 添加自定义证书 +curl --cacert /path/to/cert.pem -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" + +# 3. 更新系统证书存储 +``` + +### 调试技巧 + +1. **使用 verbose 模式**: + ```bash + curl -v -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" + ``` + +2. **检查响应头**: + ```bash + curl -s -I -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" + ``` + +3. **保存调试信息**: + ```bash + curl -s -w "\nStatus: %{http_code}\nTime: %{time_total}s\n" \ + -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/user" > debug.log + ``` + +4. **使用 Python 脚本调试**: + ```python + import requests + response = requests.get(url, headers=headers) + print(f"Status: {response.status_code}") + print(f"Headers: {response.headers}") + print(f"Body: {response.text[:500]}") + ``` + +## 最佳实践 + +### 1. 安全性最佳实践 + +#### 令牌管理 +- **永不提交令牌**: 避免将令牌硬编码在脚本或提交到版本控制系统 +- **使用环境变量**: 通过环境变量或配置文件管理敏感信息 +- **定期轮换**: 每 3-6 个月更新一次访问令牌 +- **最小权限原则**: 只为令牌授予必要的最小权限 +- **令牌存储安全**: 使用安全的密码管理器存储令牌备份 + +#### 访问控制 +- **使用个人访问令牌**: 而非密码进行 API 认证 +- **限制令牌范围**: 在创建令牌时只选择必要的权限范围 +- **监控异常活动**: 定期检查 Gitea 审计日志 +- **及时撤销未使用的令牌**: 删除不再需要的令牌 + +### 2. 错误处理和可靠性 + +#### 健壮的脚本编写 +```bash +# 示例:带有错误处理的 API 调用函数 +call_gitea_api() { + local endpoint=$1 + local method=${2:-GET} + local data=${3:-} + + local response + local http_code + + response=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -X "$method" \ + ${data:+-d "$data"} \ + "$GITEA_URL/api/v1/$endpoint" 2>/dev/null || echo "CURL_ERROR") + + http_code=$(echo "$response" | tail -n1) + local response_body=$(echo "$response" | sed '$d') + + if [[ "$http_code" =~ ^[23] ]]; then + echo "$response_body" + return 0 + else + echo "API 错误 ($http_code): $response_body" >&2 + return 1 + fi +} +``` + +#### 重试机制 +```bash +# 示例:带有重试的 API 调用 +retry_api_call() { + local max_retries=3 + local retry_delay=2 + local attempt=1 + + while [[ $attempt -le $max_retries ]]; do + if call_gitea_api "$@"; then + return 0 + fi + + if [[ $attempt -lt $max_retries ]]; then + echo "第 $attempt 次尝试失败,${retry_delay}秒后重试..." + sleep $retry_delay + ((retry_delay *= 2)) # 指数退避 + fi + + ((attempt++)) + done + + echo "达到最大重试次数 ($max_retries),操作失败" >&2 + return 1 +} +``` + +### 3. 性能优化 + +#### 批量操作 +```bash +# 避免多次 API 调用,使用批量处理 +# 错误示例:每个仓库单独调用 API +for repo in $(list_repos); do + get_repo_info "$repo" +done + +# 正确示例:一次获取所有仓库信息 +all_repos=$(curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/user/repos?limit=100") +``` + +#### 缓存策略 +```bash +# 使用本地缓存减少 API 调用 +cache_file="/tmp/gitea_repos_$(echo -n $GITEA_URL | md5sum | cut -d' ' -f1).json" +cache_age=300 # 5分钟 + +if [[ -f "$cache_file" ]] && \ + [[ $(($(date +%s) - $(stat -c %Y "$cache_file"))) -lt $cache_age ]]; then + # 使用缓存 + repos=$(cat "$cache_file") +else + # 调用 API 并更新缓存 + repos=$(call_gitea_api "user/repos?limit=100") + echo "$repos" > "$cache_file" +fi +``` + +#### 合理设置超时 +```bash +# 根据操作类型设置不同的超时时间 +export CURL_TIMEOUT=30 # 常规操作 +export CURL_LONG_TIMEOUT=120 # 长时间操作(如创建仓库) + +# 在 curl 命令中使用 +curl --max-time $CURL_TIMEOUT ... +``` + +### 4. 代码质量和可维护性 + +#### 模块化设计 +```bash +# 将功能拆分为独立的函数或脚本 +# gitea-functions.sh +source "$(dirname "$0")/gitea-config.sh" +source "$(dirname "$0")/gitea-repos.sh" +source "$(dirname "$0")/gitea-pr.sh" +``` + +#### 标准化输出格式 +```bash +# 使用一致的输出格式 +log_info() { echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $*"; } +log_error() { echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2; } +log_success() { echo "[SUCCESS] $(date '+%Y-%m-%d %H:%M:%S') $*"; } +``` + +#### 文档和注释 +```bash +#!/bin/bash +# 脚本: gitea-repo-manager.sh +# 作者: Your Name +# 描述: Gitea 仓库管理工具 +# 版本: 1.0.0 +# 用法: ./gitea-repo-manager.sh <命令> [参数] +``` + +### 5. 自动化工作流 + +#### Git Hooks 集成 +```bash +# 在 .git/hooks/pre-push 中添加 Gitea 集成 +#!/bin/bash +# 在推送前检查 Gitea 上是否存在对应的 PR +remote="$1" +url="$2" + +if [[ "$url" == *"gitea"* ]]; then + # 执行 Gitea 相关检查 + check_gitea_pr_status +fi +``` + +#### CI/CD 管道集成 +```yaml +# .gitea/workflows/ci.yml +name: CI Pipeline +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Run tests + run: | + # 使用 Gitea API 更新状态 + update_gitea_status "pending" "测试进行中" + # 运行测试 + ./run-tests.sh + update_gitea_status "success" "测试通过" +``` + +### 6. 监控和日志 + +#### 操作日志记录 +```bash +# 记录所有重要操作 +log_operation() { + local operation=$1 + local status=$2 + local details=$3 + + echo "$(date '+%Y-%m-%d %H:%M:%S') | $operation | $status | $details" \ + >> "/var/log/gitea-actor.log" + + # 可选:发送到远程日志系统 + send_to_logging_service "$operation" "$status" "$details" +} +``` + +#### 性能监控 +```bash +# 监控 API 响应时间 +monitor_api_performance() { + local endpoint=$1 + local start_time=$(date +%s%N) + + call_gitea_api "$endpoint" > /dev/null + + local end_time=$(date +%s%N) + local duration=$((($end_time - $start_time) / 1000000)) + + echo "API $endpoint 响应时间: ${duration}ms" + + if [[ $duration -gt 5000 ]]; then + log_warning "API 响应缓慢: $endpoint (${duration}ms)" + fi +} +``` + +## API 参考 + +### 核心 API 端点 + +#### 用户相关 +| 端点 | 方法 | 描述 | 参数 | +|------|------|------|------| +| `/api/v1/user` | GET | 获取当前用户信息 | - | +| `/api/v1/users/{username}` | GET | 获取指定用户信息 | - | +| `/api/v1/user/repos` | GET | 列出用户仓库 | `page`, `limit` | +| `/api/v1/user/repos` | POST | 创建仓库 | `name`, `description`, `private`, `auto_init` | +| `/api/v1/user/teams` | GET | 列出用户团队 | - | + +#### 仓库相关 +| 端点 | 方法 | 描述 | 参数 | +|------|------|------|------| +| `/api/v1/repos/{owner}/{repo}` | GET | 获取仓库信息 | - | +| `/api/v1/repos/{owner}/{repo}` | DELETE | 删除仓库 | - | +| `/api/v1/repos/{owner}/{repo}/contents` | GET | 获取文件内容 | `ref` | +| `/api/v1/repos/{owner}/{repo}/contents/{filepath}` | PUT | 创建/更新文件 | `content`, `message`, `branch` | +| `/api/v1/repos/{owner}/{repo}/branches` | GET | 列出分支 | - | +| `/api/v1/repos/{owner}/{repo}/branches` | POST | 创建分支 | `new_branch_name`, `old_branch_name` | +| `/api/v1/repos/{owner}/{repo}/tags` | GET | 列出标签 | - | +| `/api/v1/repos/{owner}/{repo}/tags` | POST | 创建标签 | `tag_name`, `message`, `target` | + +#### Pull Request 相关 +| 端点 | 方法 | 描述 | 参数 | +|------|------|------|------| +| `/api/v1/repos/{owner}/{repo}/pulls` | GET | 列出 PRs | `state`, `page`, `limit` | +| `/api/v1/repos/{owner}/{repo}/pulls` | POST | 创建 PR | `title`, `body`, `head`, `base` | +| `/api/v1/repos/{owner}/{repo}/pulls/{index}` | GET | 获取 PR 详情 | - | +| `/api/v1/repos/{owner}/{repo}/pulls/{index}/merge` | POST | 合并 PR | `Do` | +| `/api/v1/repos/{owner}/{repo}/pulls/{index}/comments` | GET | 获取 PR 评论 | - | +| `/api/v1/repos/{owner}/{repo}/pulls/{index}/comments` | POST | 添加 PR 评论 | `body`, `path`, `line` | + +#### Issue 相关 +| 端点 | 方法 | 描述 | 参数 | +|------|------|------|------| +| `/api/v1/repos/{owner}/{repo}/issues` | GET | 列出 Issues | `state`, `labels`, `page` | +| `/api/v1/repos/{owner}/{repo}/issues` | POST | 创建 Issue | `title`, `body`, `labels`, `assignees` | +| `/api/v1/repos/{owner}/{repo}/issues/{index}` | GET | 获取 Issue 详情 | - | +| `/api/v1/repos/{owner}/{repo}/issues/{index}` | PATCH | 更新 Issue | `title`, `body`, `state`, `labels` | +| `/api/v1/repos/{owner}/{repo}/issues/{index}/comments` | GET | 获取 Issue 评论 | - | +| `/api/v1/repos/{owner}/{repo}/issues/{index}/comments` | POST | 添加 Issue 评论 | `body` | + +#### 搜索相关 +| 端点 | 方法 | 描述 | 参数 | +|------|------|------|------| +| `/api/v1/repos/search` | GET | 搜索仓库 | `q`, `limit`, `page` | +| `/api/v1/users/search` | GET | 搜索用户 | `q`, `limit`, `page` | + +### 请求和响应格式 + +#### 认证头 +```bash +# 使用个人访问令牌 +Authorization: token {personal_access_token} + +# 或使用基本认证(不推荐) +Authorization: Basic $(echo -n "username:password" | base64) +``` + +#### 请求头 +```bash +# 常用请求头 +Content-Type: application/json +Accept: application/json +User-Agent: Gitea-Actor/1.0.0 + +# 分页请求头 +Link: ; rel="next" +``` + +#### 响应格式 +```json +{ + "id": 1, + "name": "example", + "full_name": "owner/example", + "description": "示例仓库", + "private": false, + "html_url": "https://gitea.com/owner/example", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-02T00:00:00Z" +} +``` + +#### 错误响应 +```json +{ + "message": "令牌无效", + "documentation_url": "https://docs.gitea.com/api" +} +``` + +### 分页和限制 + +#### 分页参数 +```bash +# 基本分页 +curl -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/user/repos?page=1&limit=20" + +# 处理分页响应 +while true; do + response=$(call_gitea_api "user/repos?page=$page&limit=20") + # 处理数据... + + # 检查是否有下一页 + next_link=$(echo "$response" | grep -i 'rel="next"' || true) + if [[ -z "$next_link" ]]; then + break + fi + ((page++)) +done +``` + +#### 速率限制 +Gitea API 通常有速率限制,可以通过响应头查看: + +```bash +# 检查速率限制头 +curl -s -I -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/user" | grep -i "rate" + +# 常见速率限制头 +X-RateLimit-Limit: 60 # 每分钟最大请求数 +X-RateLimit-Remaining: 58 # 剩余请求数 +X-RateLimit-Reset: 1625097600 # 重置时间戳 +``` + +### 实用 API 示例 + +#### 创建仓库并初始化文件 +```bash +# 1. 创建仓库 +repo_data=$(jq -n \ + --arg name "my-repo" \ + --arg desc "我的仓库" \ + '{name: $name, description: $desc, auto_init: true}') + +repo=$(curl -s -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$repo_data" \ + "$GITEA_URL/api/v1/user/repos") + +# 2. 添加初始文件 +readme_data=$(jq -n \ + --arg content "# 我的项目\n\n这是一个示例项目。" \ + --arg message "添加 README" \ + --arg branch "main" \ + '{content: $content | @base64, message: $message, branch: $branch}') + +curl -s -X PUT -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$readme_data" \ + "$GITEA_URL/api/v1/repos/$(echo $repo | jq -r '.owner.login')/$(echo $repo | jq -r '.name')/contents/README.md" +``` + +#### 批量操作示例 +```bash +# 批量创建 Issues +issues=( + '{"title": "Bug: 修复登录问题", "body": "用户无法登录", "labels": ["bug"]}' + '{"title": "功能: 添加搜索", "body": "需要全文搜索功能", "labels": ["enhancement"]}' + '{"title": "文档: 更新 API 文档", "body": "API 文档需要更新", "labels": ["documentation"]}' +) + +for issue_data in "${issues[@]}"; do + curl -s -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$issue_data" \ + "$GITEA_URL/api/v1/repos/owner/repo/issues" > /dev/null + echo "创建 Issue: $(echo $issue_data | jq -r '.title')" + sleep 1 # 避免速率限制 +done +``` + +### API 版本和兼容性 + +#### 检查 API 版本 +```bash +# 获取 Gitea 版本信息 +curl -s "$GITEA_URL/api/v1/version" | jq . + +# 响应示例 +{ + "version": "1.20.0", + "built": "2024-01-01T00:00:00Z" +} +``` + +#### API 兼容性提示 +- Gitea 1.15+ 支持完整的 API v1 +- 某些端点可能需要特定版本 +- 建议使用最新稳定版 Gitea +- 定期检查 [Gitea API 文档](https://docs.gitea.com/api) 获取更新 + +## 高级功能和集成 + +### 1. Webhook 自动化 + +#### Webhook 配置 +```bash +# 创建 Webhook +webhook_data=$(jq -n \ + --arg url "https://your-webhook-server.com/webhook" \ + --argjson active true \ + '{ + type: "gitea", + config: {url: $url, content_type: "json"}, + events: ["push", "pull_request", "issue", "release"], + active: $active + }') + +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$webhook_data" \ + "$GITEA_URL/api/v1/repos/owner/repo/hooks" +``` + +#### Webhook 处理器示例 +```python +#!/usr/bin/env python3 +# webhook-server.py +from flask import Flask, request, jsonify +import hmac +import hashlib + +app = Flask(__name__) +WEBHOOK_SECRET = "your_secret_here" + +@app.route('/webhook', methods=['POST']) +def handle_webhook(): + # 验证签名 + signature = request.headers.get('X-Gitea-Signature') + payload = request.get_data() + + expected = hmac.new( + WEBHOOK_SECRET.encode(), + payload, + hashlib.sha256 + ).hexdigest() + + if not hmac.compare_digest(signature, expected): + return jsonify({"error": "Invalid signature"}), 401 + + event = request.headers.get('X-Gitea-Event') + payload = request.json + + # 处理不同事件 + if event == 'push': + handle_push_event(payload) + elif event == 'pull_request': + handle_pr_event(payload) + elif event == 'issue': + handle_issue_event(payload) + + return jsonify({"status": "ok"}), 200 + +def handle_push_event(payload): + print(f"Push event: {payload['ref']}") + # 触发 CI/CD 或其他自动化 + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000) +``` + +### 2. CI/CD 流水线集成 + +#### Gitea Actions +```yaml +# .gitea/workflows/main.yml +name: CI Pipeline +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Install dependencies + run: pip install -r requirements.txt + + - name: Run tests + run: pytest + + - name: Notify Gitea + run: | + # 使用 API 更新 PR 状态 + curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "state": "success", + "target_url": "https://ci.example.com/build/123", + "description": "测试通过", + "context": "CI/tests" + }' \ + "$GITEA_URL/api/v1/repos/${{ github.repository }}/statuses/${{ github.sha }}" +``` + +#### 外部 CI 集成 +```bash +# 在 CI 脚本中使用 Gitea API +update_build_status() { + local state=$1 # pending, success, failure, error + local description=$2 + local context=${3:-"CI/build"} + + curl -s -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg state "$state" \ + --arg desc "$description" \ + --arg context "$context" \ + --arg url "$CI_PIPELINE_URL" \ + '{state: $state, description: $desc, context: $context, target_url: $url}')" \ + "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/statuses/$COMMIT_SHA" +} + +# 在 CI 阶段调用 +update_build_status "pending" "构建进行中" +# ... 执行构建 ... +update_build_status "success" "构建成功" +``` + +### 3. 团队和权限管理 + +#### 团队管理 +```bash +# 创建团队 +team_data=$(jq -n \ + --arg name "developers" \ + --arg desc "开发团队" \ + --argjson permission "write" \ + '{name: $name, description: $desc, permission: $permission, includes_all_repositories: false}') + +curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$team_data" \ + "$GITEA_URL/api/v1/orgs/org-name/teams" + +# 添加团队成员 +curl -X PUT -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/teams/{team-id}/members/{username}" +``` + +#### 仓库权限管理 +```bash +# 添加协作者 +curl -X PUT -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/repos/owner/repo/collaborators/username" + +# 设置协作者权限 +collab_data=$(jq -n \ + --argjson permission "write" \ + '{permission: $permission}') + +curl -X PUT -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$collab_data" \ + "$GITEA_URL/api/v1/repos/owner/repo/collaborators/username" +``` + +### 4. 高级搜索和报告 + +#### 高级搜索 +```bash +# 搜索代码 +search_query="TODO in:file language:go" +curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/repos/owner/repo/search?q=${search_query// /%20}" | jq . + +# 搜索 Issues +issues_query="is:open label:bug assignee:user1" +curl -s -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/repos/owner/repo/issues?q=${issues_query// /%20}" | jq . +``` + +#### 生成报告 +```bash +# 生成仓库报告 +generate_repo_report() { + local owner=$1 + local repo=$2 + + echo "# 仓库报告: $owner/$repo" + echo "生成时间: $(date)" + echo "" + + # 获取仓库信息 + repo_info=$(call_gitea_api "repos/$owner/$repo") + echo "## 基本信息" + echo "- 描述: $(echo $repo_info | jq -r '.description')" + echo "- 创建时间: $(echo $repo_info | jq -r '.created_at')" + echo "- 星标数: $(echo $repo_info | jq -r '.stars_count')" + echo "" + + # 获取开放 Issues + echo "## 开放 Issues" + issues=$(call_gitea_api "repos/$owner/$repo/issues?state=open") + echo "$issues" | jq -r '.[] | "- #\(.number): \(.title) (\(.user.login))"' + echo "" + + # 获取开放 PRs + echo "## 开放 Pull Requests" + prs=$(call_gitea_api "repos/$owner/$repo/pulls?state=open") + echo "$prs" | jq -r '.[] | "- #\(.number): \(.title) (\(.user.login))"' +} + +# 保存报告 +generate_repo_report "owner" "repo" > repo-report.md +``` + +### 5. Git 操作自动化 + +#### 自动同步分支 +```bash +#!/bin/bash +# sync-branches.sh +# 自动同步所有仓库的分支 + +for repo in $(list_user_repos | jq -r '.full_name'); do + echo "同步仓库: $repo" + + # 克隆仓库 + git clone "$GITEA_URL/$repo.git" temp-repo + cd temp-repo + + # 获取远程分支 + git fetch origin + + # 同步所有分支 + for branch in $(git branch -r | grep -v '\->'); do + branch_name=${branch#origin/} + git checkout -b "$branch_name" "$branch" 2>/dev/null || git checkout "$branch_name" + git pull origin "$branch_name" + done + + # 清理 + cd .. + rm -rf temp-repo +done +``` + +#### 批量仓库操作 +```bash +# 批量更新仓库描述 +update_repo_descriptions() { + local pattern=$1 + local new_description=$2 + + repos=$(call_gitea_api "user/repos") + + echo "$repos" | jq -r ".[] | select(.name | test(\"$pattern\")) | .full_name" | while read repo; do + owner=$(echo "$repo" | cut -d/ -f1) + name=$(echo "$repo" | cut -d/ -f2) + + update_data=$(jq -n \ + --arg desc "$new_description" \ + '{description: $desc}') + + echo "更新: $repo" + curl -X PATCH -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$update_data" \ + "$GITEA_URL/api/v1/repos/$owner/$name" + done +} +``` + +### 6. 监控和告警 + +#### 监控脚本 +```bash +#!/bin/bash +# monitor-gitea.sh +# 监控 Gitea 实例健康状态 + +check_gitea_health() { + # 检查 API 响应时间 + start_time=$(date +%s%N) + response=$(curl -s -w "\n%{http_code}" -H "Authorization: token $GITEA_TOKEN" \ + "$GITEA_URL/api/v1/user") + end_time=$(date +%s%N) + + response_time=$((($end_time - $start_time) / 1000000)) + http_code=$(echo "$response" | tail -n1) + + if [[ "$http_code" != "200" ]]; then + echo "警报: Gitea API 返回 $http_code (响应时间: ${response_time}ms)" + send_alert "Gitea API 异常" "HTTP $http_code" + elif [[ $response_time -gt 1000 ]]; then + echo "警告: API 响应缓慢 (${response_time}ms)" + fi + + # 检查磁盘空间 + disk_usage=$(ssh gitea-server "df -h /var/lib/gitea | tail -1") + echo "磁盘使用: $disk_usage" + + # 检查服务状态 + service_status=$(ssh gitea-server "systemctl is-active gitea") + if [[ "$service_status" != "active" ]]; then + echo "警报: Gitea 服务状态: $service_status" + send_alert "Gitea 服务异常" "状态: $service_status" + fi +} + +# 定期检查 +while true; do + check_gitea_health + sleep 300 # 5分钟 +done +``` + +### 7. 备份和迁移 + +#### 仓库备份 +```bash +#!/bin/bash +# backup-gitea-repos.sh +# 备份所有仓库 + +BACKUP_DIR="/backup/gitea/repos" +mkdir -p "$BACKUP_DIR" + +# 备份所有仓库 +for repo in $(list_user_repos | jq -r '.full_name'); do + echo "备份: $repo" + + # 使用 git bundle 创建备份 + git clone --mirror "$GITEA_URL/$repo.git" "$BACKUP_DIR/$repo.git" + + # 创建压缩包 + tar -czf "$BACKUP_DIR/${repo//\//-}.$(date +%Y%m%d).tar.gz" -C "$BACKUP_DIR" "$repo.git" + rm -rf "$BACKUP_DIR/$repo.git" +done + +# 备份到远程 +rsync -avz "$BACKUP_DIR/" "backup-server:/gitea-backups/" +``` + +#### 迁移工具 +```bash +# 迁移仓库到新实例 +migrate_repo() { + local source_url=$1 + local target_url=$2 + local repo_name=$3 + + echo "迁移: $repo_name" + + # 克隆源仓库 + git clone --mirror "$source_url/$repo_name.git" + cd "$(basename "$repo_name.git")" + + # 推送到目标 + git push --mirror "$target_url/$repo_name.git" + + cd .. + rm -rf "$(basename "$repo_name.git")" +} +``` + +### 8. 集成第三方服务 + +#### 集成 Slack +```bash +# 发送通知到 Slack +send_to_slack() { + local message=$1 + local channel=${2:-"#gitea-notifications"} + + curl -X POST -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg text "$message" \ + --arg channel "$channel" \ + '{text: $text, channel: $channel}')" \ + "https://hooks.slack.com/services/YOUR/WEBHOOK/URL" +} + +# 在 Webhook 处理器中使用 +handle_pr_event() { + local payload=$1 + local action=$(echo "$payload" | jq -r '.action') + local pr_number=$(echo "$payload" | jq -r '.number') + local title=$(echo "$payload" | jq -r '.pull_request.title') + + message="PR #$pr_number $action: $title" + send_to_slack "$message" +} +``` + +#### 集成 JIRA +```bash +# 同步 Issue 到 JIRA +sync_to_jira() { + local issue_data=$1 + + local title=$(echo "$issue_data" | jq -r '.title') + local body=$(echo "$issue_data" | jq -r '.body') + local labels=$(echo "$issue_data" | jq -r '.labels[]?.name' | tr '\n' ',') + + # 创建 JIRA Issue + jira_data=$(jq -n \ + --arg title "$title" \ + --arg desc "$body" \ + --arg labels "$labels" \ + '{ + fields: { + project: {key: "PROJ"}, + summary: $title, + description: $desc, + issuetype: {name: "Task"}, + labels: ($labels | split(",") | map(select(. != ""))) + } + }') + + curl -X POST -u "user:token" \ + -H "Content-Type: application/json" \ + -d "$jira_data" \ + "https://jira.example.com/rest/api/2/issue" +} +``` diff --git a/skills/gitea-actor/scripts/api-examples.sh b/skills/gitea-actor/scripts/api-examples.sh new file mode 100755 index 0000000..177b47e --- /dev/null +++ b/skills/gitea-actor/scripts/api-examples.sh @@ -0,0 +1,290 @@ +#!/bin/bash + +# Gitea API 示例脚本 +# 包含常用 Gitea API 操作的函数示例 + +set -e + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# 检查环境变量 +check_env() { + if [[ -z "$GITEA_URL" || -z "$GITEA_TOKEN" ]]; then + echo -e "${RED}[ERROR]${NC} 请先设置 Gitea 环境变量" + echo " 运行: source ~/.gitea-actor.env" + echo " 或设置: export GITEA_URL=..., export GITEA_TOKEN=..." + return 1 + fi +} + +# 通用 API 请求函数 +gitea_api() { + local method=$1 + local endpoint=$2 + local data=$3 + + local url="${GITEA_URL}/api/v1/${endpoint}" + local curl_cmd="curl -s -H 'Authorization: token $GITEA_TOKEN' -H 'Accept: application/json'" + + if [[ -n "$data" ]]; then + curl_cmd="$curl_cmd -H 'Content-Type: application/json' -d '$data'" + fi + + curl_cmd="$curl_cmd -X $method '$url'" + + eval "$curl_cmd" +} + +# 1. 用户相关操作 +get_current_user() { + check_env || return 1 + echo -e "${BLUE}[INFO]${NC} 获取当前用户信息..." + gitea_api "GET" "user" | jq . +} + +list_user_repos() { + check_env || return 1 + local page=${1:-1} + local limit=${2:-20} + echo -e "${BLUE}[INFO]${NC} 列出用户仓库 (第 $page 页, 每页 $limit 个)..." + gitea_api "GET" "user/repos?page=$page&limit=$limit" | jq '.[] | {id, full_name, description, private, html_url}' +} + +# 2. 仓库操作 +create_repository() { + check_env || return 1 + local name=$1 + local description=${2:-""} + local private=${3:-false} + + if [[ -z "$name" ]]; then + echo -e "${YELLOW}[USAGE]${NC} create_repository [description] [private(true/false)]" + return 1 + fi + + local data=$(jq -n \ + --arg name "$name" \ + --arg desc "$description" \ + --argjson private "$private" \ + '{name: $name, description: $desc, private: $private, auto_init: true}') + + echo -e "${BLUE}[INFO]${NC} 创建仓库: $name" + gitea_api "POST" "user/repos" "$data" | jq . +} + +get_repository() { + check_env || return 1 + local owner=$1 + local repo=$2 + + if [[ -z "$owner" || -z "$repo" ]]; then + echo -e "${YELLOW}[USAGE]${NC} get_repository " + return 1 + fi + + echo -e "${BLUE}[INFO]${NC} 获取仓库信息: $owner/$repo" + gitea_api "GET" "repos/$owner/$repo" | jq . +} + +# 3. Pull Request 操作 +create_pull_request() { + check_env || return 1 + local owner=$1 + local repo=$2 + local title=$3 + local body=$4 + local head=$5 + local base=${6:-"main"} + + if [[ -z "$owner" || -z "$repo" || -z "$title" || -z "$head" ]]; then + echo -e "${YELLOW}[USAGE]${NC} create_pull_request <body> <head> [base]" + return 1 + fi + + local data=$(jq -n \ + --arg title "$title" \ + --arg body "$body" \ + --arg head "$head" \ + --arg base "$base" \ + '{title: $title, body: $body, head: $head, base: $base}') + + echo -e "${BLUE}[INFO]${NC} 创建 Pull Request: $title" + gitea_api "POST" "repos/$owner/$repo/pulls" "$data" | jq . +} + +list_pull_requests() { + check_env || return 1 + local owner=$1 + local repo=$2 + local state=${3:-"open"} + + if [[ -z "$owner" || -z "$repo" ]]; then + echo -e "${YELLOW}[USAGE]${NC} list_pull_requests <owner> <repo> [state(open/closed/all)]" + return 1 + fi + + echo -e "${BLUE}[INFO]${NC} 列出 Pull Requests: $owner/$repo (状态: $state)" + gitea_api "GET" "repos/$owner/$repo/pulls?state=$state" | jq '.[] | {number, title, state, user: .user.login, created_at}' +} + +merge_pull_request() { + check_env || return 1 + local owner=$1 + local repo=$2 + local pr_number=$3 + + if [[ -z "$owner" || -z "$repo" || -z "$pr_number" ]]; then + echo -e "${YELLOW}[USAGE]${NC} merge_pull_request <owner> <repo> <pr-number>" + return 1 + fi + + local data='{"Do":"merge"}' + echo -e "${BLUE}[INFO]${NC} 合并 Pull Request: #$pr_number" + gitea_api "POST" "repos/$owner/$repo/pulls/$pr_number/merge" "$data" | jq . +} + +# 4. Issue 操作 +create_issue() { + check_env || return 1 + local owner=$1 + local repo=$2 + local title=$3 + local body=$4 + local labels=$5 + + if [[ -z "$owner" || -z "$repo" || -z "$title" ]]; then + echo -e "${YELLOW}[USAGE]${NC} create_issue <owner> <repo> <title> [body] [labels逗号分隔]" + return 1 + fi + + local data="{\"title\":\"$title\"" + + if [[ -n "$body" ]]; then + data="$data, \"body\":\"$body\"" + fi + + if [[ -n "$labels" ]]; then + IFS=',' read -ra label_array <<< "$labels" + local labels_json=$(printf '"%s",' "${label_array[@]}") + labels_json="[${labels_json%,}]" + data="$data, \"labels\":$labels_json" + fi + + data="$data}" + + echo -e "${BLUE}[INFO]${NC} 创建 Issue: $title" + gitea_api "POST" "repos/$owner/$repo/issues" "$data" | jq . +} + +# 5. 分支和标签 +create_branch() { + check_env || return 1 + local owner=$1 + local repo=$2 + local new_branch=$3 + local from_branch=${4:-"main"} + + if [[ -z "$owner" || -z "$repo" || -z "$new_branch" ]]; then + echo -e "${YELLOW}[USAGE]${NC} create_branch <owner> <repo> <new-branch> [from-branch]" + return 1 + fi + + local data=$(jq -n \ + --arg new_branch "$new_branch" \ + --arg old_branch "$from_branch" \ + '{new_branch_name: $new_branch, old_branch_name: $old_branch}') + + echo -e "${BLUE}[INFO]${NC} 创建分支: $new_branch (从 $from_branch)" + gitea_api "POST" "repos/$owner/$repo/branches" "$data" | jq . +} + +create_tag() { + check_env || return 1 + local owner=$1 + local repo=$2 + local tag_name=$3 + local message=${4:-"Release $tag_name"} + local target=${5:-"main"} + + if [[ -z "$owner" || -z "$repo" || -z "$tag_name" ]]; then + echo -e "${YELLOW}[USAGE]${NC} create_tag <owner> <repo> <tag-name> [message] [target-branch]" + return 1 + fi + + local data=$(jq -n \ + --arg tag_name "$tag_name" \ + --arg message "$message" \ + --arg target "$target" \ + '{tag_name: $tag_name, message: $message, target: $target}') + + echo -e "${BLUE}[INFO]${NC} 创建标签: $tag_name" + gitea_api "POST" "repos/$owner/$repo/tags" "$data" | jq . +} + +# 6. 搜索操作 +search_repositories() { + check_env || return 1 + local query=$1 + local limit=${2:-10} + + if [[ -z "$query" ]]; then + echo -e "${YELLOW}[USAGE]${NC} search_repositories <query> [limit]" + return 1 + fi + + echo -e "${BLUE}[INFO]${NC} 搜索仓库: $query" + gitea_api "GET" "repos/search?q=$query&limit=$limit" | jq '.data[] | {full_name, description, stars_count, forks_count}' +} + +# 显示帮助信息 +show_help() { + echo -e "${GREEN}Gitea API 示例脚本${NC}" + echo "" + echo -e "${BLUE}使用方法:${NC}" + echo " source api-examples.sh # 加载函数到当前shell" + echo " 然后调用任意函数" + echo "" + echo -e "${BLUE}可用函数:${NC}" + echo " 用户操作:" + echo " get_current_user" + echo " list_user_repos [page] [limit]" + echo "" + echo " 仓库操作:" + echo " create_repository <name> [description] [private]" + echo " get_repository <owner> <repo>" + echo "" + echo " Pull Request 操作:" + echo " create_pull_request <owner> <repo> <title> <body> <head> [base]" + echo " list_pull_requests <owner> <repo> [state]" + echo " merge_pull_request <owner> <repo> <pr-number>" + echo "" + echo " Issue 操作:" + echo " create_issue <owner> <repo> <title> [body] [labels]" + echo "" + echo " 分支和标签:" + echo " create_branch <owner> <repo> <new-branch> [from-branch]" + echo " create_tag <owner> <repo> <tag-name> [message] [target]" + echo "" + echo " 搜索操作:" + echo " search_repositories <query> [limit]" + echo "" + echo -e "${YELLOW}示例:${NC}" + echo " # 获取当前用户信息" + echo " get_current_user" + echo "" + echo " # 创建新仓库" + echo " create_repository \"my-new-repo\" \"My description\" false" + echo "" + echo " # 创建 Pull Request" + echo " create_pull_request \"owner\" \"repo\" \"添加新功能\" \"详细描述\" \"feature-branch\" \"main\"" +} + +# 如果直接运行脚本,显示帮助 +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + show_help +fi \ No newline at end of file diff --git a/skills/gitea-actor/scripts/gitea-cli.py b/skills/gitea-actor/scripts/gitea-cli.py new file mode 100755 index 0000000..010ee06 --- /dev/null +++ b/skills/gitea-actor/scripts/gitea-cli.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python3 +""" +Gitea CLI 工具 +提供命令行界面访问 Gitea API +""" + +import os +import sys +import json +import argparse +import requests +from typing import Optional, Dict, Any, List + + +class GiteaClient: + """Gitea API 客户端""" + + def __init__(self, url: str = None, token: str = None): + self.url = url or os.environ.get("GITEA_URL", "https://gitea.com") + self.token = token or os.environ.get("GITEA_TOKEN") + + if not self.token: + print("错误: 未设置 GITEA_TOKEN 环境变量") + print("请设置: export GITEA_TOKEN='your_token_here'") + sys.exit(1) + + # 移除URL末尾的斜杠 + self.url = self.url.rstrip("/") + self.session = requests.Session() + self.session.headers.update( + { + "Authorization": f"token {self.token}", + "Accept": "application/json", + "Content-Type": "application/json", + } + ) + + def _request(self, method: str, endpoint: str, data: Dict = None) -> Dict: + """发送API请求""" + url = f"{self.url}/api/v1/{endpoint}" + + try: + if method.upper() == "GET": + response = self.session.get(url, params=data) + elif method.upper() == "POST": + response = self.session.post(url, json=data) + elif method.upper() == "PUT": + response = self.session.put(url, json=data) + elif method.upper() == "PATCH": + response = self.session.patch(url, json=data) + elif method.upper() == "DELETE": + response = self.session.delete(url) + else: + raise ValueError(f"不支持的HTTP方法: {method}") + + response.raise_for_status() + + # 对于204 No Content响应,返回空字典 + if response.status_code == 204: + return {} + + return response.json() + + except requests.exceptions.RequestException as e: + print(f"API请求失败: {e}") + if hasattr(e, "response") and e.response is not None: + print(f"状态码: {e.response.status_code}") + try: + error_data = e.response.json() + print(f"错误信息: {error_data.get('message', '未知错误')}") + except: + print(f"响应内容: {e.response.text[:200]}") + sys.exit(1) + + def get_current_user(self) -> Dict: + """获取当前用户信息""" + return self._request("GET", "user") + + def list_repos(self, page: int = 1, limit: int = 20) -> List[Dict]: + """列出用户仓库""" + params = {"page": page, "limit": limit} + return self._request("GET", "user/repos", params) + + def create_repo( + self, name: str, description: str = "", private: bool = False + ) -> Dict: + """创建仓库""" + data = { + "name": name, + "description": description, + "private": private, + "auto_init": True, + } + return self._request("POST", "user/repos", data) + + def get_repo(self, owner: str, repo: str) -> Dict: + """获取仓库信息""" + return self._request("GET", f"repos/{owner}/{repo}") + + def create_pull_request( + self, + owner: str, + repo: str, + title: str, + body: str, + head: str, + base: str = "main", + ) -> Dict: + """创建Pull Request""" + data = {"title": title, "body": body, "head": head, "base": base} + return self._request("POST", f"repos/{owner}/{repo}/pulls", data) + + def list_pull_requests( + self, owner: str, repo: str, state: str = "open" + ) -> List[Dict]: + """列出Pull Requests""" + params = {"state": state} + return self._request("GET", f"repos/{owner}/{repo}/pulls", params) + + def merge_pull_request(self, owner: str, repo: str, pr_number: int) -> Dict: + """合并Pull Request""" + data = {"Do": "merge"} + return self._request( + "POST", f"repos/{owner}/{repo}/pulls/{pr_number}/merge", data + ) + + def create_issue( + self, + owner: str, + repo: str, + title: str, + body: str = "", + labels: List[str] = None, + ) -> Dict: + """创建Issue""" + data = {"title": title} + if body: + data["body"] = body + if labels: + data["labels"] = labels + return self._request("POST", f"repos/{owner}/{repo}/issues", data) + + def create_branch( + self, owner: str, repo: str, new_branch: str, from_branch: str = "main" + ) -> Dict: + """创建分支""" + data = {"new_branch_name": new_branch, "old_branch_name": from_branch} + return self._request("POST", f"repos/{owner}/{repo}/branches", data) + + def create_tag( + self, + owner: str, + repo: str, + tag_name: str, + message: str = "", + target: str = "main", + ) -> Dict: + """创建标签""" + data = { + "tag_name": tag_name, + "message": message or f"Release {tag_name}", + "target": target, + } + return self._request("POST", f"repos/{owner}/{repo}/tags", data) + + def search_repos(self, query: str, limit: int = 10) -> Dict: + """搜索仓库""" + params = {"q": query, "limit": limit} + return self._request("GET", "repos/search", params) + + +def main(): + parser = argparse.ArgumentParser(description="Gitea CLI 工具") + subparsers = parser.add_subparsers(dest="command", help="可用命令") + + # 用户命令 + user_parser = subparsers.add_parser("user", help="用户相关操作") + user_parser.add_argument( + "action", choices=["info", "repos"], help="info: 获取用户信息, repos: 列出仓库" + ) + user_parser.add_argument("--page", type=int, default=1, help="页码") + user_parser.add_argument("--limit", type=int, default=20, help="每页数量") + + # 仓库命令 + repo_parser = subparsers.add_parser("repo", help="仓库相关操作") + repo_parser.add_argument("action", choices=["create", "get", "search"]) + repo_parser.add_argument("--name", help="仓库名称 (create时使用)") + repo_parser.add_argument("--description", default="", help="仓库描述") + repo_parser.add_argument("--private", action="store_true", help="是否私有") + repo_parser.add_argument("--owner", help="仓库所有者 (get/search时使用)") + repo_parser.add_argument("--repo", help="仓库名称 (get时使用)") + repo_parser.add_argument("--query", help="搜索关键词") + repo_parser.add_argument("--limit", type=int, default=10, help="搜索结果数量") + + # PR命令 + pr_parser = subparsers.add_parser("pr", help="Pull Request 相关操作") + pr_parser.add_argument("action", choices=["create", "list", "merge"]) + pr_parser.add_argument("--owner", required=True, help="仓库所有者") + pr_parser.add_argument("--repo", required=True, help="仓库名称") + pr_parser.add_argument("--title", help="PR标题 (create时使用)") + pr_parser.add_argument("--body", default="", help="PR描述") + pr_parser.add_argument("--head", help="源分支") + pr_parser.add_argument("--base", default="main", help="目标分支") + pr_parser.add_argument("--number", type=int, help="PR编号 (merge时使用)") + pr_parser.add_argument( + "--state", + default="open", + choices=["open", "closed", "all"], + help="PR状态 (list时使用)", + ) + + # Issue命令 + issue_parser = subparsers.add_parser("issue", help="Issue 相关操作") + issue_parser.add_argument("action", choices=["create"]) + issue_parser.add_argument("--owner", required=True, help="仓库所有者") + issue_parser.add_argument("--repo", required=True, help="仓库名称") + issue_parser.add_argument("--title", required=True, help="Issue标题") + issue_parser.add_argument("--body", default="", help="Issue描述") + issue_parser.add_argument("--labels", help="标签 (逗号分隔)") + + # 分支命令 + branch_parser = subparsers.add_parser("branch", help="分支相关操作") + branch_parser.add_argument("action", choices=["create"]) + branch_parser.add_argument("--owner", required=True, help="仓库所有者") + branch_parser.add_argument("--repo", required=True, help="仓库名称") + branch_parser.add_argument("--name", required=True, help="新分支名称") + branch_parser.add_argument("--from-branch", default="main", help="源分支") + + # 标签命令 + tag_parser = subparsers.add_parser("tag", help="标签相关操作") + tag_parser.add_argument("action", choices=["create"]) + tag_parser.add_argument("--owner", required=True, help="仓库所有者") + tag_parser.add_argument("--repo", required=True, help="仓库名称") + tag_parser.add_argument("--name", required=True, help="标签名称") + tag_parser.add_argument("--message", default="", help="标签消息") + tag_parser.add_argument("--target", default="main", help="目标分支/提交") + + # 配置命令 + config_parser = subparsers.add_parser("config", help="配置相关操作") + config_parser.add_argument("action", choices=["test"], help="测试连接") + + args = parser.parse_args() + + if not args.command: + parser.print_help() + sys.exit(1) + + # 创建客户端 + client = GiteaClient() + + # 处理命令 + try: + if args.command == "user": + if args.action == "info": + user = client.get_current_user() + print(json.dumps(user, indent=2, ensure_ascii=False)) + elif args.action == "repos": + repos = client.list_repos(args.page, args.limit) + for repo in repos: + print(f"{repo['full_name']}: {repo.get('description', '')}") + + elif args.command == "repo": + if args.action == "create": + if not args.name: + print("错误: --name 参数是必需的") + sys.exit(1) + repo = client.create_repo(args.name, args.description, args.private) + print(f"仓库创建成功: {repo['html_url']}") + + elif args.action == "get": + if not args.owner or not args.repo: + print("错误: --owner 和 --repo 参数是必需的") + sys.exit(1) + repo = client.get_repo(args.owner, args.repo) + print(json.dumps(repo, indent=2, ensure_ascii=False)) + + elif args.action == "search": + if not args.query: + print("错误: --query 参数是必需的") + sys.exit(1) + result = client.search_repos(args.query, args.limit) + for repo in result.get("data", []): + print(f"{repo['full_name']} - ★{repo.get('stars_count', 0)}") + + elif args.command == "pr": + if args.action == "create": + if not all([args.title, args.head]): + print("错误: --title 和 --head 参数是必需的") + sys.exit(1) + pr = client.create_pull_request( + args.owner, args.repo, args.title, args.body, args.head, args.base + ) + print(f"PR创建成功: #{pr['number']} - {pr['html_url']}") + + elif args.action == "list": + prs = client.list_pull_requests(args.owner, args.repo, args.state) + for pr in prs: + print(f"#{pr['number']}: {pr['title']} ({pr['state']})") + + elif args.action == "merge": + if not args.number: + print("错误: --number 参数是必需的") + sys.exit(1) + result = client.merge_pull_request(args.owner, args.repo, args.number) + print(f"PR #{args.number} 合并成功") + + elif args.command == "issue": + if args.action == "create": + labels = args.labels.split(",") if args.labels else None + issue = client.create_issue( + args.owner, args.repo, args.title, args.body, labels + ) + print(f"Issue创建成功: #{issue['number']} - {issue['html_url']}") + + elif args.command == "branch": + if args.action == "create": + branch = client.create_branch( + args.owner, args.repo, args.name, args.from_branch + ) + print(f"分支创建成功: {args.name}") + + elif args.command == "tag": + if args.action == "create": + tag = client.create_tag( + args.owner, args.repo, args.name, args.message, args.target + ) + print(f"标签创建成功: {args.name}") + + elif args.command == "config": + if args.action == "test": + user = client.get_current_user() + print(f"连接成功! 用户: {user.get('login', user.get('username'))}") + + except Exception as e: + print(f"错误: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/skills/gitea-actor/scripts/setup.sh b/skills/gitea-actor/scripts/setup.sh new file mode 100755 index 0000000..fe04879 --- /dev/null +++ b/skills/gitea-actor/scripts/setup.sh @@ -0,0 +1,185 @@ +#!/bin/bash + +# Gitea-Actor 配置脚本 +# 版本: 1.0.0 + +set -e + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 日志函数 +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 检查依赖 +check_dependencies() { + local deps=("curl" "jq") + for dep in "${deps[@]}"; do + if ! command -v "$dep" &> /dev/null; then + log_error "缺少依赖: $dep" + log_info "请安装:" + if [[ "$dep" == "jq" ]]; then + log_info " Ubuntu/Debian: sudo apt install jq" + log_info " CentOS/RHEL: sudo yum install jq" + log_info " macOS: brew install jq" + fi + exit 1 + fi + done +} + +# 显示标题 +show_header() { + echo -e "${BLUE}========================================${NC}" + echo -e "${GREEN} Gitea-Actor 配置向导${NC}" + echo -e "${BLUE}========================================${NC}" + echo "" +} + +# 验证 Gitea 连接 +test_gitea_connection() { + local url=$1 + local token=$2 + + log_info "正在验证 Gitea 连接..." + + # 检查 URL 格式 + if [[ ! "$url" =~ ^https?:// ]]; then + log_error "URL 格式不正确,请使用 http:// 或 https:// 开头" + return 1 + fi + + # 发送测试请求 + local response + local http_code + + response=$(curl -s -w "\n%{http_code}" \ + -H "Authorization: token $token" \ + -H "Accept: application/json" \ + "$url/api/v1/user" 2>/dev/null || true) + + http_code=$(echo "$response" | tail -n1) + local response_body=$(echo "$response" | sed '$d') + + if [[ "$http_code" == "200" ]]; then + local username=$(echo "$response_body" | jq -r '.login // .username' 2>/dev/null || echo "unknown") + log_success "Gitea 连接成功!" + log_info "用户: $username" + return 0 + else + log_error "Gitea 连接失败 (HTTP $http_code)" + if [[ -n "$response_body" ]]; then + log_info "错误信息: $(echo "$response_body" | jq -r '.message // .' 2>/dev/null || echo "$response_body")" + fi + return 1 + fi +} + +# 主函数 +main() { + show_header + check_dependencies + + # 检查现有配置 + local config_file="$HOME/.gitea-actor.env" + if [[ -f "$config_file" ]]; then + log_warn "发现现有配置文件: $config_file" + echo "" + log_info "现有配置:" + grep -E "^(export )?(GITEA_URL|GITEA_TOKEN)=" "$config_file" || true + echo "" + + read -p "是否覆盖现有配置? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_info "已取消配置" + exit 0 + fi + fi + + # 获取 Gitea URL + local default_url="https://gitea.com" + echo -e "${YELLOW}提示:${NC} 请输入您的 Gitea 实例 URL" + echo -e " 例如: https://gitea.example.com" + echo -e " 或: https://codehub.zlinkcloudtech.com" + echo "" + read -p "Gitea URL [默认: $default_url]: " gitea_url + gitea_url=${gitea_url:-$default_url} + + # 移除末尾的斜杠 + gitea_url=$(echo "$gitea_url" | sed 's|/$||') + + # 获取访问令牌 + echo "" + echo -e "${YELLOW}提示:${NC} 请提供个人访问令牌 (Personal Access Token)" + echo -e " 可以在 Gitea 的 设置 -> 应用 中创建" + echo "" + read -p "访问令牌: " gitea_token + + if [[ -z "$gitea_token" ]]; then + log_error "访问令牌不能为空" + exit 1 + fi + + # 测试连接 + echo "" + if test_gitea_connection "$gitea_url" "$gitea_token"; then + # 保存配置 + cat > "$config_file" << EOF +# Gitea-Actor 配置文件 +# 生成时间: $(date) +export GITEA_URL="$gitea_url" +export GITEA_TOKEN="$gitea_token" + +# 使用方式: +# source ~/.gitea-actor.env +# 或添加到 ~/.bashrc / ~/.zshrc +EOF + + log_success "配置已保存到: $config_file" + echo "" + log_info "使用方法:" + echo " 1. 立即加载配置: source $config_file" + echo " 2. 永久生效: 将以下行添加到 ~/.bashrc 或 ~/.zshrc:" + echo " source $config_file" + echo "" + log_info "验证命令:" + echo " curl -s -H \"Authorization: token \$GITEA_TOKEN\" \"\$GITEA_URL/api/v1/user\" | jq ." + + # 询问是否立即加载配置 + echo "" + read -p "是否立即加载配置到当前shell? (Y/n): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Nn]$ ]]; then + if source "$config_file" 2>/dev/null; then + log_success "配置已加载到当前shell" + else + log_warn "无法自动加载配置,请手动运行: source $config_file" + fi + fi + else + log_error "配置失败,请检查URL和令牌是否正确" + exit 1 + fi +} + +# 运行主函数 +main "$@" \ No newline at end of file