forked from Gitlink/gitlink-cli
feat(workflows): add workflow skill system and fix batch commands
- Fix batch-label: use issue_tag_ids instead of tracker_id - Fix parseTracker: support Chinese label names (缺陷/功能/疑问 etc.) - Add workflow skill menu (workflows/SKILL.md) with 5 automation options - Add workflow reference files: community-ops, contributor-growth, multi-repo - Fix PowerShell scripts: Invoke-GL array syntax, remove ConvertFrom-Json - Fix wiki +create flag from --body to --content - Update contributor-growth doc with known issues and field corrections - Add contributor leaderboard HTML report Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
012c197ee6
commit
4f3ae280b7
|
|
@ -399,7 +399,7 @@ func newBatchLabelShortcut() *common.Shortcut {
|
|||
Name: "batch-label",
|
||||
Description: "Change tracker label for multiple issues",
|
||||
Flags: []common.Flag{
|
||||
{Name: "label", Short: "l", Usage: "Target label: bug, feature, support, doc, test, duplicate, question", Required: true},
|
||||
{Name: "label", Short: "l", Usage: "Target label: bug, feature, support, doc, test, duplicate, question, or Chinese names (缺陷/功能/文档/重复/疑问/支持/任务/测试/协助/搁置)", Required: true},
|
||||
{Name: "numbers", Short: "n", Usage: "Comma-separated issue numbers, e.g. 1,2,3"},
|
||||
{Name: "from", Usage: "Read issue numbers from a CSV file"},
|
||||
{Name: "dry-run", Usage: "Preview without making changes", Bool: true, Default: "false"},
|
||||
|
|
@ -444,7 +444,7 @@ func runBatchLabel(ctx *common.RuntimeContext) error {
|
|||
summary.Results = append(summary.Results, result)
|
||||
continue
|
||||
}
|
||||
if err := updateIssueField(ctx, number, map[string]interface{}{"tracker_id": trackerID}); err != nil {
|
||||
if err := updateIssueField(ctx, number, map[string]interface{}{"issue_tag_ids": []int{trackerID}}); err != nil {
|
||||
result.Status = "failed"
|
||||
result.Error = err.Error()
|
||||
summary.Failed++
|
||||
|
|
@ -552,7 +552,14 @@ func parsePriority(p string) (int, error) {
|
|||
}
|
||||
|
||||
func parseTracker(label string) (int, error) {
|
||||
switch strings.ToLower(strings.TrimSpace(label)) {
|
||||
trimmed := strings.TrimSpace(label)
|
||||
|
||||
// Check Chinese tag names first
|
||||
if id, ok := tagIDs[trimmed]; ok {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
switch strings.ToLower(trimmed) {
|
||||
case "bug":
|
||||
return trackerBug, nil
|
||||
case "feature":
|
||||
|
|
@ -571,7 +578,7 @@ func parseTracker(label string) (int, error) {
|
|||
if id, err := strconv.Atoi(label); err == nil {
|
||||
return id, nil
|
||||
}
|
||||
return 0, fmt.Errorf("invalid label %q: use bug, feature, support, doc, test, duplicate, or question", label)
|
||||
return 0, fmt.Errorf("invalid label %q: use bug, feature, support, doc, test, duplicate, question, or Chinese names (%s)", label, labelNames(tagIDs))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,177 +1,100 @@
|
|||
---
|
||||
name: gitlink-workflow
|
||||
version: 2.0.0
|
||||
description: "AI 自动化工作流:代码质量审查(PR Review)。当用户需要 AI 审查 PR 代码质量时触发。"
|
||||
description: "AI 自动化工作流:社区运营、代码审查、项目初始化、多仓库协同、贡献者成长。当用户需要 AI 自动化 GitLink 操作时触发。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["gitlink-cli"]
|
||||
triggers:
|
||||
- "工作流"
|
||||
- "workflow"
|
||||
- "自动化"
|
||||
- "帮我跑"
|
||||
- "执行"
|
||||
---
|
||||
|
||||
# gitlink-workflow(AI 代码质量看门人)
|
||||
# gitlink-workflow(AI 自动化工作流)
|
||||
|
||||
> **前置条件:** 先阅读 [`../gitlink-shared/SKILL.md`](../gitlink-shared/SKILL.md)
|
||||
|
||||
**CRITICAL — GitLink 操作只能用 `gitlink-cli`。禁止用 `gh`(GitHub CLI)操作 GitLink 资源。**
|
||||
**CRITICAL — GitLink 操作只能用 `gitlink-cli`。禁止用 `gh`(GitHub CLI)操作 GitLink 资源。`gh` 仅适用于 GitHub 平台。**
|
||||
|
||||
## 工作流:代码质量看门人(Code Quality Gatekeeper)
|
||||
本技能提供 Claude Code 可直接执行的高级工作流模板。
|
||||
|
||||
**触发词**:代码审查、PR review、质量检查、gatekeeper、审查 PR
|
||||
|
||||
**参数**:
|
||||
- `--owner`:仓库所属组织或用户
|
||||
- `--repo`:仓库名称
|
||||
- `--pr-id`:指定 PR ID(可选,默认审查所有 open PR)
|
||||
- `--threshold`:质量阈值(默认 80)
|
||||
|
||||
### 流程总览
|
||||
## 功能菜单
|
||||
|
||||
```
|
||||
PR 提交 → 获取详情 → 获取 Diff → AI 四维度评分 → 发布审查评论 → 检查 CI → 自动合并
|
||||
====== GitLink 自动化工作流 ======
|
||||
|
||||
请选择要执行的工作流:
|
||||
|
||||
1. 社区运营自动化
|
||||
→ Issue 自动分类、负责人分配、周报生成、Release Notes
|
||||
|
||||
2. 代码质量审查
|
||||
→ PR Review、AI 四维度评分、自动合并
|
||||
|
||||
3. 项目一键初始化
|
||||
→ 创建仓库、README、CI 配置、初始 Issues、分支保护
|
||||
|
||||
4. 多仓库协同
|
||||
→ 跨仓库 Issue/PR 追踪、状态 Dashboard、协同发版
|
||||
|
||||
5. 贡献者成长体系
|
||||
→ 数据收集、AHP 评分、排行榜、Wiki 发布、Badge 颁发
|
||||
|
||||
请输入编号(1-5)或功能名称:
|
||||
```
|
||||
|
||||
### 审查单个 PR 的步骤
|
||||
## 用户输入 → 工作流映射
|
||||
|
||||
**步骤 1** — 获取 PR 详情:
|
||||
```bash
|
||||
gitlink-cli pr +view --owner {OWNER} --repo {REPO} --id {PR_ID} --format json
|
||||
```
|
||||
提取字段:
|
||||
- title: `.data.title // .data.subject // .data.issue.subject`
|
||||
- author: `.data.author.login // .data.author.username`
|
||||
- state: `.data.state // .data.status`
|
||||
| 用户输入 | 执行的工作流 | Reference 文件 |
|
||||
|----------|-------------|---------------|
|
||||
| `1` 或 "社区运营" | 社区运营自动化 | [`workflow-community-ops.md`](references/workflow-community-ops.md) |
|
||||
| `2` 或 "代码审查" | 代码质量审查 | [`workflow-pr-review.md`](references/workflow-pr-review.md) |
|
||||
| `3` 或 "项目初始化" | 项目一键初始化 | [`workflow-repo-setup.md`](references/workflow-repo-setup.md) |
|
||||
| `4` 或 "多仓库" | 多仓库协同 | [`workflow-multi-repo.md`](references/workflow-multi-repo.md) |
|
||||
| `5` 或 "贡献者" | 贡献者成长体系 | [`workflow-contributor-growth.md`](references/workflow-contributor-growth.md) |
|
||||
|
||||
**步骤 2** — 获取变更文件列表:
|
||||
```bash
|
||||
gitlink-cli pr +files --owner {OWNER} --repo {REPO} --id {PR_ID} --format json
|
||||
```
|
||||
文件列表路径:`.data.files[]`,字段:`.name`(或 `.filename`)、`.additions`、`.deletions`
|
||||
## 执行流程
|
||||
|
||||
**步骤 3** — 获取代码差异:
|
||||
```bash
|
||||
gitlink-cli pr +diff --owner {OWNER} --repo {REPO} --id {PR_ID} --format json
|
||||
```
|
||||
提取 diff:`.data.files[].sections[].lines[].content`,截取前 5000 字符
|
||||
1. **展示菜单** — 列出所有可用工作流
|
||||
2. **获取用户选择** — 用户输入编号或功能名称
|
||||
3. **读取 Reference** — 根据选择读取对应的 reference 文件
|
||||
4. **确认参数** — 询问必要的参数(owner/repo 等)
|
||||
5. **执行工作流** — 按照 reference 文件的步骤执行
|
||||
6. **展示结果** — 输出执行结果和摘要
|
||||
|
||||
**步骤 4** — AI 四维度代码审查(总分 100):
|
||||
## 快捷触发
|
||||
|
||||
| 维度 | 满分 | 检查项 |
|
||||
|------|------|--------|
|
||||
| 代码质量 | 25 | 复杂度、命名规范、注释、格式一致性 |
|
||||
| 安全性 | 25 | SQL注入、XSS、硬编码凭证、认证绕过、输入验证 |
|
||||
| 性能 | 25 | 循环效率、资源泄漏、N+1查询、内存占用、阻塞调用 |
|
||||
| 可维护性 | 25 | 代码重复、职责单一、依赖耦合、测试覆盖 |
|
||||
用户也可以直接说特定意图,跳过菜单直接执行:
|
||||
|
||||
评分标准:
|
||||
- 90-100:优秀,可直接合并
|
||||
- 75-89:良好,建议合并
|
||||
- 60-74:一般,需要改进
|
||||
- <60:较差,不建议合并
|
||||
| 用户说的话 | 直接执行 |
|
||||
|------------|----------|
|
||||
| "帮我跑一下社区运营" | 社区运营自动化 |
|
||||
| "审查一下这个 PR" | 代码质量审查 |
|
||||
| "创建一个新项目" | 项目一键初始化 |
|
||||
| "看看组织下所有仓库" | 多仓库协同 |
|
||||
| "生成贡献者排行榜" | 贡献者成长体系 |
|
||||
|
||||
问题严重级别:
|
||||
- CRITICAL:阻止合并(安全漏洞、数据丢失风险)
|
||||
- HIGH:强烈建议修复(性能问题、逻辑错误)
|
||||
- MEDIUM:建议修复(代码质量、可维护性)
|
||||
- LOW:可选修复(风格、命名)
|
||||
## 参数说明
|
||||
|
||||
**输出要求**:AI 必须输出以下 JSON 结构:
|
||||
```json
|
||||
{
|
||||
"total": 85,
|
||||
"quality": 22,
|
||||
"security": 25,
|
||||
"performance": 20,
|
||||
"maintainability": 18,
|
||||
"issues": [
|
||||
{
|
||||
"severity": "MEDIUM",
|
||||
"category": "quality",
|
||||
"file": "src/main.go",
|
||||
"rule": "naming",
|
||||
"description": "变量名过于简短",
|
||||
"suggestion": "使用更具描述性的变量名"
|
||||
}
|
||||
],
|
||||
"positive_notes": [
|
||||
{"description": "错误处理完善"}
|
||||
],
|
||||
"recommendations": [
|
||||
"建议添加单元测试"
|
||||
],
|
||||
"verdict": "PASS"
|
||||
}
|
||||
```
|
||||
| 参数 | 说明 | 获取方式 |
|
||||
|------|------|----------|
|
||||
| `--owner` | 仓库所有者 | 自动从 git remote 解析,或询问用户 |
|
||||
| `--repo` | 仓库名称 | 自动从 git remote 解析,或询问用户 |
|
||||
| `--org` | 组织名称 | 询问用户(多仓库协同时需要) |
|
||||
|
||||
**AI 不可用时的降级方案**(关键词检测):
|
||||
- 检测到 password/secret/token/api_key/private_key → security -15
|
||||
- 检测到 eval()/exec()/system()/os.system → security -10
|
||||
- 检测到 TODO/FIXME/HACK/XXX → quality -5
|
||||
- 检测到 SELECT */findAll()/.all() → performance -10
|
||||
- 检测到 sleep()/Thread.sleep → performance -5
|
||||
- 变更文件 > 20 个 → maintainability -10
|
||||
## 最佳实践
|
||||
|
||||
**步骤 5** — 组装审查评论 Markdown:
|
||||
- 所有工作流命令使用 `--format json` 以便解析输出
|
||||
- 写入操作前确认用户意图
|
||||
- 批量操作建议先用小范围测试
|
||||
- 保存工作流执行结果以便回溯
|
||||
- 自动从 git remote 解析 owner/repo,解析失败时询问用户
|
||||
- 支持 `--dry-run` 预览模式(部分工作流)
|
||||
|
||||
```markdown
|
||||
## AI Code Quality Review - PR #{PR_ID}
|
||||
## References
|
||||
|
||||
### Scores
|
||||
| Dimension | Score | Max |
|
||||
|-----------|-------|-----|
|
||||
| Code Quality | {QUALITY} | 25 |
|
||||
| Security | {SECURITY} | 25 |
|
||||
| Performance | {PERFORMANCE} | 25 |
|
||||
| Maintainability | {MAINTAINABILITY} | 25 |
|
||||
| **Total** | **{TOTAL}** | **100** |
|
||||
|
||||
### Issues Found
|
||||
- [{SEVERITY}] {CATEGORY}: {DESCRIPTION} ({FILE}) → {SUGGESTION}
|
||||
|
||||
### Positive Notes
|
||||
- {NOTE}
|
||||
|
||||
### Recommendations
|
||||
- {REC}
|
||||
|
||||
### Verdict
|
||||
{PASS/FAIL} - Score {TOTAL} {>=/<} threshold {THRESHOLD}
|
||||
|
||||
---
|
||||
*Auto-reviewed by gitlink-cli code-quality-gatekeeper workflow*
|
||||
```
|
||||
|
||||
**步骤 6** — 发布审查评论:
|
||||
```bash
|
||||
gitlink-cli api POST /{OWNER}/{REPO}/pulls/{PR_ID}/reviews --body '{"body":"{REVIEW_MD}","event":"{EVENT}"}'
|
||||
```
|
||||
- 分数 >= 阈值 → event = "APPROVE"
|
||||
- 分数 < 阈值 → event = "COMMENT"
|
||||
|
||||
**步骤 7** — 检查 CI 构建状态:
|
||||
```bash
|
||||
gitlink-cli ci +builds --owner {OWNER} --repo {REPO} --format json
|
||||
```
|
||||
遍历 `.data.builds[]` 或 `.data[]`,检查 status 是否为 success/passed/completed
|
||||
|
||||
**步骤 8** — 自动合并(条件:分数 >= 阈值 且 CI 全部通过):
|
||||
```bash
|
||||
gitlink-cli pr +merge --owner {OWNER} --repo {REPO} --id {PR_ID} --method merge
|
||||
```
|
||||
|
||||
### 审查所有 open PR
|
||||
|
||||
如果用户没指定 `--pr-id`,先获取列表再逐个审查:
|
||||
```bash
|
||||
gitlink-cli pr +list --owner {OWNER} --repo {REPO} --state open --limit 50 --format json
|
||||
```
|
||||
PR ID 提取:`.data.issues[]` 或 `.data.pulls[]` 或 `.data[]` 中的 `.pull_request_number // .number // .id`
|
||||
|
||||
## 其他自动化工作流
|
||||
|
||||
以下工作流不需要 AI,已实现为 PowerShell 脚本(`workflows/` 目录):
|
||||
|
||||
| 工作流 | 脚本 | 用法 |
|
||||
|--------|------|------|
|
||||
| 社区运营自动化 | `01-community-ops.ps1` | `pwsh workflows/01-community-ops.ps1 -Owner zzx-coder -Repo gitlink-cli` |
|
||||
| 项目一键初始化 | `03-project-init.ps1` | `pwsh workflows/03-project-init.ps1 -Owner org -Name my-app -Desc "描述" -Lang go` |
|
||||
| 多仓库协同 | `04-multi-repo-collab.ps1` | `pwsh workflows/04-multi-repo-collab.ps1 -Org myorg` |
|
||||
| 贡献者成长体系 | `05-contributor-growth.ps1` | `pwsh workflows/05-contributor-growth.ps1 -Owner zzx-coder -Repo gitlink-cli` |
|
||||
- [gitlink-shared](../gitlink-shared/SKILL.md) — 认证和全局参数
|
||||
- [gitlink-workflows](../gitlink-workflows/SKILL.md) — 工作流总入口(含可执行脚本)
|
||||
|
|
|
|||
|
|
@ -1,579 +0,0 @@
|
|||
# AI Agent 自动化工作流完整示例
|
||||
|
||||
本文档展示如何组合使用多个 gitlink-cli 工作流,实现完整的 AI Agent 自动化项目管理。
|
||||
|
||||
## 🤖 AI Agent 完整工作流
|
||||
|
||||
### 场景:新项目从创建到发布的完整自动化
|
||||
|
||||
这个示例展示 AI Agent 如何自动化管理一个软件项目的完整生命周期,从仓库创建到版本发布。
|
||||
|
||||
## 工作流组合
|
||||
|
||||
### 1. 项目初始化阶段
|
||||
|
||||
```python
|
||||
# AI Agent 项目初始化
|
||||
def initialize_new_project(project_name, description):
|
||||
"""完整的项目初始化工作流"""
|
||||
|
||||
# 1. 创建仓库
|
||||
repo = create_repository(project_name, description)
|
||||
|
||||
# 2. 初始化项目结构
|
||||
setup_project_structure(repo)
|
||||
|
||||
# 3. 配置 CI/CD
|
||||
configure_ci_cd(repo)
|
||||
|
||||
# 4. 创建初始 Issue
|
||||
create_initial_issues(repo)
|
||||
|
||||
return repo
|
||||
|
||||
# 执行
|
||||
project = initialize_new_project(
|
||||
"my-awesome-project",
|
||||
"一个很棒的项目,用于演示自动化工作流"
|
||||
)
|
||||
```
|
||||
|
||||
### 2. 开发阶段自动化
|
||||
|
||||
```python
|
||||
# AI Agent 开发管理
|
||||
def manage_development_workflow(repo):
|
||||
"""开发阶段的自动化管理"""
|
||||
|
||||
while development_in_progress:
|
||||
# 1. 自动分类新 Issue
|
||||
triage_new_issues(repo)
|
||||
|
||||
# 2. 审查新的 PR
|
||||
review_pull_requests(repo)
|
||||
|
||||
# 3. 更新项目进度
|
||||
update_project_status(repo)
|
||||
|
||||
# 4. 检查是否需要发布
|
||||
if should_release(repo):
|
||||
generate_release_notes(repo)
|
||||
create_release(repo)
|
||||
|
||||
sleep(cycle_interval)
|
||||
```
|
||||
|
||||
### 3. Sprint 自动化
|
||||
|
||||
```python
|
||||
# AI Agent Sprint 管理
|
||||
def automate_sprint_management(repo):
|
||||
"""完整的 Sprint 自动化管理"""
|
||||
|
||||
# Sprint 开始
|
||||
sprint_number = start_new_sprint(repo)
|
||||
|
||||
# Sprint 监控
|
||||
monitor_sprint_progress(repo, sprint_number)
|
||||
|
||||
# Sprint 结束
|
||||
end_sprint(repo, sprint_number)
|
||||
generate_sprint_report(repo, sprint_number)
|
||||
|
||||
# 执行 Sprint 工作流
|
||||
sprint_result = automate_sprint_management(project)
|
||||
```
|
||||
|
||||
## 完整的端到端示例
|
||||
|
||||
### 示例:自动化 Issue 到 Release 流程
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# 完整的自动化工作流脚本
|
||||
|
||||
OWNER="ai-agent"
|
||||
REPO="demo-project"
|
||||
PROJECT_NAME="AI Agent Demo"
|
||||
|
||||
echo "🤖 启动 AI Agent 自动化工作流..."
|
||||
|
||||
# 阶段 1: 项目创建
|
||||
echo "📦 阶段 1: 创建项目"
|
||||
gitlink-cli repo +create \
|
||||
--name "$REPO" \
|
||||
--description "$PROJECT_NAME" \
|
||||
--private false
|
||||
|
||||
# 初始化本地仓库
|
||||
cd "$REPO"
|
||||
git init
|
||||
git remote add gitlink "https://www.gitlink.org.cn/$OWNER/$REPO.git"
|
||||
|
||||
# 创建基础文件
|
||||
echo "# $PROJECT_NAME" > README.md
|
||||
echo "MIT License" > LICENSE
|
||||
git add .
|
||||
git commit -m "Initial commit"
|
||||
git push -u gitlink master:master
|
||||
|
||||
# 设置分支保护
|
||||
gitlink-cli branch +protect --owner "$OWNER" --repo "$REPO" --name master
|
||||
|
||||
# 阶段 2: 创建开发 Issue
|
||||
echo "🎯 阶段 2: 创建开发 Issue"
|
||||
FEATURES=(
|
||||
"用户认证系统"
|
||||
"数据管理模块"
|
||||
"API 接口开发"
|
||||
"前端界面设计"
|
||||
"测试框架搭建"
|
||||
)
|
||||
|
||||
for feature in "${FEATURES[@]}"; do
|
||||
gitlink-cli issue +create \
|
||||
--owner "$OWNER" \
|
||||
--repo "$REPO" \
|
||||
--title "开发 $feature" \
|
||||
--body "## 任务描述
|
||||
|
||||
实现 $feature 功能
|
||||
|
||||
## 技术要求
|
||||
- 代码规范
|
||||
- 单元测试
|
||||
- 文档完整
|
||||
|
||||
## 验收标准
|
||||
- 功能正常工作
|
||||
- 测试通过
|
||||
- 代码审查通过"
|
||||
done
|
||||
|
||||
# 阶段 3: 模拟开发和 PR 创建
|
||||
echo "🔧 阶段 3: 模拟开发工作"
|
||||
|
||||
# 创建功能分支
|
||||
for feature in "${FEATURES[@]}"; do
|
||||
# 模拟分支名(将中文转为拼音)
|
||||
branch_name="feature-$(echo $feature | md5sum | cut -c1-8)"
|
||||
|
||||
git checkout -b "$branch_name"
|
||||
|
||||
# 模拟开发工作
|
||||
echo "// $feature 实现" > "${feature}.js"
|
||||
git add .
|
||||
git commit -m "Implement $feature"
|
||||
git push gitlink "$branch_name"
|
||||
|
||||
# 创建 PR
|
||||
gitlink-cli pr +create \
|
||||
--owner "$OWNER" \
|
||||
--repo "$REPO" \
|
||||
--title "Feature: $feature" \
|
||||
--head "$branch_name" \
|
||||
--base master \
|
||||
--body "## 功能说明
|
||||
|
||||
实现 $feature 功能
|
||||
|
||||
## 变更内容
|
||||
- 添加核心功能
|
||||
- 实现相关测试
|
||||
- 更新文档
|
||||
|
||||
## 测试情况
|
||||
- 单元测试通过
|
||||
- 集成测试通过
|
||||
- 手工测试完成"
|
||||
|
||||
git checkout master
|
||||
done
|
||||
|
||||
# 阶段 4: 自动 Issue 分类
|
||||
echo "🏷️ 阶段 4: 自动分类 Issue"
|
||||
|
||||
# 获取所有开放 Issue
|
||||
ISSUES=$(gitlink-cli issue +list --owner "$OWNER" --repo "$REPO" --state open --format json)
|
||||
|
||||
# 为 Issue 添加标签
|
||||
echo "$ISSUES" | jq -r '.data.issues[].id' | while read issue_id; do
|
||||
echo "处理 Issue #$issue_id"
|
||||
|
||||
# 获取 Issue 详情
|
||||
ISSUE_DETAIL=$(gitlink-cli issue +view --owner "$OWNER" --repo "$REPO" --id "$issue_id" --format json)
|
||||
TITLE=$(echo "$ISSUE_DETAIL" | jq -r '.data.subject')
|
||||
|
||||
# 基于标题分类
|
||||
if echo "$TITLE" | grep -iq "认证"; then
|
||||
echo " → 分类为: feature + security"
|
||||
# 实际执行时取消注释
|
||||
# gitlink-cli api POST "/$OWNER/$REPO/issues/$issue_id" --body '{"issue_tag_ids":[1,5]}'
|
||||
else
|
||||
echo " → 分类为: feature"
|
||||
# gitlink-cli api POST "/$OWNER/$REPO/issues/$issue_id" --body '{"issue_tag_ids":[1]}'
|
||||
fi
|
||||
done
|
||||
|
||||
# 阶段 5: PR 审查
|
||||
echo "🔍 阶段 5: 自动 PR 审查"
|
||||
|
||||
# 获取开放 PR
|
||||
PRS=$(gitlink-cli pr +list --owner "$OWNER" --repo "$REPO" --state open --format json)
|
||||
|
||||
echo "$PRS" | jq -r '.data.prs[].id' | while read pr_id; do
|
||||
echo "审查 PR #$pr_id"
|
||||
|
||||
# 获取 PR 详情
|
||||
PR_DETAIL=$(gitlink-cli pr +view --owner "$OWNER" --repo "$REPO" --id "$pr_id" --format json)
|
||||
PR_AUTHOR=$(echo "$PR_DETAIL" | jq -r '.data.author.login')
|
||||
PR_TITLE=$(echo "$PR_DETAIL" | jq -r '.data.title')
|
||||
|
||||
# 简单的代码检查(这里只是模拟)
|
||||
REVIEW_COMMENTS="# 🔍 自动审查结果
|
||||
|
||||
## PR 信息
|
||||
- **标题**: $PR_TITLE
|
||||
- **作者**: $PR_AUTHOR
|
||||
- **状态**: 待审查
|
||||
|
||||
## ✅ 自动检查
|
||||
- 代码提交正常
|
||||
- 变更描述清晰
|
||||
- 符合项目规范
|
||||
|
||||
## 💡 建议
|
||||
- 添加单元测试
|
||||
- 更新相关文档
|
||||
- 确认向后兼容性
|
||||
|
||||
## 📋 审查结论
|
||||
代码质量良好,建议合并。"
|
||||
|
||||
echo " → 添加审查评论"
|
||||
# 实际执行时取消注释
|
||||
# gitlink-cli api POST "/$OWNER/$REPO/pulls/$pr_id/reviews" --body "{\"body\":\"$REVIEW_COMMENTS\",\"event\":\"APPROVE\"}"
|
||||
done
|
||||
|
||||
# 阶段 6: 生成 Release Notes
|
||||
echo "📝 阶段 6: 生成 Release Notes"
|
||||
|
||||
# 合并所有 PR(模拟)
|
||||
echo "合并所有功能 PR..."
|
||||
MERGED_PRS=$(gitlink-cli pr +list --owner "$OWNER" --repo "$REPO" --state merged --format json)
|
||||
|
||||
# 生成 Release Notes
|
||||
RELEASE_NOTES="# 🎉 v1.0.0 首个版本发布
|
||||
|
||||
## 📊 版本概述
|
||||
这是 $PROJECT_NAME 的首个稳定版本,包含了核心功能的完整实现。
|
||||
|
||||
## ✨ 新功能
|
||||
- 用户认证系统:完整的登录注册功能
|
||||
- 数据管理模块:高效的数据存储和检索
|
||||
- API 接口:RESTful API 设计
|
||||
- 前端界面:现代化的用户界面
|
||||
- 测试框架:完整的自动化测试
|
||||
|
||||
## 🐛 Bug 修复
|
||||
- 修复认证过程中的边界问题
|
||||
- 解决数据一致性问题
|
||||
- 优化 API 响应性能
|
||||
|
||||
## 🔧 技术改进
|
||||
- 代码结构优化
|
||||
- 性能提升 30%
|
||||
- 安全性增强
|
||||
|
||||
## 📚 文档更新
|
||||
- 用户手册完善
|
||||
- API 文档更新
|
||||
- 开发指南补充
|
||||
|
||||
## 🙏 贡献者
|
||||
感谢所有参与开发的贡献者!
|
||||
|
||||
## 📥 安装方法
|
||||
\`\`\`bash
|
||||
# 使用 npm 安装
|
||||
npm install $OWNER/$REPO@v1.0.0
|
||||
|
||||
# 或使用 yarn
|
||||
yarn add $OWNER/$REPO@v1.0.0
|
||||
\`\`\`
|
||||
|
||||
## 🔄 升级指南
|
||||
从之前的版本升级,请参考迁移指南。
|
||||
|
||||
## 📚 完整文档
|
||||
- 用户指南: https://www.gitlink.org.cn/$OWNER/$REPO/wiki
|
||||
- API 文档: https://www.gitlink.org.cn/$OWNER/$REPO/api-docs
|
||||
|
||||
---
|
||||
**发布日期**: $(date +%Y-%m-%d)
|
||||
**下一版本**: v1.1.0 (计划于 $(date -d "1 month" +%Y-%m-%d) 发布)"
|
||||
|
||||
# 创建 Release
|
||||
echo "创建 Release v1.0.0..."
|
||||
gitlink-cli release +create \
|
||||
--owner "$OWNER" \
|
||||
--repo "$REPO" \
|
||||
--tag "v1.0.0" \
|
||||
--name "v1.0.0" \
|
||||
--body "$RELEASE_NOTES"
|
||||
|
||||
# 阶段 7: Sprint 报告
|
||||
echo "📊 阶段 7: 生成 Sprint 报告"
|
||||
|
||||
SPRINT_START=$(date -d "14 days ago" +%Y-%m-%d)
|
||||
SPRINT_END=$(date +%Y-%m-%d)
|
||||
|
||||
SPRINT_REPORT="# 📊 Sprint 1 完成报告
|
||||
|
||||
## 📅 时间信息
|
||||
- **Sprint 周期**: $SPRINT_START 至 $SPRINT_END
|
||||
- **团队规模**: AI Agent x 1
|
||||
- **工作模式**: 自动化开发
|
||||
|
||||
## 🎯 目标达成
|
||||
### 计划完成度
|
||||
- **计划 Issue**: 5 个
|
||||
- **实际完成**: 5 个
|
||||
- **完成率**: 100%
|
||||
|
||||
### 质量指标
|
||||
- **代码质量**: 优秀
|
||||
- **测试覆盖率**: 95%
|
||||
- **文档完整度**: 100%
|
||||
|
||||
## 💻 工作统计
|
||||
### 代码提交
|
||||
- **总提交数**: 42 次
|
||||
- **日均提交**: 3 次/天
|
||||
- **代码行数**: +2,450 -180 行
|
||||
|
||||
### Issue 处理
|
||||
- **关闭 Issue**: 5 个
|
||||
- **新建 Issue**: 0 个
|
||||
- **平均处理时间**: 2.5 天
|
||||
|
||||
### PR 合并
|
||||
- **合并 PR**: 5 个
|
||||
- **平均审查时间**: 1 小时
|
||||
- **平均合并时间**: 2 小时
|
||||
|
||||
## 🎉 主要成就
|
||||
1. ✅ 完成用户认证系统开发
|
||||
2. ✅ 实现数据管理模块
|
||||
3. ✅ 构建 RESTful API
|
||||
4. ✅ 设计现代化前端界面
|
||||
5. ✅ 建立完整测试体系
|
||||
|
||||
## 📈 性能指标
|
||||
- **开发效率**: 高
|
||||
- **代码质量**: 优秀
|
||||
- **自动化程度**: 95%
|
||||
- **文档完整度**: 100%
|
||||
|
||||
## 🔮 下期规划
|
||||
- 性能优化和改进
|
||||
- 新功能模块开发
|
||||
- 国际化支持
|
||||
- 移动端适配
|
||||
|
||||
---
|
||||
**AI Agent 自动化工作流演示**
|
||||
**报告生成**: $(date +%Y-%m-%d %H:%M:%S)"
|
||||
|
||||
# 保存 Sprint 报告
|
||||
REPORT_FILE="sprint_reports/sprint_1_$(date +%Y%m%d).md"
|
||||
mkdir -p sprint_reports
|
||||
echo "$SPRINT_REPORT" > "$REPORT_FILE"
|
||||
|
||||
echo "🎉 AI Agent 自动化工作流完成!"
|
||||
echo ""
|
||||
echo "📊 项目统计:"
|
||||
echo " 仓库: https://www.gitlink.org.cn/$OWNER/$REPO"
|
||||
echo " Issue: 5 个全部完成"
|
||||
echo " PR: 5 个全部合并"
|
||||
echo " Release: v1.0.0 已发布"
|
||||
echo ""
|
||||
echo "📄 生成的文档:"
|
||||
echo " - Release Notes: https://www.gitlink.org.cn/$OWNER/$REPO/releases/v1.0.0"
|
||||
echo " - Sprint 报告: $REPORT_FILE"
|
||||
```
|
||||
|
||||
## Claude Code 集成示例
|
||||
|
||||
### 在 Claude Code 中使用工作流
|
||||
|
||||
```markdown
|
||||
# 用户指令
|
||||
|
||||
帮助我创建一个新的项目并完成首个版本的发布。
|
||||
|
||||
# Claude Code 执行
|
||||
|
||||
我会使用 gitlink-cli 的自动化工作流来完成这个任务:
|
||||
|
||||
1. **创建仓库** - 使用 workflow-repo-setup
|
||||
2. **管理 Issue** - 使用 workflow-issue-triage
|
||||
3. **审查 PR** - 使用 workflow-pr-review
|
||||
4. **生成 Release** - 使用 workflow-release-notes
|
||||
5. **总结报告** - 使用 workflow-sprint-report
|
||||
|
||||
让我开始执行...
|
||||
```
|
||||
|
||||
### 技能组合使用
|
||||
|
||||
```python
|
||||
# AI Agent 多技能组合
|
||||
class GitLinkAgent:
|
||||
def __init__(self, owner, repo):
|
||||
self.owner = owner
|
||||
self.repo = repo
|
||||
self.cli = "gitlink-cli"
|
||||
|
||||
def complete_project_workflow(self):
|
||||
"""完整的项目工作流"""
|
||||
|
||||
# 阶段 1: 初始化
|
||||
self.setup_repository()
|
||||
|
||||
# 阶段 2: 开发管理
|
||||
self.manage_development()
|
||||
|
||||
# 阶段 3: 质量控制
|
||||
self.automated_review()
|
||||
|
||||
# 阶段 4: 发布管理
|
||||
self.create_release()
|
||||
|
||||
# 阶段 5: 报告总结
|
||||
self.generate_reports()
|
||||
|
||||
def setup_repository(self):
|
||||
"""仓库初始化"""
|
||||
# 使用 workflow-repo-setup
|
||||
create_repo_cmd = f"{self.cli} repo +create --name {self.repo}"
|
||||
subprocess.run(create_repo_cmd.split())
|
||||
|
||||
protect_branch_cmd = f"{self.cli} branch +protect --name master"
|
||||
subprocess.run(protect_branch_cmd.split())
|
||||
|
||||
def manage_development(self):
|
||||
"""开发管理"""
|
||||
# 监控新 Issue 并自动分类
|
||||
issues = self.get_new_issues()
|
||||
for issue in issues:
|
||||
self.classify_issue(issue)
|
||||
|
||||
# 监控新 PR 并审查
|
||||
prs = self.get_new_prs()
|
||||
for pr in prs:
|
||||
self.review_pr(pr)
|
||||
|
||||
def automated_review(self):
|
||||
"""自动化审查"""
|
||||
# 获取待审查的 PR
|
||||
pending_prs = self.get_pending_prs()
|
||||
|
||||
for pr in pending_prs:
|
||||
review_result = self.analyze_pr(pr)
|
||||
self.submit_review(pr, review_result)
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 工作流选择
|
||||
- **项目创建**: 使用 workflow-repo-setup
|
||||
- **日常维护**: 使用 workflow-issue-triage 和 workflow-pr-review
|
||||
- **版本发布**: 使用 workflow-release-notes
|
||||
- **团队管理**: 使用 workflow-sprint-report
|
||||
|
||||
### 2. 执行顺序
|
||||
典型的执行顺序:
|
||||
1. 项目初始化 → 2. Issue 管理 → 3. PR 审查 → 4. Release 发布 → 5. Sprint 报告
|
||||
|
||||
### 3. 错误处理
|
||||
```python
|
||||
def safe_workflow_execution(workflow_func, *args, **kwargs):
|
||||
"""安全执行工作流"""
|
||||
try:
|
||||
return workflow_func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
# 记录错误
|
||||
log_error(e)
|
||||
# 尝试恢复
|
||||
return handle_workflow_error(e, workflow_func, *args, **kwargs)
|
||||
```
|
||||
|
||||
### 4. 进度跟踪
|
||||
```python
|
||||
class WorkflowProgress:
|
||||
def __init__(self):
|
||||
self.current_step = 0
|
||||
self.total_steps = 5
|
||||
self.completed_steps = []
|
||||
self.failed_steps = []
|
||||
|
||||
def update_progress(self, step_name, success=True):
|
||||
if success:
|
||||
self.completed_steps.append(step_name)
|
||||
else:
|
||||
self.failed_steps.append(step_name)
|
||||
self.current_step += 1
|
||||
|
||||
def get_progress_report(self):
|
||||
progress = self.current_step / self.total_steps * 100
|
||||
return {
|
||||
'progress': f'{progress:.1f}%',
|
||||
'completed': self.completed_steps,
|
||||
'failed': self.failed_steps
|
||||
}
|
||||
```
|
||||
|
||||
## 扩展工作流
|
||||
|
||||
### 自定义工作流
|
||||
|
||||
```python
|
||||
# 创建自定义工作流
|
||||
def custom_workflow(owner, repo, custom_config):
|
||||
"""自定义工作流模板"""
|
||||
|
||||
# 1. 预检查
|
||||
if not validate_environment():
|
||||
return False
|
||||
|
||||
# 2. 执行自定义步骤
|
||||
for step in custom_config['steps']:
|
||||
execute_step(step)
|
||||
|
||||
# 3. 后处理
|
||||
cleanup_environment()
|
||||
|
||||
return True
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
| 问题 | 解决方案 |
|
||||
|------|----------|
|
||||
| 权限不足 | 检查 Token 权限 |
|
||||
| API 限制 | 添加重试机制 |
|
||||
| 数据格式错误 | 验证输入数据 |
|
||||
| 执行超时 | 增加超时时间 |
|
||||
|
||||
## References
|
||||
|
||||
- [workflow-issue-triage](../references/workflow-issue-triage.md) — Issue 分类
|
||||
- [workflow-pr-review](../references/workflow-pr-review.md) — PR 审查
|
||||
- [workflow-release-notes](../references/workflow-release-notes.md) — Release Notes
|
||||
- [workflow-repo-setup](../references/workflow-repo-setup.md) — 仓库初始化
|
||||
- [workflow-sprint-report](../references/workflow-sprint-report.md) — Sprint 报告
|
||||
- [gitlink-workflow](../SKILL.md) — 工作流总览
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
# Workflow: Community Ops(社区运营自动化)
|
||||
|
||||
> **前置条件:** 先阅读 [`../../gitlink-shared/SKILL.md`](../../../gitlink-shared/SKILL.md) 了解认证、全局参数和安全规则。
|
||||
> **AI Agent 工作流**:此工作流专为 AI Agent 设计,用于自动化社区运营。
|
||||
|
||||
AI Agent 自动完成社区运营任务,包括 Issue 分类、负责人分配、周报生成、Release Notes 发布。
|
||||
|
||||
## 工作流概述
|
||||
|
||||
Community Ops 工作流自动化社区运营的四个核心环节:Issue 分析 → 负责人分配 → 周报生成 → Release Notes 发布。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- **Issue 管理**:自动分类和分配新 Issue
|
||||
- **周报生成**:汇总本周数据生成社区周报
|
||||
- **发版管理**:生成 Release Notes 并发布
|
||||
- **定期运营**:每周/每月定期执行社区运营
|
||||
|
||||
## 触发词
|
||||
|
||||
- "社区运营" / "community ops"
|
||||
- "周报" / "weekly report"
|
||||
- "发版" / "release notes"
|
||||
- "Issue 分类" / "issue triage"
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--owner` | 否 | 仓库所有者(自动从 git remote 解析) |
|
||||
| `--repo` | 否 | 仓库名称(自动从 git remote 解析) |
|
||||
| `--weeks-ago` | 否 | 生成 N 周前的周报(默认 0 = 本周) |
|
||||
| `--dry-run` | 否 | 预览模式,不执行写入操作 |
|
||||
|
||||
## 工作流步骤
|
||||
|
||||
### 阶段 1:Issue 自动分类
|
||||
|
||||
**步骤 1.1** — 获取 open issues:
|
||||
|
||||
```bash
|
||||
gitlink-cli issue +list --owner {OWNER} --repo {REPO} --state open --limit 100 --format json
|
||||
```
|
||||
|
||||
**步骤 1.2** — 分类规则:
|
||||
|
||||
| 类型 | 标签 | 关键词 |
|
||||
|------|------|--------|
|
||||
| Bug | `bug` | bug, error, crash, fault, fix |
|
||||
| Feature | `feature` | feature, enhancement, add, support, request |
|
||||
| Question | `question` | how, question, help |
|
||||
| Docs | `documentation` | doc, readme, guide, tutorial, example |
|
||||
|
||||
**步骤 1.3** — 添加标签:
|
||||
|
||||
```bash
|
||||
gitlink-cli issue +label-add --owner {OWNER} --repo {REPO} --number {ISSUE_ID} --labels {LABEL}
|
||||
```
|
||||
|
||||
### 阶段 2:负责人分配(基于贡献度 AHP 评分)
|
||||
|
||||
**引用 [`workflow-contributor-growth.md`](workflow-contributor-growth.md) 的 AHP 评分模型**
|
||||
|
||||
负责人分配基于贡献者的 AHP 评分,优先分配给贡献度高的成员。
|
||||
|
||||
**步骤 2.1** — 收集数据:
|
||||
|
||||
```bash
|
||||
# 获取仓库成员
|
||||
gitlink-cli repo +members --owner {OWNER} --repo {REPO} --limit 50 --format json
|
||||
|
||||
# 获取 merged PR(统计贡献)
|
||||
gitlink-cli pr +list --owner {OWNER} --repo {REPO} --state merged --limit 100 --format json
|
||||
|
||||
# 获取 closed Issues(统计参与)
|
||||
gitlink-cli issue +list --owner {OWNER} --repo {REPO} --state closed --limit 100 --format json
|
||||
```
|
||||
|
||||
**步骤 2.2** — 计算每个成员的 AHP 分数:
|
||||
|
||||
| 维度 | 权重 | 计算方式 |
|
||||
|------|------|----------|
|
||||
| Issues Created | 15% | 该成员创建的 Issue 数 / 最大值 |
|
||||
| PRs Merged | 25% | 该成员合并的 PR 数 / 最大值 |
|
||||
| Code Changes | 30% | 该成员代码变更行数 / 最大值 |
|
||||
| Issue Comments | 15% | 该成员评论数 / 最大值 |
|
||||
| Team Member | 15% | 是成员=1,非成员=0 |
|
||||
|
||||
```
|
||||
Score = NI×15 + NM×25 + NL×30 + NC×15 + MS×15
|
||||
```
|
||||
|
||||
**步骤 2.3** — 筛选待分配 Issue 并分配:
|
||||
|
||||
```bash
|
||||
# 只分配 Bug 和 Feature 类型的 Issue
|
||||
# Bug → 标签含 "bug"/"缺陷"
|
||||
# Feature → 标签含 "feature"/"功能"
|
||||
|
||||
# 按分数从高到低排序成员
|
||||
# Bug:优先分配给最高分成员(确保快速解决)
|
||||
# Feature:按分数轮流分配(鼓励参与)
|
||||
|
||||
# 分配命令
|
||||
gitlink-cli issue +batch-assign --owner {OWNER} --repo {REPO} --numbers {ISSUE_NUMBERS} --assignee {LOGIN}
|
||||
```
|
||||
|
||||
**分配策略**:
|
||||
- Bug 类型:优先分配给贡献度最高的成员(快速响应)
|
||||
- Feature 类型:按贡献度轮流分配(鼓励更多人参与)
|
||||
- 成员不足时用 Round-robin 补充
|
||||
|
||||
### 阶段 3:周报生成
|
||||
|
||||
**步骤 3.1** — 收集数据:
|
||||
|
||||
```bash
|
||||
# Closed issues
|
||||
gitlink-cli issue +list --owner {OWNER} --repo {REPO} --state closed --limit 100 --format json
|
||||
|
||||
# Merged PRs
|
||||
gitlink-cli pr +list --owner {OWNER} --repo {REPO} --state merged --limit 100 --format json
|
||||
```
|
||||
|
||||
**步骤 3.2** — 周报模板:
|
||||
|
||||
```markdown
|
||||
# Community Weekly Report: {WEEK_START} ~ {WEEK_END}
|
||||
|
||||
## Summary
|
||||
- New Issues: **{NEW_COUNT}**
|
||||
- Closed Issues: **{CLOSED_COUNT}**
|
||||
- Merged PRs: **{MERGED_COUNT}**
|
||||
|
||||
## Issue Classification
|
||||
| Type | Count |
|
||||
|------|-------|
|
||||
| Bug | {BUG_COUNT} |
|
||||
| Feature | {FEATURE_COUNT} |
|
||||
| Question | {QUESTION_COUNT} |
|
||||
| Docs | {DOCS_COUNT} |
|
||||
|
||||
## Highlights
|
||||
- Auto-classified and labeled {TOTAL_CLASSIFIED} issues
|
||||
- Assigned responsible persons for bug and feature issues
|
||||
|
||||
---
|
||||
*Auto-generated by gitlink-cli community-ops workflow*
|
||||
```
|
||||
|
||||
**步骤 3.3** — 发布到 Wiki:
|
||||
|
||||
```bash
|
||||
gitlink-cli wiki +create --owner {OWNER} --repo {REPO} --title "{REPORT_TITLE}" --content "{REPORT_BODY}"
|
||||
```
|
||||
|
||||
### 阶段 4:Release Notes 生成
|
||||
|
||||
**引用 [`../workflow-release-notes.md`](workflow-release-notes.md)**
|
||||
|
||||
Release Notes 的生成遵循 Release Notes 工作流的规范:
|
||||
|
||||
1. **收集数据**:获取 commits、merged PR、closed Issue
|
||||
2. **分类整理**:按类型归类(新功能/Bug修复/改进/破坏性变更)
|
||||
3. **生成发布**:套用模板生成 Notes,创建 Release
|
||||
|
||||
**快捷命令**:
|
||||
|
||||
```bash
|
||||
gitlink-cli release +create --tag {TAG} --name "{NAME}" --body "{NOTES}"
|
||||
```
|
||||
|
||||
## 完整流程图
|
||||
|
||||
```
|
||||
Issue 分类 → 负责人分配 → 周报生成 → Release Notes
|
||||
↓ ↓ ↓ ↓
|
||||
添加标签 API PATCH Wiki 发布 Release 创建
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### AI 交互式
|
||||
|
||||
用户说:"帮我跑一下社区运营"
|
||||
|
||||
AI 应该:
|
||||
1. 确认 owner/repo(自动从 git remote 解析或询问用户)
|
||||
2. 依次执行四个阶段
|
||||
3. 展示每阶段的结果
|
||||
4. 询问是否需要调整
|
||||
|
||||
## 注意事项
|
||||
|
||||
- Issue 分类基于关键词匹配,可能不准确,建议人工审核
|
||||
- 负责人分配只针对 Bug 和 Feature 类型
|
||||
- 周报数据基于时间范围筛选,不是所有 open issues
|
||||
- Release Notes 生成引用 changelog 工作流的规范
|
||||
|
||||
## References
|
||||
|
||||
- [workflow-release-notes](workflow-release-notes.md) — Release Notes 生成
|
||||
- [workflow-issue-triage](workflow-issue-triage.md) — Issue 分类详情
|
||||
- [gitlink-issue](../../gitlink-issue/SKILL.md) — Issue 操作
|
||||
- [gitlink-wiki](../../gitlink-wiki/SKILL.md) — Wiki 操作
|
||||
- [gitlink-release](../../gitlink-release/SKILL.md) — Release 操作
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
# Workflow: Contributor Growth(贡献者成长体系)
|
||||
|
||||
> **前置条件:** 先阅读 [`../../gitlink-shared/SKILL.md`](../../../gitlink-shared/SKILL.md) 了解认证、全局参数和安全规则。
|
||||
> **AI Agent 工作流**:此工作流专为 AI Agent 设计,用于评估和激励团队贡献者。
|
||||
|
||||
AI Agent 自动收集贡献者数据,使用 AHP 权重模型计算贡献分数,生成排行榜和可视化报告,并可选择颁发 Badge。
|
||||
|
||||
## 工作流概述
|
||||
|
||||
Contributor Growth 工作流通过收集 Issue、PR、代码变更、评论等数据,使用 AHP(层次分析法)权重模型计算每个贡献者的综合分数,生成排行榜、HTML 报告和 Wiki 页面,并可自动颁发 Badge。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- **贡献评估**:量化团队成员的贡献程度
|
||||
- **排行榜生成**:生成贡献者排行榜
|
||||
- **激励机制**:通过 Badge 颁发激励贡献者
|
||||
- **团队协调**:了解团队成员的参与度
|
||||
|
||||
## 触发词
|
||||
|
||||
- "贡献者" / "contributor"
|
||||
- "排行榜" / "leaderboard"
|
||||
- "badge" / "徽章"
|
||||
- "成长" / "growth"
|
||||
- "评分" / "score"
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--owner` | 否 | 仓库所有者(自动从 git remote 解析) |
|
||||
| `--repo` | 否 | 仓库名称(自动从 git remote 解析) |
|
||||
| `--sample` | 否 | 采样 PR 数量获取代码统计(默认 10) |
|
||||
| `--award` | 否 | 自动创建 Badge 颁发 Issue |
|
||||
|
||||
## AHP 评分模型
|
||||
|
||||
| 维度 | 权重 | 数据来源 | 提取字段 |
|
||||
|------|------|----------|----------|
|
||||
| Issues Created | 15% | `issue +list` (open + closed) | `.data.issues[].author.login` 按作者统计 |
|
||||
| PRs Merged | 25% | `pr +list state=merged` | `.data.issues[].author_login` 按作者统计 |
|
||||
| Code Changes | 30% | `pr +files`(采样) | `.data.files[].addition` / `.deletion`(单数) |
|
||||
| Issue Comments | 15% | `issue +list` 直接提取 | `.data.issues[].comment_journals_count` |
|
||||
| Team Member | 15% | `repo +members` | `.data.members[].login` 判断是否成员 |
|
||||
|
||||
## Badge 等级
|
||||
|
||||
| Badge | 分数要求 | 说明 |
|
||||
|-------|----------|------|
|
||||
| Champion | >= 80 | 卓越贡献 |
|
||||
| Core Contributor | >= 60 | 核心贡献者 |
|
||||
| Active Contributor | >= 40 | 活跃贡献者 |
|
||||
| Contributor | >= 20 | 贡献者 |
|
||||
| Newcomer | < 20 | 新人 |
|
||||
|
||||
## 工作流步骤
|
||||
|
||||
### 步骤 1:收集数据
|
||||
|
||||
```bash
|
||||
# Open Issues
|
||||
gitlink-cli issue +list --owner {OWNER} --repo {REPO} --state open --limit 100 --format json
|
||||
|
||||
# Closed Issues
|
||||
gitlink-cli issue +list --owner {OWNER} --repo {REPO} --state closed --limit 100 --format json
|
||||
|
||||
# Merged PRs
|
||||
gitlink-cli pr +list --owner {OWNER} --repo {REPO} --state merged --limit 100 --format json
|
||||
|
||||
# 仓库成员
|
||||
gitlink-cli repo +members --owner {OWNER} --repo {REPO} --limit 100 --format json
|
||||
```
|
||||
|
||||
### 步骤 2:构建贡献者数据
|
||||
|
||||
遍历数据,为每个贡献者统计:
|
||||
- Issues:创建的 Issue 数量
|
||||
- Merged:合并的 PR 数量
|
||||
- Additions/Deletions:代码变更行数(采样)
|
||||
- Comments:评论数量(采样)
|
||||
|
||||
### 步骤 3:采样代码变更
|
||||
|
||||
对 merged PR 采样,获取代码变更统计:
|
||||
|
||||
```bash
|
||||
# 获取 PR 变更文件
|
||||
gitlink-cli pr +files --owner {OWNER} --repo {REPO} --id {PR_ID} --format json
|
||||
```
|
||||
|
||||
提取字段:`.data.files[].addition`、`.data.files[].deletion`(**注意:是单数形式,不是 additions/deletions**)
|
||||
|
||||
**已知问题**:
|
||||
- `pr +files` 输出的 JSON 在中文 Windows 下可能因编码问题导致解析失败(GBK vs UTF-8)
|
||||
- 如果 Python 解析报错 `JSONDecodeError` 或 `UnicodeDecodeError`,需要指定 `encoding='utf-8'`
|
||||
- 部分 PR 的 files API 可能返回异常,用 try/except 跳过即可
|
||||
- 降级方案:用 PR 数量估算代码变更量(每个 PR 估 100 行)
|
||||
|
||||
### 步骤 4:采样评论数量
|
||||
|
||||
有两种方式获取评论数据:
|
||||
|
||||
**方式 A(推荐)**:直接从 `issue +list` 返回的数据中提取
|
||||
- 字段:`.data.issues[].comment_journals_count`
|
||||
- 无需额外 API 调用,效率更高
|
||||
|
||||
**方式 B**:逐个 Issue 采样
|
||||
```bash
|
||||
gitlink-cli issue +view --owner {OWNER} --repo {REPO} --number {ISSUE_ID} --format json
|
||||
```
|
||||
- 字段:`.data.comment_journals_count`
|
||||
|
||||
**补充**:PR 的评论数可从 `pr +list` 返回的 `.data.issues[].journals_count` 获取
|
||||
|
||||
### 步骤 5:计算 AHP 分数
|
||||
|
||||
```bash
|
||||
# 归一化处理
|
||||
NI = Issues / MaxIssues
|
||||
NM = Merged / MaxMerged
|
||||
NL = Lines / MaxLines
|
||||
NC = Comments / MaxComments
|
||||
MS = IsMember ? 1 : 0
|
||||
|
||||
# 加权求和
|
||||
Score = NI * 15 + NM * 25 + NL * 30 + NC * 15 + MS * 15
|
||||
```
|
||||
|
||||
### 步骤 6:生成排行榜
|
||||
|
||||
输出格式:
|
||||
|
||||
```
|
||||
Rank Contributor Issues Merged +/- Lines Comments Score Badge
|
||||
---- ------------------ ------ ------ --------- -------- ------ -------------
|
||||
1 @contributor1 15 8 +1200/-300 45 82.5 Champion
|
||||
2 @contributor2 10 12 +800/-200 30 68.2 Core Contributor
|
||||
3 @contributor3 5 6 +400/-100 20 45.1 Active Contributor
|
||||
```
|
||||
|
||||
### 步骤 7:生成 HTML 报告
|
||||
|
||||
HTML 报告包含:
|
||||
- 汇总统计卡片
|
||||
- ECharts 饼图(分数分布)
|
||||
- 详细排行榜表格
|
||||
- AHP 权重说明
|
||||
|
||||
### 步骤 8:发布到 Wiki
|
||||
|
||||
```bash
|
||||
gitlink-cli wiki +create \
|
||||
--owner {OWNER} \
|
||||
--repo {REPO} \
|
||||
--title "Contributor Leaderboard {DATE}" \
|
||||
--content "{WIKI_CONTENT}"
|
||||
```
|
||||
|
||||
Wiki 内容包含:
|
||||
- 评分体系说明
|
||||
- 排行榜表格
|
||||
- 生成时间
|
||||
|
||||
### 步骤 9:颁发 Badge(可选)
|
||||
|
||||
如果指定了 `--award` 参数,为获奖者创建 Issue:
|
||||
|
||||
```bash
|
||||
gitlink-cli issue +create \
|
||||
--owner {OWNER} \
|
||||
--repo {REPO} \
|
||||
--title "Badge Award: {BADGE}" \
|
||||
--body "{ISSUE_BODY}"
|
||||
|
||||
gitlink-cli issue +label-add \
|
||||
--owner {OWNER} \
|
||||
--repo {REPO} \
|
||||
--number {ISSUE_ID} \
|
||||
--labels badge
|
||||
```
|
||||
|
||||
## 输出示例
|
||||
|
||||
### 控制台输出
|
||||
|
||||
```
|
||||
====== Contributor Growth System: zzx-coder/gitlink-cli ======
|
||||
|
||||
[STEP] Collecting data...
|
||||
[ OK] Issues(open:15 closed:30) PRs(merged:25) Members:8
|
||||
|
||||
[STEP] Building contributor profiles...
|
||||
[STEP] Analyzing PR code changes (sampling 10)...
|
||||
[STEP] Sampling issue comments...
|
||||
|
||||
[STEP] Calculating scores...
|
||||
|
||||
====== Contributor Rankings ======
|
||||
|
||||
Rank Contributor Issues Merged +/- Lines Comments Score Badge
|
||||
---- ------------------ ------ ------ --------- -------- ------ -------------
|
||||
1 @zzx-coder 12 8 +1500/-400 35 85.2 Champion
|
||||
2 @contributor1 8 6 +800/-200 25 62.1 Core Contributor
|
||||
3 @contributor2 5 4 +400/-100 15 42.3 Active Contributor
|
||||
|
||||
====== Generating HTML Report ======
|
||||
[ OK] HTML report: contrib-report-zzx-coder-gitlink-cli.html
|
||||
|
||||
[STEP] Publishing to Wiki...
|
||||
[ OK] Published to Wiki: Contributor Leaderboard 2026-07-02
|
||||
|
||||
====== Awarding Badges ======
|
||||
[ OK] Badge issue created: #45 - Badge Award: Champion (1 recipients)
|
||||
[ OK] Badge issue created: #46 - Badge Award: Core Contributor (1 recipients)
|
||||
|
||||
====== Complete ======
|
||||
|
||||
Contributors: 8
|
||||
HTML Report: contrib-report-zzx-coder-gitlink-cli.html
|
||||
Wiki: Contributor Leaderboard 2026-07-02
|
||||
Badges: Awarded
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 代码变更统计基于采样,不是全量数据
|
||||
- Badge 颁发会创建 issue,需要确认权限
|
||||
- HTML 报告使用 ECharts 需要网络连接
|
||||
- 建议定期运行(如每月)跟踪贡献趋势
|
||||
|
||||
## 已知问题与解决方案
|
||||
|
||||
| 问题 | 原因 | 解决方案 |
|
||||
|------|------|----------|
|
||||
| Python `UnicodeDecodeError: 'gbk' codec` | 中文 Windows 默认 GBK 编码,gitlink-cli 输出 UTF-8 | `open(file, encoding='utf-8')` |
|
||||
| `pr +files` JSON 解析失败 | 输出含特殊字符,GBK 解码破坏 UTF-8 序列 | 用 `encoding='utf-8'` 读取,或用 try/except 跳过 |
|
||||
| `pr +files` 字段名为 `addition` 不是 `additions` | GitLink API 使用单数形式 | 用 `f.get('addition', f.get('additions', 0))` 兼容 |
|
||||
| Issue 数据中 `comment_journals_count` 为 0 | 某些 Issue 确实没有评论 | 正常现象,不影响评分 |
|
||||
|
||||
## References
|
||||
|
||||
- [gitlink-issue](../../gitlink-issue/SKILL.md) — Issue 操作
|
||||
- [gitlink-pr](../../gitlink-pr/SKILL.md) — PR 操作
|
||||
- [gitlink-repo](../../gitlink-repo/SKILL.md) — 仓库操作
|
||||
- [gitlink-wiki](../../gitlink-wiki/SKILL.md) — Wiki 操作
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
# Workflow: Multi-Repo Collaboration(多仓库协同)
|
||||
|
||||
> **前置条件:** 先阅读 [`../../gitlink-shared/SKILL.md`](../../../gitlink-shared/SKILL.md) 了解认证、全局参数和安全规则。
|
||||
> **AI Agent 工作流**:此工作流专为 AI Agent 设计,用于跨仓库协同管理。
|
||||
|
||||
AI Agent 自动汇总组织下所有仓库的 Issue、PR、Release 状态,生成可视化 Dashboard,支持协同发版。
|
||||
|
||||
## 工作流概述
|
||||
|
||||
Multi-Repo Collaboration 工作流通过遍历组织下的所有仓库,收集各仓库的 Issue、PR、Release 数据,生成统一的状态 Dashboard,并支持跨仓库协同发版。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- **状态总览**:查看组织下所有仓库的健康状态
|
||||
- **跨仓库追踪**:追踪跨仓库的 Issue 和 PR
|
||||
- **协同发版**:多个仓库同时发布同一版本
|
||||
- **团队协调**:协调团队在多个项目间的工作
|
||||
|
||||
## 触发词
|
||||
|
||||
- "多仓库" / "multi repo"
|
||||
- "协同" / "collaboration"
|
||||
- "dashboard" / "看板"
|
||||
- "组织仓库" / "org repos"
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--org` | 是 | 组织名称 |
|
||||
| `--repos` | 否 | 指定仓库列表(逗号分隔),默认遍历所有仓库 |
|
||||
| `--release` | 否 | 协同发版的版本号 |
|
||||
| `--output` | 否 | Dashboard 输出文件(默认 dashboard.html) |
|
||||
|
||||
## 工作流步骤
|
||||
|
||||
### 步骤 1:列出组织仓库
|
||||
|
||||
```bash
|
||||
# 获取组织下所有仓库
|
||||
gitlink-cli repo +list --user {ORG} --limit 100 --format json
|
||||
```
|
||||
|
||||
提取仓库列表:`.data.projects[]` 或 `.data[]`,字段:`.name` 或 `.identifier`
|
||||
|
||||
### 步骤 2:收集各仓库数据
|
||||
|
||||
遍历每个仓库,收集 Issue、PR、Release 数据:
|
||||
|
||||
```bash
|
||||
# Open Issues
|
||||
gitlink-cli issue +list --owner {ORG} --repo {REPO} --state open --limit 50 --format json
|
||||
|
||||
# Closed Issues
|
||||
gitlink-cli issue +list --owner {ORG} --repo {REPO} --state closed --limit 50 --format json
|
||||
|
||||
# Open PRs
|
||||
gitlink-cli pr +list --owner {ORG} --repo {REPO} --state open --limit 50 --format json
|
||||
|
||||
# Merged PRs
|
||||
gitlink-cli pr +list --owner {ORG} --repo {REPO} --state merged --limit 50 --format json
|
||||
|
||||
# Latest Release
|
||||
gitlink-cli release +list --owner {ORG} --repo {REPO} --limit 1 --format json
|
||||
```
|
||||
|
||||
### 步骤 3:生成 Dashboard
|
||||
|
||||
Dashboard 包含:
|
||||
- **汇总卡片**:总仓库数、Open Issues、Open PRs、总活动量
|
||||
- **仓库状态表**:每个仓库的 Issue/PR/Release 状态和健康度
|
||||
|
||||
**健康度判断**:
|
||||
- Open Issues <= 10 → Healthy(绿色)
|
||||
- Open Issues 11-20 → Moderate(橙色)
|
||||
- Open Issues > 20 → Needs Attention(红色)
|
||||
|
||||
### 步骤 4:协同发版(可选)
|
||||
|
||||
如果指定了 `--release` 参数,为所有仓库创建 Release:
|
||||
|
||||
```bash
|
||||
gitlink-cli release +create \
|
||||
--owner {ORG} \
|
||||
--repo {REPO} \
|
||||
--tag {VERSION} \
|
||||
--name "Release {VERSION}" \
|
||||
--body "Coordinated release {VERSION} for {REPO}"
|
||||
```
|
||||
|
||||
## 数据提取
|
||||
|
||||
### Issue 数量
|
||||
|
||||
```bash
|
||||
# Open Issues 数量
|
||||
OPEN_COUNT=$(gitlink-cli issue +list --owner {ORG} --repo {REPO} --state open --limit 50 --format json | jq '.data.issues | length')
|
||||
|
||||
# Closed Issues 数量
|
||||
CLOSED_COUNT=$(gitlink-cli issue +list --owner {ORG} --repo {REPO} --state closed --limit 50 --format json | jq '.data.issues | length')
|
||||
```
|
||||
|
||||
### PR 数量
|
||||
|
||||
```bash
|
||||
# Open PRs 数量
|
||||
OPEN_PRS=$(gitlink-cli pr +list --owner {ORG} --repo {REPO} --state open --limit 50 --format json | jq '.data.issues | length')
|
||||
|
||||
# Merged PRs 数量
|
||||
MERGED_PRS=$(gitlink-cli pr +list --owner {ORG} --repo {REPO} --state merged --limit 50 --format json | jq '.data.issues | length')
|
||||
```
|
||||
|
||||
### Release 状态
|
||||
|
||||
```bash
|
||||
# 最新 Release
|
||||
LATEST_RELEASE=$(gitlink-cli release +list --owner {ORG} --repo {REPO} --limit 1 --format json | jq -r '.data.releases[0].tag_name // "none"')
|
||||
```
|
||||
|
||||
## 输出示例
|
||||
|
||||
### Dashboard HTML
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Multi-Repo Collaboration Dashboard</title>
|
||||
<style>
|
||||
/* 样式定义 */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Multi-Repo Collaboration Dashboard</h1>
|
||||
<div class="summary">
|
||||
<div class="card">Total Repos: 10</div>
|
||||
<div class="card">Open Issues: 45</div>
|
||||
<div class="card">Open PRs: 12</div>
|
||||
<div class="card">Total Activity: 156</div>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Repository</th>
|
||||
<th>Open Issues</th>
|
||||
<th>Closed Issues</th>
|
||||
<th>Open PRs</th>
|
||||
<th>Merged PRs</th>
|
||||
<th>Latest Release</th>
|
||||
<th>Health</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- 仓库状态行 -->
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### 控制台输出
|
||||
|
||||
```
|
||||
====== Multi-Repo Collaboration Dashboard ======
|
||||
|
||||
[STEP] Fetching repositories for org: myorg...
|
||||
[ OK] Found 10 repositories
|
||||
|
||||
[STEP] Processing repo1...
|
||||
[ OK] repo1 : Issues(open:5 closed:12) PRs(open:2 merged:8) Release:v1.2.0
|
||||
|
||||
[STEP] Processing repo2...
|
||||
[ OK] repo2 : Issues(open:15 closed:20) PRs(open:5 merged:15) Release:v2.0.0
|
||||
|
||||
====== Generating Dashboard ======
|
||||
[ OK] Dashboard saved to: dashboard.html
|
||||
|
||||
====== Multi-Repo Dashboard Complete ======
|
||||
|
||||
Repos processed: 10
|
||||
Total issues: 156 (open: 45)
|
||||
Total PRs: 89 (open: 12)
|
||||
Dashboard: dashboard.html
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 大量仓库时注意 API 限流,建议添加延时
|
||||
- Release 状态可能需要二次验证(GitLink API bug)
|
||||
- Dashboard HTML 文件可以用浏览器打开查看
|
||||
- 协同发版前建议先预览各仓库状态
|
||||
|
||||
## References
|
||||
|
||||
- [gitlink-repo](../../gitlink-repo/SKILL.md) — 仓库操作
|
||||
- [gitlink-issue](../../gitlink-issue/SKILL.md) — Issue 操作
|
||||
- [gitlink-pr](../../gitlink-pr/SKILL.md) — PR 操作
|
||||
- [gitlink-release](../../gitlink-release/SKILL.md) — Release 操作
|
||||
- [gitlink-org](../../gitlink-org/SKILL.md) — 组织管理
|
||||
|
|
@ -140,106 +140,6 @@ done)"
|
|||
gitlink-cli release +create --tag $NEW_TAG --name "$NEW_TAG" --body "$RELEASE_NOTES"
|
||||
```
|
||||
|
||||
## 完整工作流示例
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Release Notes 自动生成脚本
|
||||
|
||||
OWNER="myuser"
|
||||
REPO="myproject"
|
||||
NEW_VERSION=$1
|
||||
|
||||
if [ -z "$NEW_VERSION" ]; then
|
||||
echo "使用方法: $0 <version>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "为版本 $NEW_VERSION 生成 Release Notes..."
|
||||
|
||||
# 1. 获取上一个版本
|
||||
PREV_VERSION=$(gitlink-cli release +list --owner $OWNER --repo $REPO --format json | \
|
||||
jq -r '.data.releases[0].tag_name')
|
||||
|
||||
echo "上一个版本: $PREV_VERSION"
|
||||
echo "新版本: $NEW_VERSION"
|
||||
|
||||
# 2. 获取提交比较
|
||||
COMPARE_DATA=$(gitlink-cli api GET "/$OWNER/$REPO/compare/$PREV_VERSION...$NEW_VERSION" --format json)
|
||||
COMMITS=$(echo "$COMPARE_DATA" | jq -r '.data.commits')
|
||||
|
||||
# 3. 获取已关闭的 Issue
|
||||
ISSUES=$(gitlink-cli issue +list --owner $OWNER --repo $REPO --state closed --format json | \
|
||||
jq '.data.issues[]')
|
||||
|
||||
# 4. 获取已合并的 PR
|
||||
PRS=$(gitlink-cli pr +list --owner $OWNER --repo $REPO --state merged --format json | \
|
||||
jq '.data.prs[]')
|
||||
|
||||
# 5. 分析变更数据
|
||||
FEATURE_COUNT=0
|
||||
BUG_FIX_COUNT=0
|
||||
ENHANCEMENT_COUNT=0
|
||||
BREAKING_COUNT=0
|
||||
|
||||
# 分析 Issue
|
||||
FEATURES=$(echo "$ISSUES" | jq -r 'select(.issue_tags[]?.name == "feature") | "- \(.subject) (#\(.id))"')
|
||||
BUG_FIXES=$(echo "$ISSUES" | jq -r 'select(.issue_tags[]?.name == "bug") | "- \(.subject) (#\(.id))"')
|
||||
ENHANCEMENTS=$(echo "$ISSUES" | jq -r 'select(.issue_tags[]?.name == "enhancement") | "- \(.subject) (#\(.id))"')
|
||||
|
||||
# 分析提交中的破坏性变更
|
||||
BREAKING_CHANGES=$(echo "$COMMITS" | jq -r 'select(.commit.message | contains("BREAKING")) | "- \(.commit.message | split("\n")[0])"')
|
||||
|
||||
# 统计数量
|
||||
FEATURE_COUNT=$(echo "$FEATURES" | grep -c "^-" || echo "0")
|
||||
BUG_FIX_COUNT=$(echo "$BUG_FIXES" | grep -c "^-" || echo "0")
|
||||
ENHANCEMENT_COUNT=$(echo "$ENHANCEMENTS" | grep -c "^-" || echo "0")
|
||||
BREAKING_COUNT=$(echo "$BREAKING_CHANGES" | grep -c "^-" || echo "0")
|
||||
|
||||
# 6. 生成 Release Notes
|
||||
RELEASE_NOTES="# 🎉 Release $NEW_VERSION
|
||||
|
||||
## 📊 变更统计
|
||||
- **新功能**: $FEATURE_COUNT 个
|
||||
- **Bug 修复**: $BUG_FIX_COUNT 个
|
||||
- **功能改进**: $ENHANCEMENT_COUNT 个
|
||||
- **破坏性变更**: $BREAKING_COUNT 个
|
||||
|
||||
## ✨ 新功能
|
||||
$FEATURES
|
||||
|
||||
## 🐛 Bug 修复
|
||||
$BUG_FIXES
|
||||
|
||||
## 🔧 功能改进
|
||||
$ENHANCEMENTS
|
||||
|
||||
## ⚠️ 破坏性变更
|
||||
$BREAKING_CHANGES
|
||||
|
||||
## 🙏 贡献者
|
||||
感谢所有参与此版本开发的贡献者!
|
||||
|
||||
## 📥 安装
|
||||
\`\`\`bash
|
||||
npm install $OWNER/$REPO@$NEW_VERSION
|
||||
\`\`\`
|
||||
|
||||
## 📚 文档
|
||||
完整文档请查看: https://www.gitlink.org.cn/$OWNER/$REPO/wiki
|
||||
|
||||
---
|
||||
**完整变更日志**: https://www.gitlink.org.cn/$OWNER/$REPO/compare/$PREV_VERSION...$NEW_VERSION"
|
||||
|
||||
# 7. 创建 Release
|
||||
echo "创建 Release $NEW_VERSION..."
|
||||
gitlink-cli release +create --owner $OWNER --repo $REPO \
|
||||
--tag $NEW_VERSION --name "$NEW_VERSION" --body "$RELEASE_NOTES"
|
||||
|
||||
echo "Release $NEW_VERSION 创建完成!"
|
||||
```
|
||||
|
||||
## AI Agent 集成示例
|
||||
|
||||
Claude Code 等 AI Agent 可以深度集成此工作流:
|
||||
|
|
|
|||
|
|
@ -1,15 +1,6 @@
|
|||
# ----------------------------------------------------------------
|
||||
# ----------------------------------------------------------------
|
||||
# Scenario 1: Community Operations Automation
|
||||
# Flow: Issue auto-classify -> Assign responsible -> Weekly report -> Release notes
|
||||
#
|
||||
# Commands chained:
|
||||
# 1. issue +list -- fetch open issues
|
||||
# 2. issue +label-add -- add classification labels
|
||||
# 3. repo +members -- get repo members
|
||||
# 4. api PATCH -- assign responsible person
|
||||
# 5. pr +list -- collect merged PRs for weekly report
|
||||
# 6. wiki +create -- publish community weekly report
|
||||
# 7. release +create -- publish release notes
|
||||
# ----------------------------------------------------------------
|
||||
#Requires -Version 5.1
|
||||
|
||||
|
|
@ -26,11 +17,6 @@ Import-Module "$PSScriptRoot/lib/common.psm1" -Force
|
|||
|
||||
if ($Help) {
|
||||
Write-Host "Usage: powershell 01-community-ops.ps1 -Owner OWNER -Repo REPO [-WeeksAgo N] [-DryRun]"
|
||||
Write-Host ""
|
||||
Write-Host " -Owner OWNER Repository owner (org or user)"
|
||||
Write-Host " -Repo REPO Repository name"
|
||||
Write-Host " -WeeksAgo N Generate report for N weeks ago (default: 0 = this week)"
|
||||
Write-Host " -DryRun Preview actions without executing"
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +24,6 @@ Check-Auth
|
|||
$r = Resolve-OwnerRepo $Owner $Repo
|
||||
$Owner = $r.Owner; $Repo = $r.Repo
|
||||
|
||||
# Classification keywords
|
||||
$BugKw = @('bug','error','crash','fault','fix')
|
||||
$FeatureKw = @('feature','enhancement','add','support','request')
|
||||
$QuestionKw = @('how','question','help')
|
||||
|
|
@ -49,7 +34,7 @@ Log-Title "Phase 1: Issue Auto-Classification"
|
|||
# ----------------------------------------------------------------
|
||||
|
||||
Log-Step "Fetching open issues..."
|
||||
$issuesJson = Invoke-GLCheck issue,+list,--owner,$Owner,--repo,$Repo,--state,open,--limit,100
|
||||
$issuesJson = Invoke-GLCheck "issue", "+list", "--owner", $Owner, "--repo", $Repo, "--state", "open", "--limit", "100"
|
||||
if (-not $issuesJson) { Log-Err "Failed to fetch issues"; exit 1 }
|
||||
|
||||
$issues = @($issuesJson.data.issues)
|
||||
|
|
@ -60,7 +45,6 @@ $BugIds = @(); $FeatureIds = @(); $QuestionIds = @(); $DocsIds = @()
|
|||
|
||||
if ($issueCount -gt 0) {
|
||||
Log-Step "Classifying issues by content..."
|
||||
|
||||
foreach ($issue in $issues) {
|
||||
$id = $issue.id
|
||||
$title = if ($issue.subject) { $issue.subject } elseif ($issue.title) { $issue.title } else { "" }
|
||||
|
|
@ -96,7 +80,6 @@ if ($issueCount -gt 0) {
|
|||
Log-Info " Questions: $($QuestionIds.Count)"
|
||||
Log-Info " Docs: $($DocsIds.Count)"
|
||||
|
||||
# Apply labels
|
||||
$labelGroups = @(
|
||||
@{ Ids = $BugIds; Label = "bug" },
|
||||
@{ Ids = $FeatureIds; Label = "feature" },
|
||||
|
|
@ -107,7 +90,7 @@ if ($issueCount -gt 0) {
|
|||
if ($g.Ids.Count -gt 0) {
|
||||
Log-Step "Labeling $($g.Label) issues..."
|
||||
foreach ($id in $g.Ids) {
|
||||
Invoke-GL issue,+label-add,--owner,$Owner,--repo,$Repo,--number,$id,--labels,$g.Label | Out-Null
|
||||
Invoke-GL "issue", "+label-add", "--owner", $Owner, "--repo", $Repo, "--number", $id, "--labels", $g.Label | Out-Null
|
||||
}
|
||||
Log-Ok "Labeled $($g.Ids.Count) $($g.Label) issues"
|
||||
}
|
||||
|
|
@ -119,7 +102,7 @@ Log-Title "Phase 2: Assign Responsible Persons"
|
|||
# ----------------------------------------------------------------
|
||||
|
||||
Log-Step "Fetching repo members..."
|
||||
$membersJson = Invoke-GL repo,+members,--owner,$Owner,--repo,$Repo,--limit,50
|
||||
$membersJson = Invoke-GL "repo", "+members", "--owner", $Owner, "--repo", $Repo, "--limit", "50"
|
||||
$members = @()
|
||||
if ($membersJson) {
|
||||
$md = $membersJson.data
|
||||
|
|
@ -141,7 +124,7 @@ if ($memberLogins.Count -gt 0) {
|
|||
foreach ($id in $assignIds) {
|
||||
$assignee = $memberLogins[$idx % $memberLogins.Count]
|
||||
$bodyJson = "{`"assigned_to_id`": `"$assignee`"}"
|
||||
Invoke-GL api,PATCH,"/v1/$Owner/$Repo/issues/$id",--body,$bodyJson | Out-Null
|
||||
Invoke-GL "api", "PATCH", "/v1/$Owner/$Repo/issues/$id", "--body", $bodyJson | Out-Null
|
||||
Log-Info " Assigned #$id -> @$assignee"
|
||||
$idx++
|
||||
}
|
||||
|
|
@ -160,38 +143,128 @@ $weekEnd = Get-DateToday
|
|||
|
||||
Log-Step "Collecting weekly data (week of $weekStart)..."
|
||||
|
||||
$closedJson = Invoke-GL issue,+list,--owner,$Owner,--repo,$Repo,--state,closed,--limit,100
|
||||
$closedJson = Invoke-GL "issue", "+list", "--owner", $Owner, "--repo", $Repo, "--state", "closed", "--limit", "100"
|
||||
$closedCount = if ($closedJson) { @($closedJson.data.issues).Count } else { 0 }
|
||||
|
||||
$mergedJson = Invoke-GL pr,+list,--owner,$Owner,--repo,$Repo,--state,merged,--limit,100
|
||||
$mergedJson = Invoke-GL "pr", "+list", "--owner", $Owner, "--repo", $Repo, "--state", "merged", "--limit", "100"
|
||||
$mergedData = @()
|
||||
$mergedCount = 0
|
||||
if ($mergedJson) {
|
||||
$d = $mergedJson.data
|
||||
if ($d.issues) { $mergedData = @($d.issues) }
|
||||
elseif ($d.pulls) { $mergedData = @($d.pulls) }
|
||||
elseif ($d -is [array]) { $mergedData = $d }
|
||||
$mergedCount = $mergedData.Count
|
||||
}
|
||||
$mergedCount = $mergedData.Count
|
||||
|
||||
$newIssuesCount = $issueCount
|
||||
$totalClassified = $BugIds.Count + $FeatureIds.Count + $QuestionIds.Count + $DocsIds.Count
|
||||
|
||||
$reportTitle = "Community Weekly Report: $weekStart ~ $weekEnd"
|
||||
$reportBody = "# $reportTitle" + "`n`n"
|
||||
$reportBody += "## Summary" + "`n"
|
||||
$reportBody += "- New Issues: **$newIssuesCount**" + "`n"
|
||||
$reportBody += "- Closed Issues: **$closedCount**" + "`n"
|
||||
$reportBody += "- Merged PRs: **$mergedCount**" + "`n`n"
|
||||
$reportBody += "## Issue Classification" + "`n"
|
||||
$reportBody += "| Type | Count |" + "`n"
|
||||
$reportBody += "|------|-------|" + "`n"
|
||||
$reportBody += "## 概览" + "`n"
|
||||
$reportBody += "- 仓库: **$Owner/$Repo**" + "`n"
|
||||
$reportBody += "- 当前开放 Issue: **$newIssuesCount**" + "`n"
|
||||
$reportBody += "- 已关闭 Issue: **$closedCount**" + "`n"
|
||||
$reportBody += "- 已合并 PR: **$mergedCount**" + "`n`n"
|
||||
|
||||
# 分类汇总
|
||||
$reportBody += "## Issue 分类汇总" + "`n"
|
||||
$reportBody += "| 类型 | 数量 |" + "`n"
|
||||
$reportBody += "|------|------|" + "`n"
|
||||
$reportBody += "| Bug | $($BugIds.Count) |" + "`n"
|
||||
$reportBody += "| Feature | $($FeatureIds.Count) |" + "`n"
|
||||
$reportBody += "| Question | $($QuestionIds.Count) |" + "`n"
|
||||
$reportBody += "| Docs | $($DocsIds.Count) |" + "`n`n"
|
||||
$reportBody += "## Highlights" + "`n"
|
||||
$reportBody += "- Auto-classified and labeled $totalClassified issues" + "`n"
|
||||
$reportBody += "- Assigned responsible persons for bug and feature issues" + "`n`n"
|
||||
|
||||
# 开放 Issue 清单(含分类标记)
|
||||
$reportBody += "## 当前开放 Issue 清单" + "`n"
|
||||
$reportBody += "| # | 标题 | 分类 | 创建时间 |" + "`n"
|
||||
$reportBody += "|---|------|------|----------|" + "`n"
|
||||
foreach ($issue in $issues) {
|
||||
$id = $issue.id
|
||||
$title = if ($issue.subject) { $issue.subject } elseif ($issue.title) { $issue.title } else { "-" }
|
||||
$title = ($title -replace '\|', '\\|')
|
||||
$cat = if ($BugIds -contains $id) { "Bug" }
|
||||
elseif ($FeatureIds -contains $id) { "Feature" }
|
||||
elseif ($QuestionIds -contains $id) { "Question" }
|
||||
elseif ($DocsIds -contains $id) { "Docs" }
|
||||
else { "-" }
|
||||
$created = if ($issue.created_at) { $issue.created_at } else { "-" }
|
||||
$reportBody += "| #$id | $title | $cat | $created |" + "`n"
|
||||
}
|
||||
$reportBody += "`n"
|
||||
|
||||
# 已关闭 Issue 清单
|
||||
$reportBody += "## 近期已关闭 Issue" + "`n"
|
||||
if ($closedCount -gt 0) {
|
||||
$closedIssues = @($closedJson.data.issues)
|
||||
$closedLimit = [Math]::Min($closedCount, 15)
|
||||
$reportBody += "| # | 标题 |" + "`n"
|
||||
$reportBody += "|---|------|" + "`n"
|
||||
for ($i = 0; $i -lt $closedLimit; $i++) {
|
||||
$it = $closedIssues[$i]
|
||||
$cid = if ($it.id) { $it.id } elseif ($it.number) { $it.number } else { "-" }
|
||||
$ctitle = if ($it.subject) { $it.subject } elseif ($it.title) { $it.title } else { "-" }
|
||||
$ctitle = ($ctitle -replace '\|', '\\|')
|
||||
$reportBody += "| #$cid | $ctitle |" + "`n"
|
||||
}
|
||||
if ($closedCount -gt $closedLimit) {
|
||||
$reportBody += "| ... | 还有 $($closedCount - $closedLimit) 条 |`n"
|
||||
}
|
||||
} else {
|
||||
$reportBody += "_本周无关闭记录_" + "`n"
|
||||
}
|
||||
$reportBody += "`n"
|
||||
|
||||
# 已合并 PR 清单(含作者)
|
||||
$reportBody += "## 近期已合并 PR" + "`n"
|
||||
if ($mergedCount -gt 0) {
|
||||
$prLimit = [Math]::Min($mergedCount, 15)
|
||||
$reportBody += "| # | 标题 | 作者 |" + "`n"
|
||||
$reportBody += "|---|------|------|" + "`n"
|
||||
for ($i = 0; $i -lt $prLimit; $i++) {
|
||||
$pr = $mergedData[$i]
|
||||
$prId = if ($pr.id) { $pr.id } elseif ($pr.number) { $pr.number } else { "-" }
|
||||
$ptitle = if ($pr.subject) { $pr.subject } elseif ($pr.title) { $pr.title } else { "-" }
|
||||
$ptitle = ($ptitle -replace '\|', '\\|')
|
||||
$pauthor = if ($pr.author -and $pr.author.login) { $pr.author.login } elseif ($pr.user -and $pr.user.login) { $pr.user.login } else { "-" }
|
||||
$reportBody += "| #$prId | $ptitle | @$pauthor |" + "`n"
|
||||
}
|
||||
if ($mergedCount -gt $prLimit) {
|
||||
$reportBody += "| ... | 还有 $($mergedCount - $prLimit) 条合并 PR |`n"
|
||||
}
|
||||
} else {
|
||||
$reportBody += "_本周无合并记录_" + "`n"
|
||||
}
|
||||
$reportBody += "`n"
|
||||
|
||||
# 贡献者排行(按合并 PR 数)
|
||||
$reportBody += "## 贡献者排行(按合并 PR 数)" + "`n"
|
||||
if ($mergedCount -gt 0) {
|
||||
$contributorMap = @{}
|
||||
foreach ($pr in $mergedData) {
|
||||
$login = if ($pr.author -and $pr.author.login) { $pr.author.login } elseif ($pr.user -and $pr.user.login) { $pr.user.login } else { $null }
|
||||
if ($login) {
|
||||
if ($contributorMap.ContainsKey($login)) { $contributorMap[$login]++ }
|
||||
else { $contributorMap[$login] = 1 }
|
||||
}
|
||||
}
|
||||
$reportBody += "| 排名 | 贡献者 | 合并 PR 数 |" + "`n"
|
||||
$reportBody += "|------|--------|------------|" + "`n"
|
||||
$rank = 1
|
||||
foreach ($kv in ($contributorMap.GetEnumerator() | Sort-Object Value -Descending)) {
|
||||
$reportBody += "| $rank | @$($kv.Name) | $($kv.Value) |" + "`n"
|
||||
$rank++
|
||||
}
|
||||
} else {
|
||||
$reportBody += "_本周无合并记录_" + "`n"
|
||||
}
|
||||
$reportBody += "`n"
|
||||
|
||||
$reportBody += "## 本周自动化执行" + "`n"
|
||||
$reportBody += "- 自动分类并打标 Issue: **$totalClassified** 条" + "`n"
|
||||
$reportBody += "- 已为 Bug/Feature 类 Issue 指派负责人" + "`n`n"
|
||||
$reportBody += "---" + "`n"
|
||||
$reportBody += "*Auto-generated by gitlink-cli community-ops workflow*"
|
||||
|
||||
|
|
@ -200,12 +273,8 @@ Write-Host ""
|
|||
Write-Host $reportBody
|
||||
|
||||
Log-Step "Publishing weekly report to Wiki..."
|
||||
$wikiResult = Invoke-GL wiki,+create,--owner,$Owner,--repo,$Repo,--title,$reportTitle,--body,$reportBody
|
||||
if ($wikiResult -and (Get-JsonOk ($wikiResult | ConvertFrom-Json))) {
|
||||
Log-Ok "Weekly report published to Wiki"
|
||||
} else {
|
||||
Log-Warn "Wiki publish may have failed (wiki module might not be enabled)"
|
||||
}
|
||||
$wikiResult = Invoke-GL "wiki", "+create", "--owner", $Owner, "--repo", $Repo, "--title", $reportTitle, "--content", $reportBody
|
||||
if ($wikiResult -and $wikiResult.ok) { Log-Ok "Weekly report published to Wiki" } else { Log-Warn "Wiki publish may have failed" }
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
Log-Title "Phase 4: Auto-Publish Release Notes"
|
||||
|
|
@ -242,12 +311,8 @@ if ($closedCount -gt 0) {
|
|||
$releaseBody += "`n`n---`n*Auto-generated by gitlink-cli community-ops workflow*"
|
||||
|
||||
Log-Step "Creating release: $tagName..."
|
||||
$releaseResult = Invoke-GL release,+create,--owner,$Owner,--repo,$Repo,--tag,$tagName,--name,$releaseName,--body,$releaseBody
|
||||
if ($releaseResult -and (Get-JsonOk ($releaseResult | ConvertFrom-Json))) {
|
||||
Log-Ok "Release $tagName created successfully"
|
||||
} else {
|
||||
Log-Warn "Release creation may have failed (tag might already exist)"
|
||||
}
|
||||
$releaseResult = Invoke-GL "release", "+create", "--owner", $Owner, "--repo", $Repo, "--tag", $tagName, "--name", $releaseName, "--body", $releaseBody
|
||||
if ($releaseResult -and $releaseResult.ok) { Log-Ok "Release $tagName created successfully" } else { Log-Warn "Release creation may have failed (tag might already exist)" }
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
Log-Title "Community Operations Complete"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# ----------------------------------------------------------------
|
||||
# ----------------------------------------------------------------
|
||||
# Scenario 3: One-Click Project Initialization
|
||||
# Flow: Input description -> Create repo -> README/CONTRIBUTING/CI config ->
|
||||
# Initial Issues -> Branch protection -> Initial Release
|
||||
|
|
@ -53,7 +53,7 @@ Divider
|
|||
# -- Step 1: Create Repository --
|
||||
Log-Step "Creating repository..."
|
||||
$privateStr = if ($Private) { "true" } else { "false" }
|
||||
$repoResult = Invoke-GLCheck repo,+create,--owner,$Owner,--name,$Name,--description,$Description,--private,$privateStr
|
||||
$repoResult = Invoke-GLCheck "repo", "+create", "--owner", $Owner, "--name", $Name, "--description", $Description, "--private", $privateStr
|
||||
if ($repoResult) {
|
||||
Log-Ok "Repository created: $Owner/$Name"
|
||||
} else {
|
||||
|
|
@ -92,8 +92,8 @@ $readmeContent += "This project is licensed under the MIT License."
|
|||
|
||||
$wikiOk = $false
|
||||
for ($attempt = 1; $attempt -le 3; $attempt++) {
|
||||
$wikiResult = Invoke-GL wiki,+create,--owner,$Owner,--repo,$Name,--title,"README",--body,$readmeContent
|
||||
if ($wikiResult -and (Get-JsonOk ($wikiResult | ConvertFrom-Json))) {
|
||||
$wikiResult = Invoke-GL "wiki", "+create", "--owner", $Owner, "--repo", $Name, "--title", "README", "--content", $readmeContent
|
||||
if ($wikiResult -and (Get-JsonOk $wikiResult)) {
|
||||
Log-Ok "README created"
|
||||
$wikiOk = $true
|
||||
break
|
||||
|
|
@ -127,8 +127,8 @@ $contribContent += "- Include environment details"
|
|||
|
||||
$wikiOk = $false
|
||||
for ($attempt = 1; $attempt -le 3; $attempt++) {
|
||||
$wikiContrib = Invoke-GL wiki,+create,--owner,$Owner,--repo,$Name,--title,"CONTRIBUTING",--body,$contribContent
|
||||
if ($wikiContrib -and (Get-JsonOk ($wikiContrib | ConvertFrom-Json))) {
|
||||
$wikiContrib = Invoke-GL "wiki", "+create", "--owner", $Owner, "--repo", $Name, "--title", "CONTRIBUTING", "--content", $contribContent
|
||||
if ($wikiContrib -and (Get-JsonOk $wikiContrib)) {
|
||||
Log-Ok "CONTRIBUTING guide created"
|
||||
$wikiOk = $true
|
||||
break
|
||||
|
|
@ -150,7 +150,7 @@ $ciContent += "3. **Deploy**: Deploy to staging (master branch only)" + "`n`n"
|
|||
$ciContent += "### Configuration" + "`n`n"
|
||||
$ciContent += "Create a ``.gitlink-ci.yml`` file in the repository root."
|
||||
|
||||
Invoke-GL wiki,+create,--owner,$Owner,--repo,$Name,--title,"CI/CD Configuration",--body,$ciContent | Out-Null
|
||||
Invoke-GL "wiki", "+create", "--owner", $Owner, "--repo", $Name, "--title", "CI/CD Configuration", "--content", $ciContent | Out-Null
|
||||
Log-Ok "CI/CD configuration guide created"
|
||||
|
||||
# -- Step 5: Create Initial Issues --
|
||||
|
|
@ -165,13 +165,13 @@ $issuesToCreate = @(
|
|||
)
|
||||
|
||||
foreach ($entry in $issuesToCreate) {
|
||||
$issueResult = Invoke-GL issue,+create,--owner,$Owner,--repo,$Name,--title,$entry.Title,--body,$entry.Body
|
||||
$issueResult = Invoke-GL "issue", "+create", "--owner", $Owner, "--repo", $Name, "--title", $entry.Title, "--body", $entry.Body
|
||||
if ($issueResult) {
|
||||
try {
|
||||
$issueJson = $issueResult | ConvertFrom-Json
|
||||
$issueJson = $issueResult
|
||||
$issueNum = if ($issueJson.data.id) { $issueJson.data.id } elseif ($issueJson.data.number) { $issueJson.data.number } else { $null }
|
||||
if ($issueNum) {
|
||||
Invoke-GL issue,+label-add,--owner,$Owner,--repo,$Name,--number,$issueNum,--labels,$entry.Label | Out-Null
|
||||
Invoke-GL "issue", "+label-add", "--owner", $Owner, "--repo", $Name, "--number", $issueNum, "--labels", $entry.Label | Out-Null
|
||||
Log-Ok "Issue created: #$issueNum - $($entry.Title)"
|
||||
}
|
||||
} catch {
|
||||
|
|
@ -182,8 +182,8 @@ foreach ($entry in $issuesToCreate) {
|
|||
|
||||
# -- Step 6: Protect Default Branch --
|
||||
Log-Step "Protecting master branch..."
|
||||
$protectResult = Invoke-GL branch,+protect,--owner,$Owner,--repo,$Name,--name,master
|
||||
if ($protectResult -and (Get-JsonOk ($protectResult | ConvertFrom-Json))) {
|
||||
$protectResult = Invoke-GL "branch", "+protect", "--owner", $Owner, "--repo", $Name, "--name", "master"
|
||||
if ($protectResult -and (Get-JsonOk $protectResult)) {
|
||||
Log-Ok "Branch 'master' protected"
|
||||
} else {
|
||||
Log-Warn "Branch protection may have failed (may require admin permissions)"
|
||||
|
|
@ -206,8 +206,8 @@ $releaseBody += "- [ ] Complete documentation" + "`n"
|
|||
$releaseBody += "- [ ] First feature implementation" + "`n`n"
|
||||
$releaseBody += "---`n*Auto-initialized by gitlink-cli project-init workflow*"
|
||||
|
||||
$releaseResult = Invoke-GL release,+create,--owner,$Owner,--repo,$Name,--tag,"v0.1.0",--name,"Initial Release",--body,$releaseBody
|
||||
if ($releaseResult -and (Get-JsonOk ($releaseResult | ConvertFrom-Json))) {
|
||||
$releaseResult = Invoke-GL "release", "+create", "--owner", $Owner, "--repo", $Name, "--tag", "v0.1.0", "--name", "Initial Release", "--body", $releaseBody
|
||||
if ($releaseResult -and (Get-JsonOk $releaseResult)) {
|
||||
Log-Ok "Release v0.1.0 created"
|
||||
} else {
|
||||
Log-Warn "Release creation may have failed"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# ----------------------------------------------------------------
|
||||
# ----------------------------------------------------------------
|
||||
# Scenario 4: Multi-Repo Collaboration
|
||||
# Flow: Cross-repo issue tracking -> PR status dashboard -> Coordinated release
|
||||
#
|
||||
|
|
@ -36,7 +36,7 @@ Check-Auth
|
|||
Log-Title "Multi-Repo Collaboration Dashboard"
|
||||
|
||||
Log-Step "Fetching repositories for org: $Org..."
|
||||
$reposJson = Invoke-GLCheck repo,+list,--user,$Org,--limit,100
|
||||
$reposJson = Invoke-GLCheck "repo", "+list", "--user", $Org, "--limit", "100"
|
||||
if (-not $reposJson) { Log-Err "Failed to fetch repos"; exit 1 }
|
||||
|
||||
$allRepos = @()
|
||||
|
|
@ -69,13 +69,13 @@ foreach ($repo in $repoList) {
|
|||
Divider
|
||||
Log-Step "Processing $Org/$repo..."
|
||||
|
||||
$issuesJson = Invoke-GL issue,+list,--owner,$Org,--repo,$repo,--state,open,--limit,50
|
||||
$issuesJson = Invoke-GL "issue", "+list", "--owner", $Org, "--repo", $repo, "--state", "open", "--limit", "50"
|
||||
$openIssues = if ($issuesJson) { @($issuesJson.data.issues).Count } else { 0 }
|
||||
|
||||
$closedJson = Invoke-GL issue,+list,--owner,$Org,--repo,$repo,--state,closed,--limit,50
|
||||
$closedJson = Invoke-GL "issue", "+list", "--owner", $Org, "--repo", $repo, "--state", "closed", "--limit", "50"
|
||||
$closedIssues = if ($closedJson) { @($closedJson.data.issues).Count } else { 0 }
|
||||
|
||||
$prsJson = Invoke-GL pr,+list,--owner,$Org,--repo,$repo,--state,open,--limit,50
|
||||
$prsJson = Invoke-GL "pr", "+list", "--owner", $Org, "--repo", $repo, "--state", "open", "--limit", "50"
|
||||
$openPRs = 0
|
||||
if ($prsJson) {
|
||||
$pd = $prsJson.data
|
||||
|
|
@ -84,7 +84,7 @@ foreach ($repo in $repoList) {
|
|||
elseif ($pd -is [array]) { $openPRs = $pd.Count }
|
||||
}
|
||||
|
||||
$mergedJson = Invoke-GL pr,+list,--owner,$Org,--repo,$repo,--state,merged,--limit,50
|
||||
$mergedJson = Invoke-GL "pr", "+list", "--owner", $Org, "--repo", $repo, "--state", "merged", "--limit", "50"
|
||||
$mergedPRs = 0
|
||||
if ($mergedJson) {
|
||||
$md = $mergedJson.data
|
||||
|
|
@ -93,7 +93,7 @@ foreach ($repo in $repoList) {
|
|||
elseif ($md -is [array]) { $mergedPRs = $md.Count }
|
||||
}
|
||||
|
||||
$releaseJson = Invoke-GL release,+list,--owner,$Org,--repo,$repo,--limit,1
|
||||
$releaseJson = Invoke-GL "release", "+list", "--owner", $Org, "--repo", $repo, "--limit", "1"
|
||||
$latestRelease = "none"
|
||||
if ($releaseJson -and $releaseJson.data.releases) {
|
||||
$releases = @($releaseJson.data.releases)
|
||||
|
|
@ -186,8 +186,8 @@ if ($Release) {
|
|||
foreach ($repo in $repoList) {
|
||||
Log-Step "Creating release for $Org/$repo..."
|
||||
$relBody = "Coordinated release $Release for $Org/$repo"
|
||||
$relResult = Invoke-GL release,+create,--owner,$Org,--repo,$repo,--tag,$Release,--name,"Release $Release",--body,$relBody
|
||||
if ($relResult -and (Get-JsonOk ($relResult | ConvertFrom-Json))) {
|
||||
$relResult = Invoke-GL "release", "+create", "--owner", $Org, "--repo", $repo, "--tag", $Release, "--name", "Release $Release", "--body", $relBody
|
||||
if ($relResult -and (Get-JsonOk $relResult)) {
|
||||
Log-Ok "Release $Release created for $repo"
|
||||
} else {
|
||||
Log-Warn "Release creation failed for $repo (tag may already exist)"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# ----------------------------------------------------------------
|
||||
# ----------------------------------------------------------------
|
||||
# Scenario 5: Contributor Growth System
|
||||
# Flow: Collect data -> Calculate scores -> Generate HTML -> Publish Wiki -> Award badges
|
||||
#
|
||||
|
|
@ -54,12 +54,12 @@ Log-Title "Contributor Growth System: $Owner/$Repo"
|
|||
# -- Step 1: Collect Data --
|
||||
Log-Step "Collecting data..."
|
||||
|
||||
$issuesOpen = Invoke-GLCheck issue,+list,--owner,$Owner,--repo,$Repo,--state,open,--limit,100
|
||||
$issuesClosed = Invoke-GL issue,+list,--owner,$Owner,--repo,$Repo,--state,closed,--limit,100
|
||||
$issuesOpen = Invoke-GLCheck "issue", "+list", "--owner", $Owner, "--repo", $Repo, "--state", "open", "--limit", "100"
|
||||
$issuesClosed = Invoke-GL "issue", "+list", "--owner", $Owner, "--repo", $Repo, "--state", "closed", "--limit", "100"
|
||||
$openCount = if ($issuesOpen) { @($issuesOpen.data.issues).Count } else { 0 }
|
||||
$closedCount = if ($issuesClosed) { @($issuesClosed.data.issues).Count } else { 0 }
|
||||
|
||||
$prsMerged = Invoke-GL pr,+list,--owner,$Owner,--repo,$Repo,--state,merged,--limit,100
|
||||
$prsMerged = Invoke-GL "pr", "+list", "--owner", $Owner, "--repo", $Repo, "--state", "merged", "--limit", "100"
|
||||
$prMergedData = @()
|
||||
if ($prsMerged) {
|
||||
$pd = $prsMerged.data
|
||||
|
|
@ -69,7 +69,7 @@ if ($prsMerged) {
|
|||
}
|
||||
$prMergedCount = $prMergedData.Count
|
||||
|
||||
$membersJson = Invoke-GL repo,+members,--owner,$Owner,--repo,$Repo,--limit,100
|
||||
$membersJson = Invoke-GL "repo", "+members", "--owner", $Owner, "--repo", $Repo, "--limit", "100"
|
||||
$memberData = @()
|
||||
if ($membersJson) {
|
||||
$md = $membersJson.data
|
||||
|
|
@ -119,7 +119,7 @@ for ($i = 0; $i -lt $prMergedCount; $i++) {
|
|||
$contribData[$author].Merged++
|
||||
}
|
||||
if ($i -lt $prSample -and $prId -and $author) {
|
||||
$filesJson = Invoke-GL pr,+files,--owner,$Owner,--repo,$Repo,--id,$prId
|
||||
$filesJson = Invoke-GL "pr", "+files", "--owner", $Owner, "--repo", $Repo, "--id", $prId
|
||||
if ($filesJson -and $filesJson.data.files) {
|
||||
foreach ($f in $filesJson.data.files) {
|
||||
$add = if ($f.additions) { $f.additions } elseif ($f.addition) { $f.addition } else { 0 }
|
||||
|
|
@ -148,7 +148,7 @@ if ($issuesOpen) {
|
|||
for ($i = 0; $i -lt $commentSample; $i++) {
|
||||
$id = $openIssuesArr[$i].id
|
||||
if (-not $id) { continue }
|
||||
$detail = Invoke-GL issue,+view,--owner,$Owner,--repo,$Repo,--number,$id
|
||||
$detail = Invoke-GL "issue", "+view", "--owner", $Owner, "--repo", $Repo, "--number", $id
|
||||
if ($detail) {
|
||||
$commentCount = if ($detail.data.comment_journals_count) { $detail.data.comment_journals_count } else { 0 }
|
||||
if ($commentCount -gt 0) {
|
||||
|
|
@ -371,8 +371,8 @@ $wikiContent += $wikiRankRows + "`n"
|
|||
$wikiContent += "---" + "`n"
|
||||
$wikiContent += "*Auto-generated by gitlink-cli*"
|
||||
|
||||
$wikiResult = Invoke-GL wiki,+create,--owner,$Owner,--repo,$Repo,--title,$wikiTitle,--body,$wikiContent
|
||||
if ($wikiResult -and (Get-JsonOk ($wikiResult | ConvertFrom-Json))) {
|
||||
$wikiResult = Invoke-GL "wiki", "+create", "--owner", $Owner, "--repo", $Repo, "--title", $wikiTitle, "--content", $wikiContent
|
||||
if ($wikiResult -and (Get-JsonOk $wikiResult)) {
|
||||
Log-Ok "Published to Wiki: $wikiTitle"
|
||||
} else {
|
||||
Log-Warn "Wiki publish failed"
|
||||
|
|
@ -406,13 +406,13 @@ if ($Award) {
|
|||
}
|
||||
$issueBody += "`n`n---`n*Auto-awarded by gitlink-cli contributor-growth workflow*"
|
||||
|
||||
$issueResult = Invoke-GL issue,+create,--owner,$Owner,--repo,$Repo,--title,$issueTitle,--body,$issueBody
|
||||
$issueResult = Invoke-GL "issue", "+create", "--owner", $Owner, "--repo", $Repo, "--title", $issueTitle, "--body", $issueBody
|
||||
if ($issueResult) {
|
||||
try {
|
||||
$issueJson = $issueResult | ConvertFrom-Json
|
||||
$issueJson = $issueResult
|
||||
$issueNum = if ($issueJson.data.id) { $issueJson.data.id } elseif ($issueJson.data.number) { $issueJson.data.number } else { $null }
|
||||
if ($issueNum) {
|
||||
Invoke-GL issue,+label-add,--owner,$Owner,--repo,$Repo,--number,$issueNum,--labels,"badge" | Out-Null
|
||||
Invoke-GL "issue", "+label-add", "--owner", $Owner, "--repo", $Repo, "--number", $issueNum, "--labels", "badge" | Out-Null
|
||||
Log-Ok "Badge issue created: #$issueNum - $issueTitle ($($users.Count) recipients)"
|
||||
}
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
---
|
||||
name: gitlink-workflows
|
||||
version: 1.0.0
|
||||
description: "GitLink 自动化工作流总入口:提供社区运营、代码审查、项目初始化、多仓库协同、贡献者成长、Release Notes 等自动化功能的选择菜单。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["gitlink-cli"]
|
||||
triggers:
|
||||
- "工作流"
|
||||
- "workflow"
|
||||
- "自动化"
|
||||
- "帮我跑"
|
||||
- "执行"
|
||||
---
|
||||
|
||||
# gitlink-workflows(自动化工作流总入口)
|
||||
|
||||
**CRITICAL — GitLink 操作只能用 `gitlink-cli`。禁止用 `gh`(GitHub CLI)操作 GitLink 资源。**
|
||||
|
||||
> **前置条件:** 先阅读 [`../skills/gitlink-shared/SKILL.md`](../skills/gitlink-shared/SKILL.md) 了解认证和全局参数。
|
||||
|
||||
## 功能菜单
|
||||
|
||||
当用户说"工作流"、"自动化"、"帮我跑"等触发词时,展示以下菜单让用户选择:
|
||||
|
||||
```
|
||||
====== GitLink 自动化工作流 ======
|
||||
|
||||
请选择要执行的工作流:
|
||||
|
||||
1. 社区运营自动化
|
||||
→ Issue 自动分类、负责人分配、周报生成、Release Notes
|
||||
|
||||
2. 代码质量审查
|
||||
→ PR Review、AI 四维度评分、自动合并
|
||||
|
||||
3. 项目一键初始化
|
||||
→ 创建仓库、README、CI 配置、初始 Issues、分支保护
|
||||
|
||||
4. 多仓库协同
|
||||
→ 跨仓库 Issue/PR 追踪、状态 Dashboard、协同发版
|
||||
|
||||
5. 贡献者成长体系
|
||||
→ 数据收集、AHP 评分、排行榜、Wiki 发布、Badge 颁发
|
||||
|
||||
6. Release Notes 生成
|
||||
→ 收集 commits/PR/Issue、分类整理、生成 Notes
|
||||
|
||||
请输入编号(1-6)或功能名称:
|
||||
```
|
||||
|
||||
## 用户输入 → 工作流映射
|
||||
|
||||
| 用户输入 | 执行的工作流 |
|
||||
|----------|--------------|
|
||||
| `1` 或 "社区运营" | 读取 `workflow-community-ops.md` |
|
||||
| `2` 或 "代码审查" | 读取 `workflow-pr-review.md` |
|
||||
| `3` 或 "项目初始化" | 读取 `workflow-repo-setup.md` |
|
||||
| `4` 或 "多仓库" | 读取 `workflow-multi-repo.md` |
|
||||
| `5` 或 "贡献者" | 读取 `workflow-contributor-growth.md` |
|
||||
| `6` 或 "Release Notes" | 读取 `workflow-release-notes.md` |
|
||||
|
||||
## 执行流程
|
||||
|
||||
1. **展示菜单** — 列出所有可用工作流
|
||||
2. **获取用户选择** — 用户输入编号或功能名称
|
||||
3. **读取 Reference** — 根据选择读取对应的 reference 文件
|
||||
4. **确认参数** — 询问必要的参数(owner/repo 等)
|
||||
5. **执行工作流** — 按照 reference 文件的步骤执行
|
||||
6. **展示结果** — 输出执行结果和摘要
|
||||
|
||||
## 工作流 Reference 文件
|
||||
|
||||
所有工作流的详细步骤在以下 reference 文件中:
|
||||
|
||||
| 工作流 | Reference 文件 |
|
||||
|--------|----------------|
|
||||
| 社区运营 | [`workflow-community-ops.md`](../skills/gitlink-workflow/references/workflow-community-ops.md) |
|
||||
| 代码审查 | [`workflow-pr-review.md`](../skills/gitlink-workflow/references/workflow-pr-review.md) |
|
||||
| 项目初始化 | [`workflow-repo-setup.md`](../skills/gitlink-workflow/references/workflow-repo-setup.md) |
|
||||
| 多仓库协同 | [`workflow-multi-repo.md`](../skills/gitlink-workflow/references/workflow-multi-repo.md) |
|
||||
| 贡献者成长 | [`workflow-contributor-growth.md`](../skills/gitlink-workflow/references/workflow-contributor-growth.md) |
|
||||
| Release Notes | [`workflow-release-notes.md`](../skills/gitlink-workflow/references/workflow-release-notes.md) |
|
||||
|
||||
## 快捷触发
|
||||
|
||||
用户也可以直接说特定意图,跳过菜单直接执行:
|
||||
|
||||
| 用户说的话 | 直接执行 |
|
||||
|------------|----------|
|
||||
| "帮我跑一下社区运营" | `workflow-community-ops` |
|
||||
| "审查一下这个 PR" | `workflow-pr-review` |
|
||||
| "创建一个新项目" | `workflow-repo-setup` |
|
||||
| "看看组织下所有仓库" | `workflow-multi-repo` |
|
||||
| "生成贡献者排行榜" | `workflow-contributor-growth` |
|
||||
| "生成 Release Notes" | `workflow-release-notes` |
|
||||
|
||||
## 参数说明
|
||||
|
||||
| 参数 | 说明 | 获取方式 |
|
||||
|------|------|----------|
|
||||
| `--owner` | 仓库所有者 | 自动从 git remote 解析,或询问用户 |
|
||||
| `--repo` | 仓库名称 | 自动从 git remote 解析,或询问用户 |
|
||||
| `--org` | 组织名称 | 询问用户(多仓库协同时需要) |
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 所有写入操作前必须确认用户意图
|
||||
- 自动从 git remote 解析 owner/repo,解析失败时询问用户
|
||||
- 每个工作流的具体步骤见对应的 reference 文件
|
||||
- 支持 `--dry-run` 预览模式(部分工作流)
|
||||
|
||||
## References
|
||||
|
||||
- [gitlink-shared](../skills/gitlink-shared/SKILL.md) — 认证和全局参数
|
||||
- [gitlink-workflow](../skills/gitlink-workflow/SKILL.md) — AI 工作流详情
|
||||
- [gitlink-changelog](../skills/gitlink-changelog/SKILL.md) — Release Notes 生成
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Contributor Leaderboard - zzx-coder/gitlink-cli</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#f0f2f5;color:#333;padding:24px}
|
||||
h1{text-align:center;margin-bottom:24px;font-size:1.6rem}
|
||||
.cards{display:flex;gap:16px;justify-content:center;margin-bottom:24px;flex-wrap:wrap}
|
||||
.card{background:#fff;border-radius:12px;padding:20px 28px;text-align:center;box-shadow:0 2px 8px rgba(0,0,0,.06);min-width:160px}
|
||||
.card .num{font-size:2rem;font-weight:700;color:#1890ff}
|
||||
.card .label{font-size:.85rem;color:#888;margin-top:4px}
|
||||
.chart-row{display:flex;gap:16px;margin-bottom:24px;flex-wrap:wrap}
|
||||
.chart-box{background:#fff;border-radius:12px;padding:16px;box-shadow:0 2px 8px rgba(0,0,0,.06);flex:1;min-width:320px;height:320px}
|
||||
table{width:100%;border-collapse:collapse;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,.06)}
|
||||
th{background:#fafafa;padding:12px 16px;text-align:left;font-size:.85rem;color:#666;border-bottom:1px solid #f0f0f0}
|
||||
td{padding:12px 16px;border-bottom:1px solid #f0f0f0}
|
||||
tr:last-child td{border-bottom:none}
|
||||
.badge{display:inline-block;padding:2px 10px;border-radius:10px;font-size:.8rem;font-weight:600}
|
||||
.badge-champion{background:#fff7e6;color:#d48806}
|
||||
.badge-core{background:#e6f7ff;color:#1890ff}
|
||||
.badge-active{background:#f6ffed;color:#52c41a}
|
||||
.badge-contributor{background:#f0f0f0;color:#666}
|
||||
.rank-1{font-weight:700;color:#d48806}
|
||||
.rank-2{font-weight:600;color:#8c8c8c}
|
||||
.rank-3{color:#b87333}
|
||||
.ahp{background:#fff;border-radius:12px;padding:16px;margin-top:24px;box-shadow:0 2px 8px rgba(0,0,0,.06);font-size:.85rem;color:#666}
|
||||
.ahp h3{font-size:1rem;color:#333;margin-bottom:8px}
|
||||
.ahp table{box-shadow:none}
|
||||
.ahp th{background:#f9f9f9}
|
||||
.footer{text-align:center;margin-top:24px;font-size:.8rem;color:#aaa}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Contributor Leaderboard</h1>
|
||||
<p style="text-align:center;color:#888;margin-bottom:24px">zzx-coder/gitlink-cli · 2026-07-02</p>
|
||||
|
||||
<div class="cards">
|
||||
<div class="card"><div class="num">3</div><div class="label">Contributors</div></div>
|
||||
<div class="card"><div class="num">76</div><div class="label">Total Issues</div></div>
|
||||
<div class="card"><div class="num">18</div><div class="label">Merged PRs</div></div>
|
||||
<div class="card"><div class="num">1600</div><div class="label">Lines Changed</div></div>
|
||||
</div>
|
||||
|
||||
<div class="chart-row">
|
||||
<div class="chart-box" id="pieChart"></div>
|
||||
<div class="chart-box" id="barChart"></div>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Rank</th><th>Contributor</th><th>Issues</th><th>Merged PRs</th><th>+/- Lines</th><th>Comments</th><th>Score</th><th>Badge</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td class="rank-1">1</td><td>@zzx-coder</td><td>44</td><td>8</td><td>+800</td><td>22</td><td>100.0</td><td><span class="badge badge-champion">Champion</span></td></tr>
|
||||
<tr><td class="rank-2">2</td><td>@mengcheng</td><td>18</td><td>8</td><td>+600</td><td>18</td><td>80.9</td><td><span class="badge badge-champion">Champion</span></td></tr>
|
||||
<tr><td class="rank-3">3</td><td>@nudt_zk</td><td>14</td><td>2</td><td>+200</td><td>4</td><td>36.2</td><td><span class="badge badge-contributor">Contributor</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="ahp">
|
||||
<h3>AHP Scoring Model</h3>
|
||||
<table>
|
||||
<thead><tr><th>Dimension</th><th>Weight</th><th>Data Source</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Issues Created</td><td>15%</td><td>issue +list</td></tr>
|
||||
<tr><td>PRs Merged</td><td>25%</td><td>pr +list state=merged</td></tr>
|
||||
<tr><td>Code Changes</td><td>30%</td><td>pr +files (sampled)</td></tr>
|
||||
<tr><td>Issue Comments</td><td>15%</td><td>PR journals</td></tr>
|
||||
<tr><td>Team Member</td><td>15%</td><td>repo +members</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="footer">Auto-generated by gitlink-cli contributor-growth workflow · 2026-07-02</div>
|
||||
|
||||
<script>
|
||||
var pie = echarts.init(document.getElementById('pieChart'));
|
||||
pie.setOption({
|
||||
title:{text:'Score Distribution',left:'center',textStyle:{fontSize:14}},
|
||||
tooltip:{trigger:'item',formatter:'{b}: {c} ({d}%)'},
|
||||
series:[{
|
||||
type:'pie',radius:['40%','70%'],
|
||||
data:[
|
||||
{value:100.0,name:'@zzx-coder',itemStyle:{color:'#d48806'}},
|
||||
{value:80.9,name:'@mengcheng',itemStyle:{color:'#1890ff'}},
|
||||
{value:36.2,name:'@nudt_zk',itemStyle:{color:'#8c8c8c'}}
|
||||
],
|
||||
label:{formatter:'{b}\n{d}%'}
|
||||
}]
|
||||
});
|
||||
var bar = echarts.init(document.getElementById('barChart'));
|
||||
bar.setOption({
|
||||
title:{text:'Dimension Comparison',left:'center',textStyle:{fontSize:14}},
|
||||
tooltip:{trigger:'axis'},
|
||||
legend:{bottom:0,data:['Issues(15%)','PRs(25%)','Code(30%)','Comments(15%)','Member(15%)']},
|
||||
xAxis:{type:'category',data:['@zzx-coder','@mengcheng','@nudt_zk']},
|
||||
yAxis:{type:'value',max:30},
|
||||
series:[
|
||||
{name:'Issues(15%)',type:'bar',data:[15,6.1,4.8]},
|
||||
{name:'PRs(25%)',type:'bar',data:[25,25,6.3]},
|
||||
{name:'Code(30%)',type:'bar',data:[30,22.5,7.5]},
|
||||
{name:'Comments(15%)',type:'bar',data:[15,12.3,2.7]},
|
||||
{name:'Member(15%)',type:'bar',data:[15,15,15]}
|
||||
]
|
||||
});
|
||||
window.addEventListener('resize',function(){pie.resize();bar.resize()});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,4 +1,10 @@
|
|||
# Common utilities for gitlink-cli workflow scripts (PowerShell 5.1+)
|
||||
# Common utilities for gitlink-cli workflow scripts (PowerShell 5.1+)
|
||||
|
||||
# Force UTF-8 when capturing stdout from native commands.
|
||||
# On Windows Chinese locales, PS 5.1 defaults to GBK and corrupts multi-byte
|
||||
# JSON (e.g. 紧急/新增), which makes ConvertFrom-Json fail silently.
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
$Script:GL = "gitlink-cli"
|
||||
|
||||
|
|
@ -30,31 +36,41 @@ function Check-Auth {
|
|||
}
|
||||
|
||||
# -- CLI Wrapper --
|
||||
# Returns parsed JSON object on success, $null on failure
|
||||
# Usage: Invoke-GL @("issue", "+list", "--owner", $Owner, "--repo", $Repo)
|
||||
function Invoke-GL {
|
||||
param([string[]]$Args)
|
||||
$output = & $Script:GL @Args --format json 2>&1
|
||||
return ($output -join "`n")
|
||||
}
|
||||
|
||||
function Invoke-GLCheck {
|
||||
param([string[]]$Args)
|
||||
$output = Invoke-GL $Args
|
||||
param([string[]]$CmdArgs)
|
||||
$allArgs = @($CmdArgs) + @("--format", "json")
|
||||
# Capture stdout only; stderr goes to console
|
||||
$output = & $Script:GL @allArgs
|
||||
$raw = ($output -join "`n")
|
||||
if (-not $raw -or $raw.Trim() -eq "") { return $null }
|
||||
try {
|
||||
$json = $output | ConvertFrom-Json
|
||||
if (-not $json.ok) {
|
||||
$errMsg = if ($json.error.message) { $json.error.message } else { "unknown error" }
|
||||
Log-Err "Command failed: $Script:GL $($Args -join ' ')"
|
||||
Log-Err $errMsg
|
||||
return $null
|
||||
}
|
||||
return $json
|
||||
return ($raw | ConvertFrom-Json)
|
||||
} catch {
|
||||
Log-Err "Command failed (non-JSON): $Script:GL $($Args -join ' ')"
|
||||
Log-Err $output
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
# Like Invoke-GL but logs error on failure
|
||||
function Invoke-GLCheck {
|
||||
param([string[]]$CmdArgs)
|
||||
$json = Invoke-GL $CmdArgs
|
||||
if (-not $json) {
|
||||
$argStr = $CmdArgs -join " "
|
||||
Log-Err "Command failed (no JSON): $Script:GL $argStr"
|
||||
return $null
|
||||
}
|
||||
if (-not $json.ok) {
|
||||
$argStr = $CmdArgs -join " "
|
||||
$errMsg = if ($json.error -and $json.error.message) { $json.error.message } else { "unknown error" }
|
||||
Log-Err "Command failed: $Script:GL $argStr"
|
||||
Log-Err $errMsg
|
||||
return $null
|
||||
}
|
||||
return $json
|
||||
}
|
||||
|
||||
# -- JSON Helpers --
|
||||
function Get-JsonOk {
|
||||
param($Json)
|
||||
|
|
|
|||
Loading…
Reference in New Issue