forked from Gitlink/gitlink-cli
261 lines
10 KiB
PowerShell
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
|