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

261 lines
10 KiB
PowerShell

# ----------------------------------------------------------------
# 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
param(
[string]$Owner = "",
[string]$Repo = "",
[int]$WeeksAgo = 0,
[switch]$DryRun,
[switch]$Help
)
$ErrorActionPreference = "Stop"
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
}
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')
$DocsKw = @('doc','readme','guide','tutorial','example')
# ----------------------------------------------------------------
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
if (-not $issuesJson) { Log-Err "Failed to fetch issues"; exit 1 }
$issues = @($issuesJson.data.issues)
$issueCount = $issues.Count
Log-Ok "Found $issueCount open issues"
$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 { "" }
$desc = if ($issue.description) { $issue.description } else { "" }
$combined = "$title $desc".ToLower()
$classified = $false
foreach ($kw in $BugKw) {
if ($combined -match [regex]::Escape($kw)) { $BugIds += $id; Log-Info " #$id -> BUG: $title"; $classified = $true; break }
}
if (-not $classified) {
foreach ($kw in $FeatureKw) {
if ($combined -match [regex]::Escape($kw)) { $FeatureIds += $id; Log-Info " #$id -> FEATURE: $title"; $classified = $true; break }
}
}
if (-not $classified) {
foreach ($kw in $QuestionKw) {
if ($combined -match [regex]::Escape($kw)) { $QuestionIds += $id; Log-Info " #$id -> QUESTION: $title"; $classified = $true; break }
}
}
if (-not $classified) {
foreach ($kw in $DocsKw) {
if ($combined -match [regex]::Escape($kw)) { $DocsIds += $id; Log-Info " #$id -> DOCS: $title"; $classified = $true; break }
}
}
if (-not $classified) { Log-Info " #$id -> UNCATEGORIZED: $title" }
}
Divider
Log-Info "Classification summary:"
Log-Info " Bugs: $($BugIds.Count)"
Log-Info " Features: $($FeatureIds.Count)"
Log-Info " Questions: $($QuestionIds.Count)"
Log-Info " Docs: $($DocsIds.Count)"
# Apply labels
$labelGroups = @(
@{ Ids = $BugIds; Label = "bug" },
@{ Ids = $FeatureIds; Label = "feature" },
@{ Ids = $QuestionIds; Label = "question" },
@{ Ids = $DocsIds; Label = "documentation" }
)
foreach ($g in $labelGroups) {
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
}
Log-Ok "Labeled $($g.Ids.Count) $($g.Label) issues"
}
}
}
# ----------------------------------------------------------------
Log-Title "Phase 2: Assign Responsible Persons"
# ----------------------------------------------------------------
Log-Step "Fetching repo members..."
$membersJson = Invoke-GL repo,+members,--owner,$Owner,--repo,$Repo,--limit,50
$members = @()
if ($membersJson) {
$md = $membersJson.data
if ($md.members) { $members = @($md.members) }
elseif ($md -is [array]) { $members = $md }
}
$memberLogins = @()
foreach ($m in $members) {
$login = if ($m.login) { $m.login } elseif ($m.username) { $m.username } else { $null }
if ($login) { $memberLogins += $login }
}
if ($memberLogins.Count -gt 0) {
$assignIds = @($BugIds + $FeatureIds)
if ($assignIds.Count -gt 0) {
Log-Step "Assigning issues to members (round-robin)..."
$idx = 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
Log-Info " Assigned #$id -> @$assignee"
$idx++
}
Log-Ok "Assignment complete"
}
} else {
Log-Warn "No repo members found, skipping assignment"
}
# ----------------------------------------------------------------
Log-Title "Phase 3: Generate Community Weekly Report"
# ----------------------------------------------------------------
$weekStart = (Get-Date).AddDays(-$WeeksAgo * 7).ToString("yyyy-MM-dd")
$weekEnd = Get-DateToday
Log-Step "Collecting weekly data (week of $weekStart)..."
$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 = @()
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
$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 += "| 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"
$reportBody += "---" + "`n"
$reportBody += "*Auto-generated by gitlink-cli community-ops workflow*"
Log-Ok "Weekly report generated"
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)"
}
# ----------------------------------------------------------------
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"
}
}
$releaseBody += "`n`n## Closed Issues ($closedCount)"
if ($closedCount -gt 0) {
$closedIssues = @($closedJson.data.issues)
$limit = [Math]::Min($closedCount, 10)
for ($i = 0; $i -lt $limit; $i++) {
$issueTitle = if ($closedIssues[$i].subject) { $closedIssues[$i].subject } elseif ($closedIssues[$i].title) { $closedIssues[$i].title } else { "" }
$issueNum = if ($closedIssues[$i].number) { $closedIssues[$i].number } elseif ($closedIssues[$i].id) { $closedIssues[$i].id } else { "" }
if ($issueTitle) { $releaseBody += "`n- #$issueNum $issueTitle" }
}
}
$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)"
}
# ----------------------------------------------------------------
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