gitlink-cli/workflows/01-community-ops.ps1

485 lines
19 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ----------------------------------------------------------------
# Scenario 1: Community Operations Automation (定时批量链路)
# Flow: 收集周期数据 → 10类关键词分类 → 生成 Release Notes + 社区周报
# 遵循 gitlink-changelog skill 的收集→分类→发布流程
# 条目格式: - 描述 (#编号) (@作者) [分类]
=======
# ----------------------------------------------------------------
#Requires -Version 5.1
param(
[string]$Owner = "",
[string]$Repo = "",
[int]$PeriodHours = 6,
[string]$ReleaseVersion = "",
[switch]$DryRun,
[switch]$Help
)
$ErrorActionPreference = "Stop"
Import-Module "$PSScriptRoot/lib/common.psm1" -Force
if ($Help) {
Write-Host "Usage: powershell 01-community-ops.ps1 [-Owner O] [-Repo R] [-PeriodHours N] [-ReleaseVersion TAG] [-DryRun]"
=======
exit 0
}
Check-Auth
$r = Resolve-OwnerRepo $Owner $Repo
$Owner = $r.Owner; $Repo = $r.Repo
# ================================================================
# Phase 1: 时间窗口和基线
# ================================================================
Log-Title "Phase 1: Time Window"
=======
$periodStart = (Get-Date).AddHours(-$PeriodHours)
$periodEnd = Get-Date
$periodStartStr = $periodStart.ToString("yyyy-MM-dd HH:mm")
$periodEndStr = $periodEnd.ToString("yyyy-MM-dd HH:mm")
Log-Step "Finding previous release..."
$prevTag = ""
$releasesJson = Invoke-GL release,+list,--owner,$Owner,--repo,$Repo,--limit,5
if ($releasesJson) {
try {
$relData = ($releasesJson | ConvertFrom-Json).data
$releases = if ($relData.releases) { @($relData.releases) } elseif ($relData -is [array]) { @($relData) } else { @() }
if ($releases.Count -gt 0) {
$prevTag = if ($releases[0].tag_name) { $releases[0].tag_name } else { "" }
=======
}
} catch {}
}
if (-not $prevTag) { $prevTag = "initial" }
$newVersion = if ($ReleaseVersion) { $ReleaseVersion } else { "weekly-$(Get-Date -Format 'yyyyMMdd')" }
Log-Ok "Period: $periodStartStr ~ $periodEndStr"
Log-Ok "Release: $prevTag -> $newVersion"
# ================================================================
# Phase 2: 收集数据
# ================================================================
Log-Title "Phase 2: Collect Data"
# -- Commits --
Log-Step "Collecting commits..."
$commitCount = 0
$commitList = "(无 commit 数据)"
if ($prevTag -ne "initial") {
$compareJson = Invoke-GL api,GET,"/v1/$Owner/$Repo/compare/$prevTag...master"
if ($compareJson) {
try {
$cData = ($compareJson | ConvertFrom-Json).data
if ($cData.commits) {
$commits = @($cData.commits)
$commitCount = $commits.Count
$lines = @()
$show = [Math]::Min($commitCount, 50)
for ($i = 0; $i -lt $show; $i++) {
$msg = if ($commits[$i].commit.message) { ($commits[$i].commit.message -split "`n")[0] } else { "N/A" }
$author = if ($commits[$i].commit.author.name) { $commits[$i].commit.author.name } else { "unknown" }
$lines += "- $msg ($author)"
}
$commitList = $lines -join "`n"
}
} catch {}
}
}
Log-Ok "Commits: $commitCount"
<<<<<<< HEAD
# -- Merged PRs --
Log-Step "Collecting merged PRs..."
$prItems = @()
$prContributors = @{}
$prsJson = Invoke-GL pr,+list,--owner,$Owner,--repo,$Repo,--state,merged,--limit,100
if ($prsJson) {
try {
$prData = ($prsJson | ConvertFrom-Json).data
$prs = if ($prData.issues) { @($prData.issues) } elseif ($prData.pulls) { @($prData.pulls) } elseif ($prData -is [array]) { @($prData) } else { @() }
foreach ($pr in $prs) {
$prTitle = if ($pr.subject) { $pr.subject } elseif ($pr.title) { $pr.title } else { "N/A" }
$prNum = if ($pr.pull_request_number) { $pr.pull_request_number } elseif ($pr.number) { $pr.number } else { "?" }
$prAuthor = if ($pr.author_login) { $pr.author_login } elseif ($pr.author.login) { $pr.author.login } else { "?" }
$prItems += "- $prTitle (#$prNum) (@$prAuthor)"
$prContributors[$prAuthor] = $true
}
} catch {}
}
$prCount = $prItems.Count
$prText = if ($prCount -gt 0) { ($prItems -join "`n") } else { "(本周期无 PR 合并)" }
Log-Ok "Merged PRs: $prCount"
# -- Issues (新增 + 关闭) --
Log-Step "Collecting issues..."
$bugLines = @()
$featLines = @()
$docLines = @()
$otherLines = @()
$issContributors = @{}
$issCount = 0
$today = (Get-Date -Format "yyyy-MM-dd")
$yesterday = ((Get-Date).AddDays(-1)).ToString("yyyy-MM-dd")
foreach ($state in @("open","closed")) {
$issJson = Invoke-GL issue,+list,--owner,$Owner,--repo,$Repo,--state,$state,--limit,100
if (-not $issJson) { continue }
try {
$issData = ($issJson | ConvertFrom-Json).data
$issues = if ($issData.issues) { @($issData.issues) } elseif ($issData -is [array]) { @($issData) } else { @() }
foreach ($iss in $issues) {
$num = if ($iss.project_issues_index) { $iss.project_issues_index } elseif ($iss.number) { $iss.number } else { "?" }
$title = if ($iss.subject) { $iss.subject } elseif ($iss.title) { $iss.title } else { "N/A" }
$desc = if ($iss.description) { $iss.description } else { "" }
$author = if ($iss.author.login) { $iss.author.login } elseif ($iss.author.username) { $iss.author.username } else { "?" }
$created = if ($iss.created_at) { $iss.created_at } else { "" }
$closed = if ($iss.closed_at) { $iss.closed_at } else { "" }
$stateName = if ($iss.status.name) { $iss.status.name } elseif ($iss.state) { $iss.state } else { "?" }
# 时间筛选created 或 closed >= periodStart
$inPeriod = $false
if ($created) { try { if (([DateTime]$created) -ge $periodStart) { $inPeriod = $true } } catch {} }
if (-not $inPeriod -and $closed) { try { if (([DateTime]$closed) -ge $periodStart) { $inPeriod = $true } } catch {} }
if (-not $inPeriod) { continue }
<<<<<<< HEAD
$line = "- $title (#$num) (@$author)"
if ($stateName -match "关闭|closed") { $line += " [已关闭]" }
# 标题+描述 关键词分类 (10 个标准类别)
$combined = "$title $desc".ToLower()
if ($combined -match '(?i)bug|error|crash|fault|fix|缺陷|错误|异常|崩溃|修复|故障') {
$line += " [缺陷]"; $bugLines += $line
} elseif ($combined -match '(?i)feature|enhancement|add|新增|建议|功能|特性|新功能|支持|request') {
$line += " [功能]"; $featLines += $line
} elseif ($combined -match '(?i)doc|readme|guide|wiki|tutorial|文档|说明|教程|手册') {
$line += " [文档]"; $docLines += $line
} elseif ($combined -match '(?i)test|测试|用例|覆盖|验证') {
$line += " [测试]"; $docLines += $line
} elseif ($combined -match '(?i)duplicate|重复|重复的') {
$line += " [重复]"; $docLines += $line
} elseif ($combined -match '(?i)question|疑问|不确定|讨论|澄清|是否|可否') {
$line += " [疑问]"; $docLines += $line
} elseif ($combined -match '(?i)help|协助|帮助|协作|请求帮助|互助') {
$line += " [协助]"; $docLines += $line
} elseif ($combined -match '(?i)postpone|wontfix|暂缓|搁置|低优|不重要|不紧急|暂不|delay') {
$line += " [搁置]"; $docLines += $line
} elseif ($combined -match '(?i)task|todo|任务|待办|计划|安排') {
$line += " [任务]"; $docLines += $line
} elseif ($combined -match '(?i)support|兼容|环境|依赖|平台|适配') {
$line += " [支持]"; $docLines += $line
} else {
$line += " [其他]"; $otherLines += $line
}
if ($author -ne "?") { $issContributors[$author] = $true }
$issCount++
}
} catch {}
}
$bugSection = if ($bugLines.Count -gt 0) { ($bugLines -join "`n") } else { "_无_" }
$featSection = if ($featLines.Count -gt 0) { ($featLines -join "`n") } else { "_无_" }
$docSection = if ($docLines.Count -gt 0) { ($docLines -join "`n") } else { "_无_" }
$otherSection = if ($otherLines.Count -gt 0) { ($otherLines -join "`n") } else { "_无_" }
Log-Ok "Issues in period: $issCount"
=======
$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"
$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
}
>>>>>>> master
# -- 贡献者汇总 --
$allContribs = @($prContributors.Keys; $issContributors.Keys) | Select-Object -Unique | Sort-Object
$contribText = if ($allContribs.Count -gt 0) { ($allContribs | ForEach-Object { "- @$_" }) -join "`n" } else { "(无活跃贡献者)" }
<<<<<<< HEAD
# ================================================================
# Phase 3: 生成 Release Notes (changelog skill 模板)
# ================================================================
Log-Title "Phase 3: Generate Release Notes"
=======
$reportTitle = "Community Weekly Report: $weekStart ~ $weekEnd"
$reportBody = "# $reportTitle" + "`n`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"
# 开放 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*"
>>>>>>> master
$releaseBody = @"
# 🎉 Release $newVersion
## 📊
- ****: $periodStartStr ~ $periodEndStr
- ** PR**: $prCount
- **Issue **: $issCount /
- **Commits**: $commitCount
## 📝 Issue
### 🐛 / Bug
$bugSection
### / Feature
$featSection
### 📖 /
$docSection
### 💡
$otherSection
## 🔀 PR
$prText
## 🙏
$contribText
---
****: https://www.gitlink.org.cn/$Owner/$Repo/compare/$prevTag...$newVersion
*Auto-generated by gitlink-cli community-ops workflow (gitlink-changelog skill)*
"@
Write-Host $releaseBody
Write-Host ""
<<<<<<< HEAD
# ================================================================
# Phase 4: 发布 Release
# ================================================================
Log-Title "Phase 4: Publish Release"
if ($DryRun) {
Log-Warn "[DRY RUN] Would create release: $newVersion"
} else {
Log-Step "Creating release $newVersion..."
$relResult = Invoke-GL release,+create,--owner,$Owner,--repo,$Repo,--tag,$newVersion,--name,"Release $newVersion",--body,$releaseBody
if ($relResult -and (Get-JsonOk ($relResult | ConvertFrom-Json))) {
Log-Ok "Release $newVersion published!"
Log-Info "View: https://www.gitlink.org.cn/$Owner/$Repo/releases"
} else {
Log-Warn "Release may have failed (tag might exist)"
=======
Log-Step "Publishing weekly report to Wiki..."
$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"
# ----------------------------------------------------------------
Log-Step "Collecting recent changes for release notes..."
$tagName = "weekly-$(Get-Date -Format 'yyyyMMdd')"
$releaseName = "Weekly Release $(Get-Date -Format 'yyyy-MM-dd')"
$releaseBody = "# Release Notes - $(Get-Date -Format 'yyyy-MM-dd')" + "`n`n"
$releaseBody += "## Merged PRs ($mergedCount)"
if ($mergedCount -gt 0) {
$limit = [Math]::Min($mergedCount, 10)
for ($i = 0; $i -lt $limit; $i++) {
$prTitle = if ($mergedData[$i].subject) { $mergedData[$i].subject } elseif ($mergedData[$i].title) { $mergedData[$i].title } else { "" }
$prNum = if ($mergedData[$i].id) { $mergedData[$i].id } elseif ($mergedData[$i].number) { $mergedData[$i].number } else { "" }
$releaseBody += "`n- #$prNum $prTitle"
>>>>>>> master
}
}
# ================================================================
# Phase 5: 发布社区周报 (覆盖更新同一天)
# ================================================================
Log-Title "Phase 5: Publish Weekly Report"
$wikiTitle = "社区周报"
$wikiBody = @"
# - $Owner/$Repo
**$periodStartStr ~ $periodEndStr**
---
## 📊
- PR: **$prCount**
- Issue : **$issCount**
- : $($allContribs.Count)
## 🔀 PR
$prText
## 📝 Issue
$bugSection
$featSection
$docSection
$otherSection
---
*Auto-generated by gitlink-cli | $periodStartStr ~ $periodEndStr | Next report in ~$PeriodHours h*
"@
if ($DryRun) {
Log-Warn "[DRY RUN] Would publish Wiki"
} else {
Log-Step "Publishing to Wiki..."
# 先尝试覆盖更新,不存在则新建
$wikiResult = Invoke-GL wiki,+update,--owner,$Owner,--repo,$Repo,--title,$wikiTitle,--cover,$wikiBody
if ($wikiResult -and (Get-JsonOk ($wikiResult | ConvertFrom-Json))) {
Log-Ok "Weekly report updated on Wiki"
} else {
Log-Info "Page not found, creating new..."
$wikiResult = Invoke-GL wiki,+create,--owner,$Owner,--repo,$Repo,--title,$wikiTitle,--content,$wikiBody
if ($wikiResult -and (Get-JsonOk ($wikiResult | ConvertFrom-Json))) {
Log-Ok "Weekly report created on Wiki"
} else {
Log-Warn "Wiki publish failed"
}
}
}
<<<<<<< HEAD
Log-Title "Complete"
Write-Host " Release: $newVersion" -ForegroundColor Green
Write-Host " PRs merged: $prCount" -ForegroundColor Green
Write-Host " Issues: $issCount" -ForegroundColor Green
Write-Host " Contributors: $($allContribs.Count)" -ForegroundColor Green
Write-Host " Wiki: $wikiTitle" -ForegroundColor Green
=======
$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 $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"
# ----------------------------------------------------------------
Write-Host " Issues classified: $totalClassified" -ForegroundColor Green
Write-Host " Closed this week: $closedCount" -ForegroundColor Green
Write-Host " Merged PRs: $mergedCount" -ForegroundColor Green
Write-Host " Weekly report: Published to Wiki" -ForegroundColor Green
Write-Host " Release notes: $tagName" -ForegroundColor Green
>>>>>>> master