forked from Gitlink/gitlink-cli
fix(workflows): make PowerShell scripts compatible with PS 5.1
- Replace ?? operator with if/else for Windows PowerShell 5.1 compatibility - Remove Chinese characters to avoid encoding issues - Use string concatenation instead of complex expressions in here-strings - Update common.psm1 to remove PS 7+ syntax Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ecf16b379d
commit
012c197ee6
|
|
@ -1,6 +1,6 @@
|
|||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
# Scenario 1: Community Operations Automation
|
||||
# Flow: Issue auto-classify → Assign responsible → Weekly report → Release notes
|
||||
# Flow: Issue auto-classify -> Assign responsible -> Weekly report -> Release notes
|
||||
#
|
||||
# Commands chained:
|
||||
# 1. issue +list -- fetch open issues
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
# 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(
|
||||
|
|
@ -25,7 +25,7 @@ $ErrorActionPreference = "Stop"
|
|||
Import-Module "$PSScriptRoot/lib/common.psm1" -Force
|
||||
|
||||
if ($Help) {
|
||||
Write-Host "Usage: pwsh 01-community-ops.ps1 -Owner OWNER -Repo REPO [-WeeksAgo N] [-DryRun]"
|
||||
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"
|
||||
|
|
@ -39,16 +39,14 @@ $r = Resolve-OwnerRepo $Owner $Repo
|
|||
$Owner = $r.Owner; $Repo = $r.Repo
|
||||
|
||||
# Classification keywords
|
||||
$Keywords = @{
|
||||
bug = @('bug','error','crash','fault','fix','修复','错误','异常','崩溃')
|
||||
feature = @('feature','新增','建议','enhancement','add','support','功能')
|
||||
question = @('how','怎么','如何','question','help','?','?')
|
||||
documentation = @('doc','文档','readme','说明','guide')
|
||||
}
|
||||
$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
|
||||
|
|
@ -65,30 +63,30 @@ if ($issueCount -gt 0) {
|
|||
|
||||
foreach ($issue in $issues) {
|
||||
$id = $issue.id
|
||||
$title = $issue.subject ?? $issue.title ?? ""
|
||||
$desc = $issue.description ?? ""
|
||||
$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 $Keywords.bug) {
|
||||
if ($combined -match [regex]::Escape($kw)) { $BugIds += $id; Log-Info " #$id → BUG: $title"; $classified = $true; break }
|
||||
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 $Keywords.feature) {
|
||||
if ($combined -match [regex]::Escape($kw)) { $FeatureIds += $id; Log-Info " #$id → FEATURE: $title"; $classified = $true; break }
|
||||
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 $Keywords.question) {
|
||||
if ($combined -match [regex]::Escape($kw)) { $QuestionIds += $id; Log-Info " #$id → QUESTION: $title"; $classified = $true; break }
|
||||
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 $Keywords.documentation) {
|
||||
if ($combined -match [regex]::Escape($kw)) { $DocsIds += $id; Log-Info " #$id → DOCS: $title"; $classified = $true; break }
|
||||
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" }
|
||||
if (-not $classified) { Log-Info " #$id -> UNCATEGORIZED: $title" }
|
||||
}
|
||||
|
||||
Divider
|
||||
|
|
@ -100,10 +98,10 @@ if ($issueCount -gt 0) {
|
|||
|
||||
# Apply labels
|
||||
$labelGroups = @(
|
||||
@{ Ids = $BugIds; Label = "bug" },
|
||||
@{ Ids = $BugIds; Label = "bug" },
|
||||
@{ Ids = $FeatureIds; Label = "feature" },
|
||||
@{ Ids = $QuestionIds; Label = "question" },
|
||||
@{ Ids = $DocsIds; Label = "documentation" }
|
||||
@{ Ids = $DocsIds; Label = "documentation" }
|
||||
)
|
||||
foreach ($g in $labelGroups) {
|
||||
if ($g.Ids.Count -gt 0) {
|
||||
|
|
@ -116,9 +114,9 @@ if ($issueCount -gt 0) {
|
|||
}
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
Log-Title "Phase 2: Assign Responsible Persons"
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
Log-Step "Fetching repo members..."
|
||||
$membersJson = Invoke-GL repo,+members,--owner,$Owner,--repo,$Repo,--limit,50
|
||||
|
|
@ -131,7 +129,7 @@ if ($membersJson) {
|
|||
|
||||
$memberLogins = @()
|
||||
foreach ($m in $members) {
|
||||
$login = $m.login ?? $m.username
|
||||
$login = if ($m.login) { $m.login } elseif ($m.username) { $m.username } else { $null }
|
||||
if ($login) { $memberLogins += $login }
|
||||
}
|
||||
|
||||
|
|
@ -142,8 +140,9 @@ if ($memberLogins.Count -gt 0) {
|
|||
$idx = 0
|
||||
foreach ($id in $assignIds) {
|
||||
$assignee = $memberLogins[$idx % $memberLogins.Count]
|
||||
Invoke-GL api,PATCH,"/v1/$Owner/$Repo/issues/$id",--body,"{`"assigned_to_id`": `"$assignee`"}" | Out-Null
|
||||
Log-Info " Assigned #$id → @$assignee"
|
||||
$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"
|
||||
|
|
@ -152,9 +151,9 @@ if ($memberLogins.Count -gt 0) {
|
|||
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
|
||||
|
|
@ -178,29 +177,23 @@ $newIssuesCount = $issueCount
|
|||
$totalClassified = $BugIds.Count + $FeatureIds.Count + $QuestionIds.Count + $DocsIds.Count
|
||||
|
||||
$reportTitle = "Community Weekly Report: $weekStart ~ $weekEnd"
|
||||
$reportBody = @"
|
||||
# $reportTitle
|
||||
|
||||
## Summary
|
||||
- New Issues: **$newIssuesCount**
|
||||
- Closed Issues: **$closedCount**
|
||||
- Merged PRs: **$mergedCount**
|
||||
|
||||
## Issue Classification
|
||||
| Type | Count |
|
||||
|------|-------|
|
||||
| Bug | $($BugIds.Count) |
|
||||
| Feature | $($FeatureIds.Count) |
|
||||
| Question | $($QuestionIds.Count) |
|
||||
| Docs | $($DocsIds.Count) |
|
||||
|
||||
## Highlights
|
||||
- Auto-classified and labeled $totalClassified issues
|
||||
- Assigned responsible persons for bug and feature issues
|
||||
|
||||
---
|
||||
*Auto-generated by gitlink-cli community-ops workflow*
|
||||
"@
|
||||
$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 ""
|
||||
|
|
@ -214,22 +207,23 @@ if ($wikiResult -and (Get-JsonOk ($wikiResult | ConvertFrom-Json))) {
|
|||
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## Merged PRs ($mergedCount)"
|
||||
$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 = $mergedData[$i].subject ?? $mergedData[$i].title ?? ""
|
||||
$prNum = $mergedData[$i].id ?? $mergedData[$i].number ?? ""
|
||||
$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"
|
||||
}
|
||||
}
|
||||
|
|
@ -239,8 +233,8 @@ if ($closedCount -gt 0) {
|
|||
$closedIssues = @($closedJson.data.issues)
|
||||
$limit = [Math]::Min($closedCount, 10)
|
||||
for ($i = 0; $i -lt $limit; $i++) {
|
||||
$issueTitle = $closedIssues[$i].subject ?? $closedIssues[$i].title ?? ""
|
||||
$issueNum = $closedIssues[$i].number ?? $closedIssues[$i].id ?? ""
|
||||
$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" }
|
||||
}
|
||||
}
|
||||
|
|
@ -255,9 +249,9 @@ if ($releaseResult -and (Get-JsonOk ($releaseResult | ConvertFrom-Json))) {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
# Scenario 3: One-Click Project Initialization
|
||||
# Flow: Input description → Create repo → README/CONTRIBUTING → CI config →
|
||||
# Initial Issues + Milestone → Branch protection → Initial Release
|
||||
# Flow: Input description -> Create repo -> README/CONTRIBUTING/CI config ->
|
||||
# Initial Issues -> Branch protection -> Initial Release
|
||||
#
|
||||
# Commands chained:
|
||||
# 1. repo +create -- create repository
|
||||
# 2. wiki +create -- create README wiki page
|
||||
# 3. wiki +create -- create CONTRIBUTING guide
|
||||
# 4. issue +create -- create initial issues with milestone
|
||||
# 5. branch +protect -- protect master branch
|
||||
# 6. release +create -- create initial release
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# 4. wiki +create -- create CI/CD config guide
|
||||
# 5. issue +create -- create initial issues
|
||||
# 6. branch +protect -- protect master branch
|
||||
# 7. release +create -- create initial release
|
||||
# ----------------------------------------------------------------
|
||||
#Requires -Version 5.1
|
||||
|
||||
param(
|
||||
|
|
@ -29,7 +30,7 @@ $ErrorActionPreference = "Stop"
|
|||
Import-Module "$PSScriptRoot/lib/common.psm1" -Force
|
||||
|
||||
if ($Help) {
|
||||
Write-Host "Usage: pwsh 03-project-init.ps1 -Owner OWNER -Name REPO_NAME -Description DESC [-Lang go|python|node|java] [-Private] [-DryRun]"
|
||||
Write-Host "Usage: powershell 03-project-init.ps1 -Owner OWNER -Name REPO_NAME -Description DESC [-Lang go|python|node|java] [-Private] [-DryRun]"
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
|
@ -39,9 +40,9 @@ if (-not $Owner) {
|
|||
$Owner = $detected.Owner
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
Log-Title "Project Initialization: $Owner/$Name"
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
Write-Host " Owner: $Owner"
|
||||
Write-Host " Name: $Name"
|
||||
Write-Host " Description: $Description"
|
||||
|
|
@ -49,7 +50,7 @@ Write-Host " Language: $Lang"
|
|||
Write-Host " Private: $($Private.IsPresent)"
|
||||
Divider
|
||||
|
||||
# ── Step 1: Create Repository ────────────────────────────────────────
|
||||
# -- 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
|
||||
|
|
@ -60,128 +61,34 @@ if ($repoResult) {
|
|||
exit 1
|
||||
}
|
||||
|
||||
# ── Step 2: Create README ────────────────────────────────────────────
|
||||
# -- Step 2: Create README --
|
||||
Log-Step "Creating README wiki page..."
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
$langContent = switch ($Lang) {
|
||||
$langSection = switch ($Lang) {
|
||||
"go" {
|
||||
@"
|
||||
### Prerequisites
|
||||
- Go 1.21+
|
||||
- Git
|
||||
|
||||
### Installation
|
||||
``````bash
|
||||
git clone https://gitlink.org.cn/$Owner/$Name.git
|
||||
cd $Name
|
||||
go mod download
|
||||
go build ./...
|
||||
``````
|
||||
|
||||
### Usage
|
||||
``````bash
|
||||
go run main.go
|
||||
``````
|
||||
|
||||
### Testing
|
||||
``````bash
|
||||
go test ./...
|
||||
``````
|
||||
"@
|
||||
"### Prerequisites`n- Go 1.21+`n- Git`n`n### Installation`n``````bash`ngit clone https://gitlink.org.cn/$Owner/$Name.git`ncd $Name`ngo mod download`ngo build ./...`n```````n`n### Usage`n``````bash`ngo run main.go`n```````n`n### Testing`n``````bash`ngo test ./...`n``````"
|
||||
}
|
||||
"python" {
|
||||
@"
|
||||
### Prerequisites
|
||||
- Python 3.9+
|
||||
- pip
|
||||
|
||||
### Installation
|
||||
``````bash
|
||||
git clone https://gitlink.org.cn/$Owner/$Name.git
|
||||
cd $Name
|
||||
pip install -r requirements.txt
|
||||
``````
|
||||
|
||||
### Usage
|
||||
``````bash
|
||||
python main.py
|
||||
``````
|
||||
|
||||
### Testing
|
||||
``````bash
|
||||
pytest
|
||||
``````
|
||||
"@
|
||||
"### Prerequisites`n- Python 3.9+`n- pip`n`n### Installation`n``````bash`ngit clone https://gitlink.org.cn/$Owner/$Name.git`ncd $Name`npip install -r requirements.txt`n```````n`n### Usage`n``````bash`npython main.py`n```````n`n### Testing`n``````bash`npytest`n``````"
|
||||
}
|
||||
"node" {
|
||||
@"
|
||||
### Prerequisites
|
||||
- Node.js 18+
|
||||
- npm or yarn
|
||||
|
||||
### Installation
|
||||
``````bash
|
||||
git clone https://gitlink.org.cn/$Owner/$Name.git
|
||||
cd $Name
|
||||
npm install
|
||||
``````
|
||||
|
||||
### Usage
|
||||
``````bash
|
||||
npm start
|
||||
``````
|
||||
|
||||
### Testing
|
||||
``````bash
|
||||
npm test
|
||||
``````
|
||||
"@
|
||||
"### Prerequisites`n- Node.js 18+`n- npm or yarn`n`n### Installation`n``````bash`ngit clone https://gitlink.org.cn/$Owner/$Name.git`ncd $Name`nnpm install`n```````n`n### Usage`n``````bash`nnpm start`n```````n`n### Testing`n``````bash`nnpm test`n``````"
|
||||
}
|
||||
"java" {
|
||||
@"
|
||||
### Prerequisites
|
||||
- JDK 17+
|
||||
- Maven 3.8+
|
||||
|
||||
### Installation
|
||||
``````bash
|
||||
git clone https://gitlink.org.cn/$Owner/$Name.git
|
||||
cd $Name
|
||||
mvn clean install
|
||||
``````
|
||||
|
||||
### Usage
|
||||
``````bash
|
||||
mvn exec:java
|
||||
``````
|
||||
|
||||
### Testing
|
||||
``````bash
|
||||
mvn test
|
||||
``````
|
||||
"@
|
||||
"### Prerequisites`n- JDK 17+`n- Maven 3.8+`n`n### Installation`n``````bash`ngit clone https://gitlink.org.cn/$Owner/$Name.git`ncd $Name`nmvn clean install`n```````n`n### Usage`n``````bash`nmvn exec:java`n```````n`n### Testing`n``````bash`nmvn test`n``````"
|
||||
}
|
||||
default { "" }
|
||||
}
|
||||
|
||||
$readmeContent = @"
|
||||
# $Name
|
||||
|
||||
$Description
|
||||
|
||||
## Getting Started
|
||||
|
||||
$langContent
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING](./CONTRIBUTING) for guidelines.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
"@
|
||||
$readmeContent = "# $Name" + "`n`n"
|
||||
$readmeContent += "$Description" + "`n`n"
|
||||
$readmeContent += "## Getting Started" + "`n`n"
|
||||
$readmeContent += $langSection + "`n`n"
|
||||
$readmeContent += "## Contributing" + "`n`n"
|
||||
$readmeContent += "See [CONTRIBUTING](./CONTRIBUTING) for guidelines." + "`n`n"
|
||||
$readmeContent += "## License" + "`n`n"
|
||||
$readmeContent += "This project is licensed under the MIT License."
|
||||
|
||||
$wikiOk = $false
|
||||
for ($attempt = 1; $attempt -le 3; $attempt++) {
|
||||
|
|
@ -195,41 +102,28 @@ for ($attempt = 1; $attempt -le 3; $attempt++) {
|
|||
}
|
||||
if (-not $wikiOk) { Log-Warn "README wiki creation may have failed" }
|
||||
|
||||
# ── Step 3: Create CONTRIBUTING Guide ────────────────────────────────
|
||||
# -- Step 3: Create CONTRIBUTING Guide --
|
||||
Log-Step "Creating CONTRIBUTING guide..."
|
||||
|
||||
$contribContent = @"
|
||||
# Contributing to $Name
|
||||
|
||||
Thank you for your interest in contributing!
|
||||
|
||||
## How to Contribute
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch: ``git checkout -b feature/my-feature``
|
||||
3. Make your changes
|
||||
4. Run tests to ensure everything passes
|
||||
5. Commit your changes: ``git commit -m 'feat: add my feature'``
|
||||
6. Push to your fork: ``git push origin feature/my-feature``
|
||||
7. Create a Pull Request
|
||||
|
||||
## Code Style
|
||||
|
||||
- Follow the existing code style
|
||||
- Write meaningful commit messages
|
||||
- Add tests for new features
|
||||
- Update documentation as needed
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
- Use the issue tracker
|
||||
- Include reproduction steps
|
||||
- Include environment details
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please be respectful and constructive in all interactions.
|
||||
"@
|
||||
$contribContent = "# Contributing to $Name" + "`n`n"
|
||||
$contribContent += "Thank you for your interest in contributing!" + "`n`n"
|
||||
$contribContent += "## How to Contribute" + "`n`n"
|
||||
$contribContent += "1. Fork the repository" + "`n"
|
||||
$contribContent += "2. Create a feature branch: ``git checkout -b feature/my-feature```n"
|
||||
$contribContent += "3. Make your changes" + "`n"
|
||||
$contribContent += "4. Run tests to ensure everything passes" + "`n"
|
||||
$contribContent += "5. Commit your changes: ``git commit -m 'feat: add my feature'```n"
|
||||
$contribContent += "6. Push to your fork: ``git push origin feature/my-feature```n"
|
||||
$contribContent += "7. Create a Pull Request" + "`n`n"
|
||||
$contribContent += "## Code Style" + "`n`n"
|
||||
$contribContent += "- Follow the existing code style" + "`n"
|
||||
$contribContent += "- Write meaningful commit messages" + "`n"
|
||||
$contribContent += "- Add tests for new features" + "`n"
|
||||
$contribContent += "- Update documentation as needed" + "`n`n"
|
||||
$contribContent += "## Reporting Issues" + "`n`n"
|
||||
$contribContent += "- Use the issue tracker" + "`n"
|
||||
$contribContent += "- Include reproduction steps" + "`n"
|
||||
$contribContent += "- Include environment details"
|
||||
|
||||
$wikiOk = $false
|
||||
for ($attempt = 1; $attempt -le 3; $attempt++) {
|
||||
|
|
@ -243,88 +137,50 @@ for ($attempt = 1; $attempt -le 3; $attempt++) {
|
|||
}
|
||||
if (-not $wikiOk) { Log-Warn "CONTRIBUTING wiki creation may have failed" }
|
||||
|
||||
# ── Step 4: Create CI Config Wiki Page ───────────────────────────────
|
||||
# -- Step 4: Create CI Config Guide --
|
||||
Log-Step "Creating CI/CD configuration guide..."
|
||||
|
||||
$ciContent = @"
|
||||
# CI/CD Configuration
|
||||
|
||||
## GitLink CI Setup
|
||||
|
||||
This project uses GitLink CI for continuous integration.
|
||||
|
||||
### Pipeline Stages
|
||||
|
||||
1. **Test**: Run unit tests
|
||||
2. **Build**: Build the project
|
||||
3. **Deploy**: Deploy to staging (master branch only)
|
||||
|
||||
### Configuration
|
||||
|
||||
Create a ``.gitlink-ci.yml`` file in the repository root:
|
||||
|
||||
``````yaml
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
- deploy
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script:
|
||||
- echo "Running tests..."
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script:
|
||||
- echo "Building project..."
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "Deploying..."
|
||||
only:
|
||||
- master
|
||||
``````
|
||||
|
||||
### Status Badges
|
||||
|
||||
Add a CI status badge to your README:
|
||||
|
||||
``````markdown
|
||||
[](https://gitlink.org.cn/$Owner/$Name/pipelines)
|
||||
``````
|
||||
"@
|
||||
$ciContent = "# CI/CD Configuration" + "`n`n"
|
||||
$ciContent += "## GitLink CI Setup" + "`n`n"
|
||||
$ciContent += "This project uses GitLink CI for continuous integration." + "`n`n"
|
||||
$ciContent += "### Pipeline Stages" + "`n`n"
|
||||
$ciContent += "1. **Test**: Run unit tests" + "`n"
|
||||
$ciContent += "2. **Build**: Build the project" + "`n"
|
||||
$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
|
||||
Log-Ok "CI/CD configuration guide created"
|
||||
|
||||
# ── Step 5: Create Initial Issues with Milestone ─────────────────────
|
||||
# -- Step 5: Create Initial Issues --
|
||||
Log-Step "Creating initial issues..."
|
||||
|
||||
$issuesToCreate = @(
|
||||
@{ Title = "Setup CI/CD Pipeline"; Body = "Configure continuous integration and deployment for the project.`n`n## Tasks`n- [ ] Create `.gitlink-ci.yml` configuration`n- [ ] Setup test stage`n- [ ] Setup build stage`n- [ ] Setup deploy stage`n- [ ] Add status badge to README"; Label = "feature" },
|
||||
@{ Title = "Setup CI/CD Pipeline"; Body = "Configure continuous integration and deployment for the project.`n`n## Tasks`n- [ ] Create .gitlink-ci.yml configuration`n- [ ] Setup test stage`n- [ ] Setup build stage`n- [ ] Setup deploy stage`n- [ ] Add status badge to README"; Label = "feature" },
|
||||
@{ Title = "Write Project Documentation"; Body = "Complete project documentation including API docs and architecture guide.`n`n## Tasks`n- [ ] Write API documentation`n- [ ] Create architecture diagram`n- [ ] Add usage examples`n- [ ] Document configuration options"; Label = "documentation" },
|
||||
@{ Title = "Setup Code Review Process"; Body = "Establish code review guidelines and automation.`n`n## Tasks`n- [ ] Define review checklist`n- [ ] Setup branch protection rules`n- [ ] Configure required reviewers`n- [ ] Document review process"; Label = "enhancement" },
|
||||
@{ Title = "Add Unit Tests"; Body = "Add comprehensive unit test coverage for core modules.`n`n## Tasks`n- [ ] Setup test framework`n- [ ] Write tests for core modules`n- [ ] Achieve 80% code coverage`n- [ ] Add CI test integration"; Label = "enhancement" },
|
||||
@{ Title = "Add Unit Tests"; Body = "Add comprehensive unit test coverage for core modules.`n`n## Tasks`n- [ ] Setup test framework`n- [ ] Write tests for core modules`n- [ ] Achieve 80 percent code coverage`n- [ ] Add CI test integration"; Label = "enhancement" },
|
||||
@{ Title = "Setup Dependency Management"; Body = "Configure dependency scanning and updates.`n`n## Tasks`n- [ ] Setup dependency scanner`n- [ ] Configure automatic updates`n- [ ] Add license compliance check`n- [ ] Document dependency policy"; Label = "security" }
|
||||
)
|
||||
|
||||
foreach ($entry in $issuesToCreate) {
|
||||
$issueResult = Invoke-GL issue,+create,--owner,$Owner,--repo,$Name,--title,$entry.Title,--body,$entry.Body
|
||||
if ($issueResult) {
|
||||
$issueJson = $issueResult | ConvertFrom-Json
|
||||
$issueNum = $issueJson.data.id ?? $issueJson.data.number
|
||||
if ($issueNum) {
|
||||
Invoke-GL issue,+label-add,--owner,$Owner,--repo,$Name,--number,$issueNum,--labels,$entry.Label | Out-Null
|
||||
Log-Ok "Issue created: #$issueNum - $($entry.Title)"
|
||||
try {
|
||||
$issueJson = $issueResult | ConvertFrom-Json
|
||||
$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
|
||||
Log-Ok "Issue created: #$issueNum - $($entry.Title)"
|
||||
}
|
||||
} catch {
|
||||
Log-Warn "Issue creation may have failed: $($entry.Title)"
|
||||
}
|
||||
} else {
|
||||
Log-Warn "Issue creation may have failed: $($entry.Title)"
|
||||
}
|
||||
}
|
||||
|
||||
# ── Step 6: Protect Default Branch ───────────────────────────────────
|
||||
# -- 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))) {
|
||||
|
|
@ -333,28 +189,22 @@ if ($protectResult -and (Get-JsonOk ($protectResult | ConvertFrom-Json))) {
|
|||
Log-Warn "Branch protection may have failed (may require admin permissions)"
|
||||
}
|
||||
|
||||
# ── Step 7: Create Initial Release ───────────────────────────────────
|
||||
# -- Step 7: Create Initial Release --
|
||||
Log-Step "Creating initial release v0.1.0..."
|
||||
|
||||
$releaseBody = @"
|
||||
# v0.1.0 - Initial Release
|
||||
|
||||
## What's New
|
||||
- Project initialized with $Lang template
|
||||
- README and CONTRIBUTING guides created
|
||||
- CI/CD configuration guide created
|
||||
- 5 initial issues filed
|
||||
- Branch protection enabled
|
||||
|
||||
## Next Steps
|
||||
- [ ] Setup CI/CD pipeline
|
||||
- [ ] Write comprehensive tests
|
||||
- [ ] Complete documentation
|
||||
- [ ] First feature implementation
|
||||
|
||||
---
|
||||
*Auto-initialized by gitlink-cli project-init workflow*
|
||||
"@
|
||||
$releaseBody = "# v0.1.0 - Initial Release" + "`n`n"
|
||||
$releaseBody += "## What's New" + "`n"
|
||||
$releaseBody += "- Project initialized with $Lang template" + "`n"
|
||||
$releaseBody += "- README and CONTRIBUTING guides created" + "`n"
|
||||
$releaseBody += "- CI/CD configuration guide created" + "`n"
|
||||
$releaseBody += "- 5 initial issues filed" + "`n"
|
||||
$releaseBody += "- Branch protection enabled" + "`n`n"
|
||||
$releaseBody += "## Next Steps" + "`n"
|
||||
$releaseBody += "- [ ] Setup CI/CD pipeline" + "`n"
|
||||
$releaseBody += "- [ ] Write comprehensive tests" + "`n"
|
||||
$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))) {
|
||||
|
|
@ -363,9 +213,9 @@ if ($releaseResult -and (Get-JsonOk ($releaseResult | ConvertFrom-Json))) {
|
|||
Log-Warn "Release creation may have failed"
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
Log-Title "Project Initialization Complete"
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
Write-Host " Repository: $Owner/$Name" -ForegroundColor Green
|
||||
Write-Host " README: Wiki page" -ForegroundColor Green
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
# Scenario 4: Multi-Repo Collaboration
|
||||
# Flow: Cross-repo issue tracking → PR status dashboard → Coordinated release
|
||||
# Flow: Cross-repo issue tracking -> PR status dashboard -> Coordinated release
|
||||
#
|
||||
# Commands chained:
|
||||
# 1. repo +list -- list all repos in org
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
# 4. release +list -- check release status across repos
|
||||
# 5. release +create -- coordinated release (optional)
|
||||
# 6. Generate HTML dashboard
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
#Requires -Version 5.1
|
||||
|
||||
param(
|
||||
|
|
@ -26,13 +26,13 @@ $ErrorActionPreference = "Stop"
|
|||
Import-Module "$PSScriptRoot/lib/common.psm1" -Force
|
||||
|
||||
if ($Help) {
|
||||
Write-Host "Usage: pwsh 04-multi-repo-collab.ps1 -Org ORG [-Repos 'repo1,repo2'] [-Release TAG] [-Output FILE] [-DryRun]"
|
||||
Write-Host "Usage: powershell 04-multi-repo-collab.ps1 -Org ORG [-Repos 'repo1,repo2'] [-Release TAG] [-Output FILE] [-DryRun]"
|
||||
exit 0
|
||||
}
|
||||
|
||||
Check-Auth
|
||||
|
||||
# ── Step 1: List Repositories ────────────────────────────────────────
|
||||
# -- Step 1: List Repositories --
|
||||
Log-Title "Multi-Repo Collaboration Dashboard"
|
||||
|
||||
Log-Step "Fetching repositories for org: $Org..."
|
||||
|
|
@ -46,21 +46,20 @@ elseif ($rd -is [array]) { $allRepos = $rd }
|
|||
|
||||
Log-Ok "Found $($allRepos.Count) repositories"
|
||||
|
||||
# Filter repos if specified
|
||||
$repoList = @()
|
||||
if ($Repos) {
|
||||
$repoList = $Repos -split ','
|
||||
Log-Info "Filtering to specified repos: $($repoList -join ', ')"
|
||||
} else {
|
||||
foreach ($r in $allRepos) {
|
||||
$rname = $r.name ?? $r.identifier
|
||||
$rname = if ($r.name) { $r.name } elseif ($r.identifier) { $r.identifier } else { $null }
|
||||
if ($rname) { $repoList += $rname }
|
||||
}
|
||||
}
|
||||
|
||||
Log-Ok "Will process $($repoList.Count) repositories"
|
||||
|
||||
# ── Step 2-3: Collect Issues and PRs from each repo ──────────────────
|
||||
# -- Step 2-3: Collect Issues and PRs from each repo --
|
||||
Log-Title "Collecting Data Across Repos"
|
||||
|
||||
$totalIssues = 0; $totalOpenIssues = 0; $totalPRs = 0; $totalOpenPRs = 0
|
||||
|
|
@ -70,15 +69,12 @@ foreach ($repo in $repoList) {
|
|||
Divider
|
||||
Log-Step "Processing $Org/$repo..."
|
||||
|
||||
# Open issues
|
||||
$issuesJson = Invoke-GL issue,+list,--owner,$Org,--repo,$repo,--state,open,--limit,50
|
||||
$openIssues = if ($issuesJson) { @($issuesJson.data.issues).Count } else { 0 }
|
||||
|
||||
# Closed issues
|
||||
$closedJson = Invoke-GL issue,+list,--owner,$Org,--repo,$repo,--state,closed,--limit,50
|
||||
$closedIssues = if ($closedJson) { @($closedJson.data.issues).Count } else { 0 }
|
||||
|
||||
# Open PRs
|
||||
$prsJson = Invoke-GL pr,+list,--owner,$Org,--repo,$repo,--state,open,--limit,50
|
||||
$openPRs = 0
|
||||
if ($prsJson) {
|
||||
|
|
@ -88,7 +84,6 @@ foreach ($repo in $repoList) {
|
|||
elseif ($pd -is [array]) { $openPRs = $pd.Count }
|
||||
}
|
||||
|
||||
# Merged PRs
|
||||
$mergedJson = Invoke-GL pr,+list,--owner,$Org,--repo,$repo,--state,merged,--limit,50
|
||||
$mergedPRs = 0
|
||||
if ($mergedJson) {
|
||||
|
|
@ -98,53 +93,28 @@ foreach ($repo in $repoList) {
|
|||
elseif ($md -is [array]) { $mergedPRs = $md.Count }
|
||||
}
|
||||
|
||||
# Latest release
|
||||
$releaseJson = Invoke-GL release,+list,--owner,$Org,--repo,$repo,--limit,1
|
||||
$latestRelease = "none"
|
||||
if ($releaseJson -and $releaseJson.data.releases) {
|
||||
$releases = @($releaseJson.data.releases)
|
||||
if ($releases.Count -gt 0) {
|
||||
$latestRelease = $releases[0].tag_name ?? $releases[0].name ?? "none"
|
||||
$latestRelease = if ($releases[0].tag_name) { $releases[0].tag_name } elseif ($releases[0].name) { $releases[0].name } else { "none" }
|
||||
}
|
||||
}
|
||||
|
||||
Log-Ok "$repo : Issues(open:$openIssues closed:$closedIssues) PRs(open:$openPRs merged:$mergedPRs) Release:$latestRelease"
|
||||
|
||||
# Build PR details HTML
|
||||
$prDetailsHtml = ""
|
||||
if ($openPRs -gt 0 -and $prsJson) {
|
||||
$prData = @()
|
||||
$pd2 = $prsJson.data
|
||||
if ($pd2.issues) { $prData = @($pd2.issues) }
|
||||
elseif ($pd2.pulls) { $prData = @($pd2.pulls) }
|
||||
elseif ($pd2 -is [array]) { $prData = $pd2 }
|
||||
|
||||
$limit = [Math]::Min($prData.Count, 5)
|
||||
for ($i = 0; $i -lt $limit; $i++) {
|
||||
$prId = $prData[$i].pull_request_number ?? $prData[$i].number ?? $prData[$i].id ?? ""
|
||||
$prTitle = $prData[$i].subject ?? $prData[$i].title ?? ""
|
||||
$prAuthor = $prData[$i].author_login ?? $prData[$i].author.login ?? "unknown"
|
||||
$prDetailsHtml += "<tr><td>#$prId</td><td>$prTitle</td><td>@$prAuthor</td><td>open</td></tr>`n"
|
||||
}
|
||||
}
|
||||
|
||||
# Health status
|
||||
$statusColor = "green"
|
||||
$healthText = "Healthy"
|
||||
if ($openIssues -gt 10) { $statusColor = "orange"; $healthText = "Moderate" }
|
||||
if ($openIssues -gt 20) { $statusColor = "red"; $healthText = "Needs Attention" }
|
||||
|
||||
$dashboardRows += @"
|
||||
<tr>
|
||||
<td><a href="https://gitlink.org.cn/$Org/$repo">$repo</a></td>
|
||||
<td>$openIssues</td>
|
||||
<td>$closedIssues</td>
|
||||
<td>$openPRs</td>
|
||||
<td>$mergedPRs</td>
|
||||
<td>$latestRelease</td>
|
||||
<td style="color:$statusColor;font-weight:bold;">$healthText</td>
|
||||
</tr>
|
||||
"@
|
||||
$dashboardRows += "<tr>`n"
|
||||
$dashboardRows += " <td><a href=`"https://gitlink.org.cn/$Org/$repo`">$repo</a></td>`n"
|
||||
$dashboardRows += " <td>$openIssues</td><td>$closedIssues</td>`n"
|
||||
$dashboardRows += " <td>$openPRs</td><td>$mergedPRs</td><td>$latestRelease</td>`n"
|
||||
$dashboardRows += " <td style=`"color:$statusColor;font-weight:bold;`">$healthText</td>`n"
|
||||
$dashboardRows += "</tr>`n"
|
||||
|
||||
$totalIssues += $openIssues + $closedIssues
|
||||
$totalOpenIssues += $openIssues
|
||||
|
|
@ -152,14 +122,13 @@ foreach ($repo in $repoList) {
|
|||
$totalOpenPRs += $openPRs
|
||||
}
|
||||
|
||||
# ── Step 4: Generate HTML Dashboard ──────────────────────────────────
|
||||
# -- Step 4: Generate HTML Dashboard --
|
||||
Log-Title "Generating Dashboard"
|
||||
|
||||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
$repoCount = $repoList.Count
|
||||
|
||||
$html = @"
|
||||
<!DOCTYPE html>
|
||||
$html = '<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
|
@ -167,7 +136,7 @@ $html = @"
|
|||
<title>Multi-Repo Collaboration Dashboard</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f5f5f5; padding: 20px; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #f5f5f5; padding: 20px; }
|
||||
.container { max-width: 1200px; margin: 0 auto; }
|
||||
h1 { color: #333; margin-bottom: 20px; }
|
||||
.summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 30px; }
|
||||
|
|
@ -190,28 +159,27 @@ a:hover { text-decoration: underline; }
|
|||
<body>
|
||||
<div class="container">
|
||||
<h1>Multi-Repo Collaboration Dashboard</h1>
|
||||
<p class="timestamp">Generated: $timestamp | Organization: $Org</p>
|
||||
<p class="timestamp">Generated: ' + $timestamp + ' | Organization: ' + $Org + '</p>
|
||||
<div class="summary">
|
||||
<div class="card blue"><h3>Total Repos</h3><div class="value">$repoCount</div></div>
|
||||
<div class="card orange"><h3>Open Issues</h3><div class="value">$totalOpenIssues</div></div>
|
||||
<div class="card purple"><h3>Open PRs</h3><div class="value">$totalOpenPRs</div></div>
|
||||
<div class="card green"><h3>Total Activity</h3><div class="value">$totalIssues</div></div>
|
||||
<div class="card blue"><h3>Total Repos</h3><div class="value">' + $repoCount + '</div></div>
|
||||
<div class="card orange"><h3>Open Issues</h3><div class="value">' + $totalOpenIssues + '</div></div>
|
||||
<div class="card purple"><h3>Open PRs</h3><div class="value">' + $totalOpenPRs + '</div></div>
|
||||
<div class="card green"><h3>Total Activity</h3><div class="value">' + $totalIssues + '</div></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>
|
||||
$dashboardRows
|
||||
' + $dashboardRows + '
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"@
|
||||
</html>'
|
||||
|
||||
$html | Out-File -FilePath $Output -Encoding UTF8
|
||||
Log-Ok "Dashboard saved to: $Output"
|
||||
|
||||
# ── Step 5: Coordinated Release ──────────────────────────────────────
|
||||
# -- Step 5: Coordinated Release --
|
||||
if ($Release) {
|
||||
Log-Title "Coordinated Release: $Release"
|
||||
|
||||
|
|
@ -227,9 +195,9 @@ if ($Release) {
|
|||
}
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
Log-Title "Multi-Repo Dashboard Complete"
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
Write-Host " Repos processed: $repoCount" -ForegroundColor Green
|
||||
Write-Host " Total issues: $totalIssues (open: $totalOpenIssues)" -ForegroundColor Green
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
# Scenario 5: Contributor Growth System
|
||||
# Flow: Collect data → Calculate scores → Generate HTML → Publish Wiki → Award badges
|
||||
# Flow: Collect data -> Calculate scores -> Generate HTML -> Publish Wiki -> Award badges
|
||||
#
|
||||
# Scoring (AHP weight model):
|
||||
# - Issues Created: 15% weight (issue +list)
|
||||
|
|
@ -10,12 +10,12 @@
|
|||
# - Team Member: 15% weight (repo +members)
|
||||
#
|
||||
# Badges:
|
||||
# - Champion (冠军) >= 80
|
||||
# - Core Contributor >= 60
|
||||
# - Active Contributor >= 40
|
||||
# - Contributor >= 20
|
||||
# - Newcomer (新人) < 20
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# - Champion >= 80
|
||||
# - Core Contributor >= 60
|
||||
# - Active Contributor >= 40
|
||||
# - Contributor >= 20
|
||||
# - Newcomer < 20
|
||||
# ----------------------------------------------------------------
|
||||
#Requires -Version 5.1
|
||||
|
||||
param(
|
||||
|
|
@ -31,7 +31,7 @@ $ErrorActionPreference = "Stop"
|
|||
Import-Module "$PSScriptRoot/lib/common.psm1" -Force
|
||||
|
||||
if ($Help) {
|
||||
Write-Host "Usage: pwsh 05-contributor-growth.ps1 -Owner OWNER -Repo REPO [-Sample N] [-Award] [-DryRun]"
|
||||
Write-Host "Usage: powershell 05-contributor-growth.ps1 -Owner OWNER -Repo REPO [-Sample N] [-Award] [-DryRun]"
|
||||
Write-Host ""
|
||||
Write-Host " -Owner OWNER Repository owner"
|
||||
Write-Host " -Repo REPO Repository name"
|
||||
|
|
@ -47,11 +47,11 @@ $Owner = $r.Owner; $Repo = $r.Repo
|
|||
|
||||
$reportFile = "contrib-report-$Owner-$Repo.html"
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
Log-Title "Contributor Growth System: $Owner/$Repo"
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# ── Step 1: Collect Data ─────────────────────────────────────────────
|
||||
# -- Step 1: Collect Data --
|
||||
Log-Step "Collecting data..."
|
||||
|
||||
$issuesOpen = Invoke-GLCheck issue,+list,--owner,$Owner,--repo,$Repo,--state,open,--limit,100
|
||||
|
|
@ -80,10 +80,9 @@ $memberCount = $memberData.Count
|
|||
|
||||
Log-Ok "Issues(open:$openCount closed:$closedCount) PRs(merged:$prMergedCount) Members:$memberCount"
|
||||
|
||||
# ── Step 2: Build Contributor Data ───────────────────────────────────
|
||||
# -- Step 2: Build Contributor Data --
|
||||
Log-Step "Building contributor profiles..."
|
||||
|
||||
# Hashtable: user -> stats
|
||||
$contribData = @{}
|
||||
|
||||
function Ensure-Contrib {
|
||||
|
|
@ -101,7 +100,7 @@ $allIssues = @()
|
|||
if ($issuesOpen) { $allIssues += @($issuesOpen.data.issues) }
|
||||
if ($issuesClosed) { $allIssues += @($issuesClosed.data.issues) }
|
||||
foreach ($issue in $allIssues) {
|
||||
$author = $issue.author.login ?? $issue.author.username
|
||||
$author = if ($issue.author.login) { $issue.author.login } elseif ($issue.author.username) { $issue.author.username } else { $null }
|
||||
if ($author) {
|
||||
Ensure-Contrib $author
|
||||
$contribData[$author].Issues++
|
||||
|
|
@ -113,8 +112,8 @@ Log-Step "Analyzing PR code changes (sampling $Sample)..."
|
|||
$prSample = [Math]::Min($prMergedCount, $Sample)
|
||||
for ($i = 0; $i -lt $prMergedCount; $i++) {
|
||||
$pr = $prMergedData[$i]
|
||||
$author = $pr.author_login ?? $pr.author.login
|
||||
$prId = $pr.pull_request_number ?? $pr.number ?? $pr.id
|
||||
$author = if ($pr.author_login) { $pr.author_login } elseif ($pr.author.login) { $pr.author.login } else { $null }
|
||||
$prId = if ($pr.pull_request_number) { $pr.pull_request_number } elseif ($pr.number) { $pr.number } elseif ($pr.id) { $pr.id } else { $null }
|
||||
if ($author) {
|
||||
Ensure-Contrib $author
|
||||
$contribData[$author].Merged++
|
||||
|
|
@ -123,8 +122,8 @@ for ($i = 0; $i -lt $prMergedCount; $i++) {
|
|||
$filesJson = Invoke-GL pr,+files,--owner,$Owner,--repo,$Repo,--id,$prId
|
||||
if ($filesJson -and $filesJson.data.files) {
|
||||
foreach ($f in $filesJson.data.files) {
|
||||
$add = $f.additions ?? $f.addition ?? 0
|
||||
$del = $f.deletions ?? $f.deletion ?? 0
|
||||
$add = if ($f.additions) { $f.additions } elseif ($f.addition) { $f.addition } else { 0 }
|
||||
$del = if ($f.deletions) { $f.deletions } elseif ($f.deletion) { $f.deletion } else { 0 }
|
||||
$contribData[$author].Additions += $add
|
||||
$contribData[$author].Deletions += $del
|
||||
}
|
||||
|
|
@ -134,7 +133,7 @@ for ($i = 0; $i -lt $prMergedCount; $i++) {
|
|||
|
||||
# Members
|
||||
foreach ($m in $memberData) {
|
||||
$login = $m.login ?? $m.username
|
||||
$login = if ($m.login) { $m.login } elseif ($m.username) { $m.username } else { $null }
|
||||
if ($login) {
|
||||
Ensure-Contrib $login
|
||||
$contribData[$login].IsMember = $true
|
||||
|
|
@ -144,16 +143,16 @@ foreach ($m in $memberData) {
|
|||
# Comments (sample open issues)
|
||||
Log-Step "Sampling issue comments..."
|
||||
if ($issuesOpen) {
|
||||
$openIssues = @($issuesOpen.data.issues)
|
||||
$commentSample = [Math]::Min($openIssues.Count, 10)
|
||||
$openIssuesArr = @($issuesOpen.data.issues)
|
||||
$commentSample = [Math]::Min($openIssuesArr.Count, 10)
|
||||
for ($i = 0; $i -lt $commentSample; $i++) {
|
||||
$id = $openIssues[$i].id
|
||||
$id = $openIssuesArr[$i].id
|
||||
if (-not $id) { continue }
|
||||
$detail = Invoke-GL issue,+view,--owner,$Owner,--repo,$Repo,--number,$id
|
||||
if ($detail) {
|
||||
$commentCount = $detail.data.comment_journals_count ?? 0
|
||||
$commentCount = if ($detail.data.comment_journals_count) { $detail.data.comment_journals_count } else { 0 }
|
||||
if ($commentCount -gt 0) {
|
||||
$author = $openIssues[$i].author.login
|
||||
$author = $openIssuesArr[$i].author.login
|
||||
if ($author) {
|
||||
Ensure-Contrib $author
|
||||
$contribData[$author].Comments += $commentCount
|
||||
|
|
@ -163,10 +162,9 @@ if ($issuesOpen) {
|
|||
}
|
||||
}
|
||||
|
||||
# ── Step 3: Calculate Scores ─────────────────────────────────────────
|
||||
# -- Step 3: Calculate Scores --
|
||||
Log-Step "Calculating scores..."
|
||||
|
||||
# Find max values for normalization
|
||||
$maxIssues = 0; $maxMerged = 0; $maxLines = 0; $maxComments = 0
|
||||
foreach ($user in $contribData.Keys) {
|
||||
$c = $contribData[$user]
|
||||
|
|
@ -190,11 +188,11 @@ foreach ($user in $contribData.Keys) {
|
|||
$scores[$user] = $score
|
||||
}
|
||||
|
||||
# ── Step 4: Display Rankings ─────────────────────────────────────────
|
||||
# -- Step 4: Display Rankings --
|
||||
Log-Title "Contributor Rankings"
|
||||
Write-Host ""
|
||||
Write-Host ("{0,-4} {1,-18} {2,-8} {3,-8} {4,-12} {5,-10} {6,-8} {7}" -f "Rank","Contributor","Issues","Merged","+/- Lines","Comments","Score","Badge") -ForegroundColor White
|
||||
Write-Host " ──── ─────────────────── ──────── ──────── ──────────── ────────── ──────── ─────────────"
|
||||
Write-Host " ---- ------------------ -------- -------- ------------ ---------- -------- -------------"
|
||||
|
||||
$ranked = $scores.GetEnumerator() | Sort-Object -Property Value -Descending
|
||||
$rank = 1
|
||||
|
|
@ -204,29 +202,21 @@ foreach ($entry in $ranked) {
|
|||
$score = $entry.Value
|
||||
$c = $contribData[$user]
|
||||
$si = [int]$score
|
||||
$badge = switch ($true) {
|
||||
($si -ge 80) { "Champion" }
|
||||
($si -ge 60) { "Core Contributor" }
|
||||
($si -ge 40) { "Active Contributor" }
|
||||
($si -ge 20) { "Contributor" }
|
||||
default { "Newcomer" }
|
||||
}
|
||||
$badge = if ($si -ge 80) { "Champion" } elseif ($si -ge 60) { "Core Contributor" } elseif ($si -ge 40) { "Active Contributor" } elseif ($si -ge 20) { "Contributor" } else { "Newcomer" }
|
||||
$lines = $c.Additions + $c.Deletions
|
||||
Write-Host ("{0,-4} {1,-18} {2,-8} {3,-8} +{4,-6}/-{5,-4} {6,-10} {7,-8} {8}" -f $rank,$user,$c.Issues,$c.Merged,$c.Additions,$c.Deletions,$c.Comments,$score,$badge)
|
||||
$rankedList += @{ Rank=$rank; User=$user; Issues=$c.Issues; Merged=$c.Merged; Lines=$lines; Additions=$c.Additions; Deletions=$c.Deletions; Comments=$c.Comments; Score=$score; Badge=$badge }
|
||||
$rank++
|
||||
}
|
||||
|
||||
# ── Step 5: Generate HTML Report ─────────────────────────────────────
|
||||
# -- Step 5: Generate HTML Report --
|
||||
Log-Title "Generating HTML Report"
|
||||
|
||||
# Build pie chart data
|
||||
$pieData = ""
|
||||
foreach ($entry in $ranked) {
|
||||
$pieData += "{value: $($entry.Value), name: '$($entry.Key)'},"
|
||||
}
|
||||
|
||||
# Build table rows
|
||||
$tableRows = ""
|
||||
foreach ($r in $rankedList) {
|
||||
$rankCls = ""
|
||||
|
|
@ -242,25 +232,22 @@ foreach ($r in $rankedList) {
|
|||
default { "newcomer" }
|
||||
}
|
||||
|
||||
$tableRows += @"
|
||||
<tr><td class="rank$rankCls">$($r.Rank)</td><td>@$($r.User)</td><td>$($r.Issues)</td><td>$($r.Merged)</td><td>$($r.Lines)</td><td>$($r.Comments)</td><td>$($r.Score)</td><td><span class="badge badge-$badgeCls">$($r.Badge)</span></td></tr>
|
||||
"@
|
||||
$tableRows += ' <tr><td class="rank' + $rankCls + '">' + $r.Rank + '</td><td>@' + $r.User + '</td><td>' + $r.Issues + '</td><td>' + $r.Merged + '</td><td>' + $r.Lines + '</td><td>' + $r.Comments + '</td><td>' + $r.Score + '</td><td><span class="badge badge-' + $badgeCls + '">' + $r.Badge + '</span></td></tr>' + "`n"
|
||||
}
|
||||
|
||||
$totalIssuesCount = $openCount + $closedCount
|
||||
$contribCount = $contribData.Count
|
||||
|
||||
$html = @"
|
||||
<!DOCTYPE html>
|
||||
$html = '<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Contributor Report - $Owner/$Repo</title>
|
||||
<title>Contributor Report - ' + $Owner + '/' + $Repo + '</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 40px 20px; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 40px 20px; }
|
||||
.container { max-width: 1200px; margin: 0 auto; }
|
||||
.header { text-align: center; color: white; margin-bottom: 40px; }
|
||||
.header h1 { font-size: 2.5rem; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); }
|
||||
|
|
@ -298,12 +285,12 @@ $html = @"
|
|||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>Contributor Report</h1>
|
||||
<p>$Owner/$Repo - Team Contribution Analysis</p>
|
||||
<p>' + $Owner + '/' + $Repo + ' - Team Contribution Analysis</p>
|
||||
</div>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card"><div class="stat-value">$contribCount</div><div class="stat-label">Contributors</div></div>
|
||||
<div class="stat-card"><div class="stat-value">$totalIssuesCount</div><div class="stat-label">Total Issues</div></div>
|
||||
<div class="stat-card"><div class="stat-value">$prMergedCount</div><div class="stat-label">Merged PRs</div></div>
|
||||
<div class="stat-card"><div class="stat-value">' + $contribCount + '</div><div class="stat-label">Contributors</div></div>
|
||||
<div class="stat-card"><div class="stat-value">' + $totalIssuesCount + '</div><div class="stat-label">Total Issues</div></div>
|
||||
<div class="stat-card"><div class="stat-value">' + $prMergedCount + '</div><div class="stat-label">Merged PRs</div></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Score Distribution</h2>
|
||||
|
|
@ -312,7 +299,7 @@ $html = @"
|
|||
<div class="card">
|
||||
<h2>Detailed Rankings</h2>
|
||||
<table><thead><tr><th>Rank</th><th>Contributor</th><th>Issues</th><th>Merged PRs</th><th>Code Lines</th><th>Comments</th><th>Score</th><th>Badge</th></tr></thead><tbody>
|
||||
$tableRows
|
||||
' + $tableRows + '
|
||||
</tbody></table>
|
||||
</div>
|
||||
<div class="card">
|
||||
|
|
@ -329,30 +316,29 @@ $tableRows
|
|||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var chart = echarts.init(document.getElementById('pieChart'));
|
||||
var chart = echarts.init(document.getElementById("pieChart"));
|
||||
chart.setOption({
|
||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||
legend: { orient: 'vertical', left: 'left', top: 'middle' },
|
||||
tooltip: { trigger: "item", formatter: "{a} <br/>{b}: {c} ({d}%)" },
|
||||
legend: { orient: "vertical", left: "left", top: "middle" },
|
||||
series: [{
|
||||
name: 'Score',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
center: ['60%', '50%'],
|
||||
itemStyle: { borderRadius: 10, borderColor: '#fff', borderWidth: 2 },
|
||||
label: { show: true, formatter: '{b}\n{d}%' },
|
||||
data: [$pieData]
|
||||
name: "Score",
|
||||
type: "pie",
|
||||
radius: ["40%", "70%"],
|
||||
center: ["60%", "50%"],
|
||||
itemStyle: { borderRadius: 10, borderColor: "#fff", borderWidth: 2 },
|
||||
label: { show: true, formatter: "{b}\n{d}%" },
|
||||
data: [' + $pieData + ']
|
||||
}]
|
||||
});
|
||||
window.addEventListener('resize', () => chart.resize());
|
||||
window.addEventListener("resize", function() { chart.resize(); });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"@
|
||||
</html>'
|
||||
|
||||
$html | Out-File -FilePath $reportFile -Encoding UTF8
|
||||
Log-Ok "HTML report: $reportFile"
|
||||
|
||||
# ── Step 6: Publish to Wiki ──────────────────────────────────────────
|
||||
# -- Step 6: Publish to Wiki --
|
||||
Log-Step "Publishing to Wiki..."
|
||||
|
||||
$wikiRankRows = ""
|
||||
|
|
@ -364,34 +350,26 @@ foreach ($r in $rankedList) {
|
|||
"Contributor" { "Contributor" }
|
||||
default { "Newcomer" }
|
||||
}
|
||||
$wikiRankRows += "| $($r.Rank) | @$($r.User) | $($r.Issues) | $($r.Merged) | $($r.Lines) | $($r.Comments) | $($r.Score) | $shortBadge |`n"
|
||||
$wikiRankRows += "| $($r.Rank) | @$($r.User) | $($r.Issues) | $($r.Merged) | $($r.Lines) | $($r.Comments) | $($r.Score) | $shortBadge |" + "`n"
|
||||
}
|
||||
|
||||
$wikiTitle = "Contributor Leaderboard $(Get-Date -Format 'yyyy-MM-dd')"
|
||||
$wikiContent = @"
|
||||
# Contributor Leaderboard - $Owner/$Repo
|
||||
|
||||
*Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm')*
|
||||
|
||||
## Scoring System
|
||||
|
||||
| Dimension | Weight | Source |
|
||||
|-----------|--------|--------|
|
||||
| Issues Created | 15% | issue +list |
|
||||
| PRs Merged | 25% | pr +list state=merged |
|
||||
| Code Changes | 30% | pr +files |
|
||||
| Issue Comments | 15% | issue +view |
|
||||
| Team Member | 15% | repo +members |
|
||||
|
||||
## Rankings
|
||||
|
||||
| Rank | Contributor | Issues | Merged | Lines | Comments | Score | Badge |
|
||||
|------|-------------|--------|--------|-------|----------|-------|-------|
|
||||
$wikiRankRows
|
||||
|
||||
---
|
||||
*Auto-generated by gitlink-cli*
|
||||
"@
|
||||
$wikiContent = "# Contributor Leaderboard - $Owner/$Repo" + "`n`n"
|
||||
$wikiContent += "*Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm')*" + "`n`n"
|
||||
$wikiContent += "## Scoring System" + "`n`n"
|
||||
$wikiContent += "| Dimension | Weight | Source |" + "`n"
|
||||
$wikiContent += "|-----------|--------|--------|" + "`n"
|
||||
$wikiContent += "| Issues Created | 15% | issue +list |" + "`n"
|
||||
$wikiContent += "| PRs Merged | 25% | pr +list state=merged |" + "`n"
|
||||
$wikiContent += "| Code Changes | 30% | pr +files |" + "`n"
|
||||
$wikiContent += "| Issue Comments | 15% | issue +view |" + "`n"
|
||||
$wikiContent += "| Team Member | 15% | repo +members |" + "`n`n"
|
||||
$wikiContent += "## Rankings" + "`n`n"
|
||||
$wikiContent += "| Rank | Contributor | Issues | Merged | Lines | Comments | Score | Badge |" + "`n"
|
||||
$wikiContent += "|------|-------------|--------|--------|-------|----------|-------|-------|" + "`n"
|
||||
$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))) {
|
||||
|
|
@ -400,7 +378,7 @@ if ($wikiResult -and (Get-JsonOk ($wikiResult | ConvertFrom-Json))) {
|
|||
Log-Warn "Wiki publish failed"
|
||||
}
|
||||
|
||||
# ── Step 7: Award Badges (optional) ──────────────────────────────────
|
||||
# -- Step 7: Award Badges (optional) --
|
||||
if ($Award) {
|
||||
Log-Title "Awarding Badges"
|
||||
|
||||
|
|
@ -412,51 +390,41 @@ if ($Award) {
|
|||
|
||||
foreach ($badge in $badgeGroups.Keys) {
|
||||
$users = $badgeGroups[$badge]
|
||||
if ($badge -eq "Newcomer") { continue } # Skip newcomers
|
||||
if ($badge -eq "Newcomer") { continue }
|
||||
|
||||
$userList = ($users | ForEach-Object { "@$_" }) -join ", "
|
||||
$issueTitle = "Badge Award: $badge"
|
||||
$issueBody = @"
|
||||
## Congratulations! :tada:
|
||||
|
||||
The following contributors have earned the **$badge** badge:
|
||||
|
||||
$userList
|
||||
|
||||
### Badge Criteria
|
||||
$(switch ($badge) {
|
||||
"Champion" { "- Score >= 80: Exceptional contribution to the project" }
|
||||
"Core Contributor" { "- Score >= 60: Significant and consistent contributions" }
|
||||
"Active Contributor" { "- Score >= 40: Regular contributions to the project" }
|
||||
"Contributor" { "- Score >= 20: Made meaningful contributions" }
|
||||
})
|
||||
|
||||
### Scoring Dimensions
|
||||
- Issues Created: 15%
|
||||
- PRs Merged: 25%
|
||||
- Code Changes: 30%
|
||||
- Issue Comments: 15%
|
||||
- Team Member: 15%
|
||||
|
||||
---
|
||||
*Auto-awarded by gitlink-cli contributor-growth workflow*
|
||||
"@
|
||||
$issueBody = "## Congratulations!" + "`n`n"
|
||||
$issueBody += "The following contributors have earned the **$badge** badge:" + "`n`n"
|
||||
$issueBody += $userList + "`n`n"
|
||||
$issueBody += "### Badge Criteria" + "`n"
|
||||
$issueBody += switch ($badge) {
|
||||
"Champion" { "- Score >= 80: Exceptional contribution to the project" }
|
||||
"Core Contributor" { "- Score >= 60: Significant and consistent contributions" }
|
||||
"Active Contributor" { "- Score >= 40: Regular contributions to the project" }
|
||||
"Contributor" { "- Score >= 20: Made meaningful contributions" }
|
||||
}
|
||||
$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
|
||||
if ($issueResult) {
|
||||
$issueJson = $issueResult | ConvertFrom-Json
|
||||
$issueNum = $issueJson.data.id ?? $issueJson.data.number
|
||||
if ($issueNum) {
|
||||
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)"
|
||||
try {
|
||||
$issueJson = $issueResult | ConvertFrom-Json
|
||||
$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
|
||||
Log-Ok "Badge issue created: #$issueNum - $issueTitle ($($users.Count) recipients)"
|
||||
}
|
||||
} catch {
|
||||
Log-Warn "Badge issue creation may have failed: $issueTitle"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
Log-Title "Complete"
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
Write-Host " Contributors: $contribCount" -ForegroundColor Green
|
||||
Write-Host " HTML Report: $reportFile" -ForegroundColor Green
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
# Common utilities for gitlink-cli workflow scripts (PowerShell)
|
||||
#Requires -Version 5.1
|
||||
# Common utilities for gitlink-cli workflow scripts (PowerShell 5.1+)
|
||||
|
||||
$Script:GL = "gitlink-cli"
|
||||
|
||||
# ── Logging ──────────────────────────────────────────────────────────
|
||||
# -- Logging --
|
||||
function Log-Step { param([string]$Msg) Write-Host "[STEP] $Msg" -ForegroundColor Blue }
|
||||
function Log-Ok { param([string]$Msg) Write-Host "[ OK] $Msg" -ForegroundColor Green }
|
||||
function Log-Warn { param([string]$Msg) Write-Host "[WARN] $Msg" -ForegroundColor Yellow }
|
||||
function Log-Err { param([string]$Msg) Write-Host "[ ERR] $Msg" -ForegroundColor Red }
|
||||
function Log-Info { param([string]$Msg) Write-Host "[INFO] $Msg" -ForegroundColor Cyan }
|
||||
function Log-Title { param([string]$Msg) Write-Host "`n══════ $Msg ══════`n" -ForegroundColor White }
|
||||
function Divider { Write-Host "────────────────────────────────────────────────" -ForegroundColor Cyan }
|
||||
function Log-Title { param([string]$Msg) Write-Host ""; Write-Host "====== $Msg ======" -ForegroundColor White; Write-Host "" }
|
||||
function Divider { Write-Host "------------------------------------------------" -ForegroundColor Cyan }
|
||||
|
||||
# ── Auth Check ───────────────────────────────────────────────────────
|
||||
# -- Auth Check --
|
||||
function Check-Auth {
|
||||
if ($env:GITLINK_TOKEN) {
|
||||
Log-Ok "GITLINK_TOKEN is set"
|
||||
return
|
||||
}
|
||||
$status = & $Script:GL auth status 2>&1
|
||||
if ($status -match "logged in|✓") {
|
||||
$statusStr = $status -join " "
|
||||
if ($statusStr -match "logged in") {
|
||||
Log-Ok "Authenticated"
|
||||
return
|
||||
}
|
||||
Log-Err "Not authenticated. Please login first:"
|
||||
Log-Info " gitlink-cli auth login"
|
||||
Log-Info " `$env:GITLINK_TOKEN = 'your-private-token'"
|
||||
Log-Info ' $env:GITLINK_TOKEN = "your-private-token"'
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ── CLI Wrapper ──────────────────────────────────────────────────────
|
||||
# -- CLI Wrapper --
|
||||
function Invoke-GL {
|
||||
param([string[]]$Args)
|
||||
$output = & $Script:GL @Args --format json 2>&1
|
||||
|
|
@ -42,8 +42,9 @@ function Invoke-GLCheck {
|
|||
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 ($json.error.message ?? "unknown error")
|
||||
Log-Err $errMsg
|
||||
return $null
|
||||
}
|
||||
return $json
|
||||
|
|
@ -54,18 +55,13 @@ function Invoke-GLCheck {
|
|||
}
|
||||
}
|
||||
|
||||
# ── JSON Helpers ─────────────────────────────────────────────────────
|
||||
# -- JSON Helpers --
|
||||
function Get-JsonOk {
|
||||
param($Json)
|
||||
return ($Json.ok -eq $true)
|
||||
}
|
||||
|
||||
function Get-JsonData {
|
||||
param($Json)
|
||||
return $Json.data
|
||||
}
|
||||
|
||||
# ── Owner/Repo Detection ────────────────────────────────────────────
|
||||
# -- Owner/Repo Detection --
|
||||
function Detect-OwnerRepo {
|
||||
$remote = git remote get-url origin 2>$null
|
||||
if (-not $remote) {
|
||||
|
|
@ -83,27 +79,14 @@ function Resolve-OwnerRepo {
|
|||
param([string]$Owner, [string]$Repo)
|
||||
if (-not $Owner -or -not $Repo) {
|
||||
$detected = Detect-OwnerRepo
|
||||
$Owner = $Owner ?? $detected.Owner
|
||||
$Repo = $Repo ?? $detected.Repo
|
||||
if (-not $Owner) { $Owner = $detected.Owner }
|
||||
if (-not $Repo) { $Repo = $detected.Repo }
|
||||
}
|
||||
Log-Info "Using: $Owner/$Repo"
|
||||
return @{ Owner = $Owner; Repo = $Repo }
|
||||
}
|
||||
|
||||
# ── Confirmation ─────────────────────────────────────────────────────
|
||||
function Confirm-Action {
|
||||
param([string]$Msg, [switch]$DryRun)
|
||||
if ($DryRun) {
|
||||
Log-Warn "[DRY RUN] Would execute: $Msg"
|
||||
return $false
|
||||
}
|
||||
$answer = Read-Host "$Msg [y/N]"
|
||||
return ($answer -match '^[Yy]')
|
||||
}
|
||||
|
||||
# ── Date Helpers ─────────────────────────────────────────────────────
|
||||
# -- Date Helpers --
|
||||
function Get-DateToday { return (Get-Date -Format "yyyy-MM-dd") }
|
||||
function Get-DateWeekAgo { return ((Get-Date).AddDays(-7).ToString("yyyy-MM-dd")) }
|
||||
function Get-DateMonthAgo { return ((Get-Date).AddDays(-30).ToString("yyyy-MM-dd")) }
|
||||
|
||||
Export-ModuleMember -Function *
|
||||
|
|
|
|||
Loading…
Reference in New Issue