fb63d134e5
- gitea_ops.py: API CLI(组织/成员/工单/标签/里程碑/仓库),纯标准库 - board_ops.py: 看板网页 CLI(Playwright),处理 Gitea 1.26 看板无 API 限制 - SKILL.md: QwenPaw skill 文档(命令手册 + 血泪教训 + 失败模式) - README.md: 项目说明
172 lines
11 KiB
Markdown
172 lines
11 KiB
Markdown
---
|
||
description: 'Use this skill when the user needs to manage Gitea projects/orgs: listing
|
||
or creating issues (获取/创建工单), milestones (查看/创建/管理里程碑), labels, getting org members/people
|
||
(获取人员), repo info/topics, or creating and managing kanban boards (创建和管理看板). Triggers
|
||
on PineSound repo governance, Gitea API scripts, board/project operations, or "查工单/建工单/看板/里程碑".
|
||
Provides ready-to-run scripts gitea_ops.py (API) and board_ops.py (Playwright for
|
||
boards, which have no REST API in Gitea 1.26).'
|
||
name: gitea-project-manage
|
||
---
|
||
|
||
# Gitea 项目管理操作(gitea-project-manage)
|
||
|
||
## 概述
|
||
|
||
对 Gitea 组织/仓库做项目治理操作的完整工具箱:**工单(issue)、标签(label)、里程碑(milestone)、人员(组织成员)、组织/仓库信息、看板(project board)**。
|
||
|
||
一次写好的两个脚本,避免每次重复手写 curl/网页操作:
|
||
|
||
| 脚本 | 定位 | 依赖 |
|
||
|---|---|---|
|
||
| `scripts/gitea_ops.py` | API 操作:工单/标签/里程碑/成员/组织/仓库(**看板除外**) | Python 3.10+ 标准库,零第三方依赖 |
|
||
| `scripts/board_ops.py` | **看板网页操作**:新建/改名/删除看板、列管理、工单进看板、卡片拖拽、完成列关单 | playwright + chromium |
|
||
|
||
> ⚠️ **为什么拆两个脚本**:Gitea 1.26 的看板(Projects)**没有 REST API**(swagger 里 300 个端点完全没有 project 相关),工单/标签/里程碑有 API 但看板必须走浏览器 DOM。这是版本事实,不是配置问题。
|
||
|
||
## 环境与凭据
|
||
|
||
| 条目 | 内容 |
|
||
|---|---|
|
||
| Gitea Web | 内网 `http://192.168.1.3:10082`(API 前缀 `/api/v1`)· 外网 `code.infoepoch.cn` |
|
||
| 组织 | `PineSound`(可 `--org` 覆盖) |
|
||
| API Token | `/app/working.secret/gitea_token`(chmod 600,管理员权限,**不提交 git、不出现在文档**) |
|
||
| 网页登录 | Gitea 1.26 登录页**无 CSRF**,直接表单提交;凭据用 `GITEA_USER` / `GITEA_PASS` 环境变量传入(**不写明文进脚本/文档**) |
|
||
| SSH push | `ssh -i ~/.ssh/id_ed25519_gitea -p 2222 git@192.168.1.3`(Gitea 用户 Luci,可认证) |
|
||
|
||
## 快速开始
|
||
|
||
```bash
|
||
# gitea_ops.py:token 自动从 /app/working.secret/gitea_token 读取,直接跑
|
||
python3 scripts/gitea_ops.py org members
|
||
python3 scripts/gitea_ops.py issue list PineSoundServer --state open
|
||
|
||
# board_ops.py:首次需 playwright 环境
|
||
pip install playwright && playwright install chromium
|
||
GITEA_USER=Pine GITEA_PASS='<密码>' python3 scripts/board_ops.py boards PineSoundServer
|
||
# 登录态 cookie 缓存到 /tmp/gitea_board_state.json,之后同目录复用;密码只从环境变量读
|
||
```
|
||
|
||
## gitea_ops.py(API 操作)
|
||
|
||
全局参数:`--org <组织>`(默认 PineSound)、`--json`(原始 JSON,**必须放在子命令前**,如 `gitea_ops.py --json issue get ...`)。
|
||
|
||
### 组织与人员
|
||
|
||
```bash
|
||
python3 gitea_ops.py config # 当前配置
|
||
python3 gitea_ops.py org repos # 组织仓库列表(名称/公私/描述)
|
||
python3 gitea_ops.py org members # 组织成员(人员),含邮箱
|
||
```
|
||
|
||
### 工单(issue)
|
||
|
||
```bash
|
||
python3 gitea_ops.py issue list <repo> [--state open|closed|all] [--milestone 标题] [--label 标签名] [--assignee 用户名] [--limit N]
|
||
python3 gitea_ops.py issue get <repo> <number>
|
||
python3 gitea_ops.py issue create <repo> --title "标题" [--body] [--labels "标签1,标签2"] [--milestone "Beta-V1.1"] [--assignee "YangYi,Luci"] [--deadline 2026-08-31]
|
||
python3 gitea_ops.py issue edit <repo> <number> [--title] [--body] [--labels "标签1" | ""] [--assignee "用户" | ""] [--milestone 标题|none] [--state open|closed] [--deadline YYYY-MM-DD|none]
|
||
python3 gitea_ops.py issue close <repo> <number> [--comment "原因"] # 同理 reopen
|
||
python3 gitea_ops.py issue comment <repo> <number> --body "评论内容"
|
||
python3 gitea_ops.py issue delete <repo> <number> --yes # 危险,不可恢复
|
||
```
|
||
|
||
关键事实(实测):
|
||
- `--labels` / `--milestone` / `--assignee` 都支持**名称**(自动解析 id),不必查 id。
|
||
- **换标签必须走 `PUT /issues/{index}/labels`**:`PATCH /issues/{index}` 里的 `labels` 字段**不生效**(实测 201 但标签不变),gitea_ops.py 内部已处理。
|
||
- 工单列表自动过滤 PR(issue 对象带 `"pull_request": null` 键,直接判断会把 PR 也算进来)。
|
||
- 关闭后工单仍在里程碑统计内,`--comment` 会顺带加关闭说明(团队习惯:为什么关)。
|
||
|
||
### 里程碑(milestone)
|
||
|
||
```bash
|
||
python3 gitea_ops.py milestone list <repo> [--state all|open|closed]
|
||
python3 gitea_ops.py milestone create <repo> --title "Beta-V1.1" [--description] [--deadline 2026-08-31]
|
||
python3 gitea_ops.py milestone edit <repo> <id> [--title] [--description] [--deadline] [--state]
|
||
python3 gitea_ops.py milestone delete <repo> <id> --yes
|
||
```
|
||
|
||
### 标签(label)
|
||
|
||
默认仓库级;加 `--org-level` 变组织级(组织级标签所有仓库共享)。
|
||
|
||
```bash
|
||
python3 gitea_ops.py label list <repo> # 仓库级
|
||
python3 gitea_ops.py label list --org-level # 组织级
|
||
python3 gitea_ops.py label create <repo> --name "🐛 缺陷(Bug)" --color "#d73a4a" [--description]
|
||
python3 gitea_ops.py label edit <repo> <id> [--name] [--color] [--description]
|
||
python3 gitea_ops.py label delete <repo> <id> --yes
|
||
```
|
||
|
||
### 仓库信息
|
||
|
||
```bash
|
||
python3 gitea_ops.py repo info <repo> # 描述/topics/头像/默认分支
|
||
python3 gitea_ops.py repo topics <repo> --topics backend,api,go
|
||
python3 gitea_ops.py repo avatar <repo> /path/logo.png
|
||
```
|
||
|
||
## board_ops.py(看板网页操作)
|
||
|
||
Gitea 1.26 看板无 API,用 Playwright 驱动真实浏览器。**依赖**:`pip install playwright && playwright install chromium`(浏览器在 `/usr/bin/chromium`)。
|
||
|
||
**登录**:`GITEA_USER` / `GITEA_PASS` 环境变量(或 `--user/--pass`)。登录态存 `/tmp/gitea_board_state.json` + profile 目录 `/tmp/gitea_board_profile`,同目录复跑不再要密码(换机器/清 /tmp 后重新登录)。调试可见模式:`BOARD_HEADED=1`。
|
||
|
||
```bash
|
||
python3 board_ops.py boards <repo> # 列出所有看板 + 每列(id/名/卡片数)
|
||
python3 board_ops.py create <repo> --name "开发看板" # 新建(基础看板模板,4 列)
|
||
python3 board_ops.py rename <repo> --board 3 --name "新名字"
|
||
python3 board_ops.py delete <repo> --board 3 --yes
|
||
python3 board_ops.py add-column <repo> --board 3 --name "🚧 测试列"
|
||
python3 board_ops.py rename-column <repo> --board 3 --column 10 --name "新列名" # column 支持 id 或当前名
|
||
python3 board_ops.py add-issue <repo> --issue 8 --board 3 # 工单进看板(进"默认列"= 星标列)
|
||
python3 board_ops.py move <repo> --issue 8 --board 3 --to "🛠 开发中" # 卡片拖拽(自动两步法)
|
||
python3 board_ops.py done-close <repo> --board 3 --done "✅ 完成" # 完成列所有工单批量关闭(内部调 gitea_ops.py)
|
||
```
|
||
|
||
## 关键经验(血泪教训,勿改)
|
||
|
||
1. **工单进看板必须 DOM click**:`fetch/requests` 模拟 `POST /issues/projects`、`/issues/milestone` 返回 `{"ok":true}` 但**实际不生效**(Gitea 无 CSRF meta、cookie httpOnly、API 不暴露 project 字段)。必须打开工单详情页 → 点侧栏「项目」下拉 → 点项目项。
|
||
2. **拖拽用两步法**:跨多列一次性拖拽不稳定,先拖到中间列再拖到目标列(脚本已自动)。
|
||
3. **JS 派发的事件无效**(`isTrusted=false`),必须 Playwright/浏览器真实鼠标点击与拖拽。
|
||
4. **卡片默认 `draggable=false`**,进看板页后先刷新一次再拖(脚本已处理)。
|
||
5. **列选择器用 `.project-column[data-id="X"]`**;`#board_X .ui.cards` 是后代选择器永远匹配不到(`#board_X` 本身就是 `.ui.cards`)。
|
||
6. **登录页按钮没有 `type=submit`**,用 `button.ui.primary.button` 定位。
|
||
7. **`wait_for_load_state("networkidle")` 会超时**(Gitea 有长轮询),一律用 `"load"` + 固定等待。
|
||
8. **移到「✅ 完成」列 = 顺手关闭工单**(治理惯例,根治"干完忘关");`done-close` 实现批量。
|
||
9. 标签体系(PineSound 标准):中文名 + 括号英文,如 `🐛 缺陷(Bug)`;打标签规则:新功能类🟠高、维护优化类🟡中、bug 加🐛缺陷。
|
||
|
||
## 典型工作流(新需求从提出到上板)
|
||
|
||
```bash
|
||
# 1. 建工单(带标签/里程碑/指派)
|
||
python3 gitea_ops.py issue create PineSoundServer --title "音频向量化接口" \
|
||
--labels "✨ 新功能(Feature),🟠 高(High),🧠 AI算法(AI Algorithm)" \
|
||
--milestone "Beta-V1.1" --assignee YangYi
|
||
# 2. 进看板(默认列=待办)
|
||
python3 board_ops.py add-issue PineSoundServer --issue <N> --board 3
|
||
# 3. 开发中
|
||
python3 board_ops.py move PineSoundServer --issue <N> --board 3 --to "🛠 开发中"
|
||
# 4. dev 测试 / 合并 main
|
||
python3 board_ops.py move PineSoundServer --issue <N> --board 3 --to "🧪 dev 测试"
|
||
python3 board_ops.py move PineSoundServer --issue <N> --board 3 --to "🚀 已合并 main"
|
||
# 5. 完成 → 拖到 ✅ 完成 + 关单(两步可以合并:done-close)
|
||
python3 board_ops.py move PineSoundServer --issue <N> --board 3 --to "✅ 完成"
|
||
python3 gitea_ops.py issue close PineSoundServer <N> --comment "合入 main,关闭"
|
||
```
|
||
|
||
## 失败模式与恢复
|
||
|
||
| 现象 | 原因 | 处理 |
|
||
|---|---|---|
|
||
| `issue edit --labels` 后标签没变 | PATCH 的 labels 字段不生效 | 已修复为 PUT /labels;确认在最新脚本 |
|
||
| `board_ops.py` 登录报 Timeout | 按钮选择器/networkidle | 更新到最新脚本;手动用浏览器验证登录页结构 |
|
||
| 拖拽后卡片没动 | 卡片 draggable=false 或一次跨太多列 | 脚本已刷新+两步法;仍失败可开 `BOARD_HEADED=1` 人工看 |
|
||
| add-issue 找不到项目下拉 | 工单详情页结构变化(Gitea 升级) | 打开工单页找 `.ui.dropdown.full-width` 里 strong 文本 ==「项目」 |
|
||
| 404 on org repos | BASE 少了 `/api/v1` | `GITEA_BASE` 必须带 `/api/v1` |
|
||
| 看板列 id 与名称 | 列 id 不稳定(不同看板不同) | 一律用 `boards` 子命令先查 id,或用列名(脚本按名匹配) |
|
||
|
||
## 维护说明
|
||
|
||
- 脚本自带 `python3 -m py_compile` 可快速验证语法。
|
||
- 若 Gitea 升级导致 DOM 变化,用 Playwright/浏览器 DevTools 重新抓取选择器(参考:列 `.project-column[data-id]`、列名 `.project-column-title-text`、列菜单 `.project-column-header .ui.dropdown.tw-p-1`、创建列表单 `#project-column-title-input` + `.project-column-button-save`、看板删除 `button[data-url*="/projects/{id}/delete"]`、确认按钮 `.ui.primary.ok.button`)。
|
||
- 经验文档 `docs/gitea-project-governance.md` 记录了 PineSound 四仓库治理现状(里程碑/看板列/标签全量清单),动治理前先读。 |