Files
agent-skills/skills/gitea-actor/SKILL.md
yanghuajun336 ab5e59e999 feat: 优化 gitea-actor 技能,新增脚本工具和完整文档
- 新增 setup.sh 配置脚本
- 新增 api-examples.sh API 函数库
- 新增 gitea-cli.py Python CLI 工具
- 完善文档结构 (1640 行)
- 添加实际用例和工作流示例
- 增强故障排除和最佳实践指南
2026-03-31 22:35:30 +08:00

44 KiB
Raw Blame History

name, description
name description
gitea-actor 类似 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. 环境变量配置

# 设置 Gitea 实例 URL默认为 https://gitea.com
export GITEA_URL="https://your-gitea-instance.com"

# 设置个人访问令牌
export GITEA_TOKEN="your_personal_access_token_here"

3. 验证配置

# 验证 Gitea 连接
curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user" | jq .

快速开始

方法一:使用配置脚本(推荐)

# 下载并运行配置脚本
curl -s https://raw.githubusercontent.com/your-repo/gitea-actor/main/scripts/setup.sh | bash

# 或手动下载后运行
chmod +x setup.sh
./setup.sh

方法二:手动配置

# 设置环境变量
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 工具

# 安装依赖
pip install requests

# 使用 CLI
python gitea-cli.py config test

核心功能指令

1. 仓库管理

1.1 创建仓库

# 创建新仓库
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 克隆仓库

# 克隆仓库
git clone "$GITEA_URL/<owner>/<repo>.git"
cd <repo>

1.3 列出用户仓库

# 列出用户的所有仓库
curl -s -H "Authorization: token $GITEA_TOKEN" \
  "$GITEA_URL/api/v1/user/repos" | jq '.[] | .full_name'

2. Pull Request 管理

2.1 创建 Pull Request

# 创建 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/<owner>/<repo>/pulls"

2.2 列出 Pull Requests

# 列出仓库的所有 PR
curl -s -H "Authorization: token $GITEA_TOKEN" \
  "$GITEA_URL/api/v1/repos/<owner>/<repo>/pulls" | jq '.[] | .number, .title'

2.3 合并 Pull Request

# 合并 PR
curl -X POST -H "Authorization: token $GITEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"Do":"merge"}' \
  "$GITEA_URL/api/v1/repos/<owner>/<repo>/pulls/<pr-number>/merge"

3. Issue 管理

3.1 创建 Issue

# 创建新 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/<owner>/<repo>/issues"

3.2 分配 Issue

# 分配 Issue 给用户
curl -X POST -H "Authorization: token $GITEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"assignees":["username"]}' \
  "$GITEA_URL/api/v1/repos/<owner>/<repo>/issues/<issue-number>/assignees"

4. 代码审查

4.1 添加 PR 评论

# 在 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/<owner>/<repo>/pulls/<pr-number>/comments"

4.2 查看代码差异

# 获取 PR 的差异
curl -s -H "Authorization: token $GITEA_TOKEN" \
  "$GITEA_URL/api/v1/repos/<owner>/<repo>/pulls/<pr-number>.diff"

5. 分支和标签管理

5.1 创建分支

# 从现有分支创建新分支
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/<owner>/<repo>/branches"

5.2 创建标签

# 创建新标签
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/<owner>/<repo>/tags"

工作流程示例

示例 1完整的 PR 工作流

# 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/<owner>/<repo>/pulls"

示例 2Issue 跟踪工作流

# 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/<owner>/<repo>/issues"

# 2. 修复后关闭 Issue
curl -X PATCH -H "Authorization: token $GITEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"state":"closed"}' \
  "$GITEA_URL/api/v1/repos/<owner>/<repo>/issues/<issue-number>"

实际用例

以下是一些真实场景下的 Gitea 自动化用例:

用例 1自动化项目初始化

场景: 新项目启动需要快速创建仓库、配置 CI/CD、添加基础文件

#!/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自动化代码审查工作流

场景: 团队需要标准化代码审查流程

#!/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监控和报告生成

场景: 定期生成团队活动报告

#!/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自动化发布管理

场景: 自动化版本发布和变更日志生成

#!/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
  • 支持颜色输出和错误处理

使用方法:

# 授予执行权限
chmod +x scripts/setup.sh

# 运行配置向导
./scripts/setup.sh

# 加载配置
source ~/.gitea-actor.env

2. API 示例脚本 (api-examples.sh)

包含常用 Gitea API 操作的 Bash 函数库。

功能:

  • 20+ 个常用 API 函数
  • 统一的错误处理
  • 友好的输出格式
  • 可直接在 Shell 中使用

使用方法:

# 加载函数到当前 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 输出格式
  • 错误处理和验证

安装和使用:

# 安装依赖
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 格式错误

解决方案:

# 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 outCould not resolve host 可能原因:

  • 网络不可达
  • DNS 解析失败
  • 防火墙阻止

解决方案:

# 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 版本过旧
  • 资源不存在

解决方案:

# 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 状态码 可能原因:

  • 令牌缺少必要权限
  • 用户无权访问资源
  • 仓库权限设置限制

解决方案:

# 1. 检查令牌权限范围
# 2. 在 Gitea 中检查仓库权限
# 3. 确保令牌有对应操作的权限

5. 速率限制 (429 Too Many Requests)

症状: API 返回 429 状态码 可能原因:

  • API 请求过于频繁
  • Gitea 实例设置了速率限制

解决方案:

# 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 返回错误页面

解决方案:

# 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 problemself-signed certificate 可能原因:

  • 自签名证书
  • 证书过期
  • 证书链不完整

解决方案:

# 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 模式:

    curl -v -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user"
    
  2. 检查响应头:

    curl -s -I -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/user"
    
  3. 保存调试信息:

    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 脚本调试:

    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. 错误处理和可靠性

健壮的脚本编写

# 示例:带有错误处理的 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
}

重试机制

# 示例:带有重试的 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. 性能优化

批量操作

# 避免多次 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")

缓存策略

# 使用本地缓存减少 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

合理设置超时

# 根据操作类型设置不同的超时时间
export CURL_TIMEOUT=30  # 常规操作
export CURL_LONG_TIMEOUT=120  # 长时间操作(如创建仓库)

# 在 curl 命令中使用
curl --max-time $CURL_TIMEOUT ...

4. 代码质量和可维护性

模块化设计

# 将功能拆分为独立的函数或脚本
# gitea-functions.sh
source "$(dirname "$0")/gitea-config.sh"
source "$(dirname "$0")/gitea-repos.sh"
source "$(dirname "$0")/gitea-pr.sh"

标准化输出格式

# 使用一致的输出格式
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') $*"; }

文档和注释

#!/bin/bash
# 脚本: gitea-repo-manager.sh
# 作者: Your Name
# 描述: Gitea 仓库管理工具
# 版本: 1.0.0
# 用法: ./gitea-repo-manager.sh <命令> [参数]

5. 自动化工作流

Git Hooks 集成

# 在 .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 管道集成

# .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. 监控和日志

操作日志记录

# 记录所有重要操作
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"
}

性能监控

# 监控 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

请求和响应格式

认证头

# 使用个人访问令牌
Authorization: token {personal_access_token}

# 或使用基本认证(不推荐)
Authorization: Basic $(echo -n "username:password" | base64)

请求头

# 常用请求头
Content-Type: application/json
Accept: application/json
User-Agent: Gitea-Actor/1.0.0

# 分页请求头
Link: <https://gitea.com/api/v1/user/repos?page=2>; rel="next"

响应格式

{
  "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"
}

错误响应

{
  "message": "令牌无效",
  "documentation_url": "https://docs.gitea.com/api"
}

分页和限制

分页参数

# 基本分页
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 通常有速率限制,可以通过响应头查看:

# 检查速率限制头
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 示例

创建仓库并初始化文件

# 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"

批量操作示例

# 批量创建 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 版本

# 获取 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 文档 获取更新

高级功能和集成

1. Webhook 自动化

Webhook 配置

# 创建 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 处理器示例

#!/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

# .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 集成

# 在 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. 团队和权限管理

团队管理

# 创建团队
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}"

仓库权限管理

# 添加协作者
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. 高级搜索和报告

高级搜索

# 搜索代码
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 .

生成报告

# 生成仓库报告
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 操作自动化

自动同步分支

#!/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

批量仓库操作

# 批量更新仓库描述
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. 监控和告警

监控脚本

#!/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. 备份和迁移

仓库备份

#!/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/"

迁移工具

# 迁移仓库到新实例
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

# 发送通知到 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

# 同步 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"
}