Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ area:database:
- any-glob-to-any-file:
- src/TeamBuilder.Infrastructure/**/Migrations/**
- src/TeamBuilder.Infrastructure/**/Persistence/**
- src/TeamBuilder.Infrastructure/**/Data/**
- src/TeamBuilder.Infrastructure/**/*Configuration.cs

area:tests:
- changed-files:
Expand Down Expand Up @@ -83,3 +85,10 @@ area:community:
- docs/community/**
- docs/contributing/**
- CONTRIBUTING.md

area:community:
- changed-files:
- any-glob-to-any-file:
- docs/community/**
- docs/contributing/**
- CONTRIBUTING.md
148 changes: 148 additions & 0 deletions docs/discussion-labels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# TeamBuilder Discussion Labels

This document records the standardized label assignments for all GitHub Discussions in the
TeamBuilder repository. Labels are applied using the confirmed standardized label set only.

## Final Label Assignments

### #5 — Welcome to TeamBuilder Discussions

| | |
|---|---|
| **Labels** | `area:community`, `help wanted` |
| **Removed** | `enhancement` |

---

### #6 — TeamBuilder Vision, Mission, and Roadmap

| | |
|---|---|
| **Labels** | `area:product`, `roadmap`, `help wanted` |
| **Removed** | `enhancement` |

---

### #7 — Start Here: How to Contribute to TeamBuilder

| | |
|---|---|
| **Labels** | `area:community`, `area:docs`, `documentation`, `help wanted`, `good first issue` |
| **Removed** | _(none)_ |

---

### #8 — TeamBuilder Architecture: API-First, Frontend-Agnostic, Azure SQL Ready

| | |
|---|---|
| **Labels** | `area:architecture`, `area:api`, `area:database`, `help wanted` |
| **Removed** | `enhancement` |

---

### #9 — Common Roster Language Proposal

| | |
|---|---|
| **Labels** | `area:roster-language`, `area:product`, `enhancement`, `help wanted`, `question` |
| **Removed** | _(none)_ |

---

### #10 — Product Use Cases: Sports, Gaming, Events, and Communities

| | |
|---|---|
| **Labels** | `area:product`, `area:community`, `question` |
| **Removed** | `enhancement` |

---

### #11 — Team Refill and Open Spot Matching

| | |
|---|---|
| **Labels** | `area:product`, `enhancement`, `question` |
| **Removed** | _(none)_ |

---

### #12 — Frontend Clients and Integration Ideas

| | |
|---|---|
| **Labels** | `area:frontend`, `area:api`, `enhancement`, `help wanted` |
| **Removed** | _(none)_ |

---

### #13 — DevOps, Environments, and Deployment Strategy

| | |
|---|---|
| **Labels** | `area:devops`, `area:database`, `area:github-actions`, `help wanted` |
| **Removed** | `enhancement` |

---

### #14 — Ask TeamBuilder Questions Here

| | |
|---|---|
| **Labels** | `question`, `area:community` |
| **Removed** | _(none)_ |

---

### #15 — Show and Tell: TeamBuilder Concepts, Mockups, and Integrations

| | |
|---|---|
| **Labels** | `area:community`, `area:frontend`, `help wanted` |
| **Removed** | `enhancement` |

---

## Updating Discussion Labels

### Automated (GitHub CLI)

Use the script at `scripts/update-discussion-labels.ps1` to re-apply the standardized
label set to all discussions. The script uses only GitHub CLI and the GraphQL API.
No secrets beyond a standard `gh auth login` session are required.

```powershell
.\scripts\update-discussion-labels.ps1
```

### Manual (GitHub UI)

1. Open the TeamBuilder repository on GitHub.
2. Go to **Discussions**.
3. Open the target discussion.
4. In the right sidebar, find **Labels**.
5. Click the gear icon next to Labels.
6. Add the recommended labels listed above.
7. Remove any labels listed under **Removed**.
8. Click away to save.

---

## Standardized Label Reference

Only the following labels are permitted on discussions. Do not create or use any other labels.

### Type Labels
`bug` `enhancement` `documentation` `question` `duplicate` `invalid` `wontfix`

### Contributor Labels
`good first issue` `help wanted`

### Area Labels
`area:api` `area:application` `area:architecture` `area:community` `area:database`
`area:devops` `area:docs` `area:domain` `area:frontend` `area:github-actions`
`area:infrastructure` `area:product` `area:roster-language` `area:tests`

### Planning Labels
`roadmap`
4 changes: 2 additions & 2 deletions docs/github-labels.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ The `.github/labeler.yml` file defines rules that automatically apply area label
| area:application | `src/TeamBuilder.Application/**` |
| area:domain | `src/TeamBuilder.Domain/**` |
| area:infrastructure | `src/TeamBuilder.Infrastructure/**` |
| area:database | `src/TeamBuilder.Infrastructure/**/Migrations/**`<br>`src/TeamBuilder.Infrastructure/**/Persistence/**` |
| area:database | `src/TeamBuilder.Infrastructure/**/Migrations/**`<br>`src/TeamBuilder.Infrastructure/**/Persistence/**`<br>`src/TeamBuilder.Infrastructure/**/Data/**`<br>`src/TeamBuilder.Infrastructure/**/*Configuration.cs` |
| area:tests | `tests/**` |
| area:docs | `docs/**`, `README.md`, `SECURITY.md`, `CONTRIBUTING.md`, `.github/copilot-instructions.md` |
| area:github-actions | `.github/workflows/**`, `.github/dependabot.yml`, `.github/labeler.yml` |
Expand Down Expand Up @@ -107,7 +107,7 @@ The script:
- Creates missing labels with standardized descriptions and colors
- Updates existing label descriptions and colors
- Is idempotent and safe to rerun
- Uses `gh label view` for reliable existence detection (handles names with `:` correctly)
- Uses `gh label list --json name` for reliable existence detection (handles names with `:` correctly)
- Requires GitHub CLI (`gh`) to be installed and authenticated

### Prerequisites
Expand Down
171 changes: 171 additions & 0 deletions scripts/update-discussion-labels.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Apply standardized labels to all TeamBuilder GitHub Discussions.

.DESCRIPTION
Uses the GitHub GraphQL API via the GitHub CLI to apply the confirmed standardized
label set to every TeamBuilder discussion. Existing labels not in the target set
are removed. The script is idempotent and safe to rerun.

No secrets are required beyond a standard authenticated GitHub CLI session.

.PARAMETER Owner
The GitHub organization or user that owns the repository. Defaults to 'RocketDelivery2'.

.PARAMETER Repo
The GitHub repository name. Defaults to 'TeamBuilder'.

.EXAMPLE
.\scripts\update-discussion-labels.ps1

.EXAMPLE
.\scripts\update-discussion-labels.ps1 -Owner 'myorg' -Repo 'myrepo'

.NOTES
Requires GitHub CLI: https://cli.github.com/
Authenticate first with: gh auth login
#>

param(
[string]$Owner = 'RocketDelivery2',
[string]$Repo = 'TeamBuilder'
)

$ErrorActionPreference = 'Stop'

if (-not (Get-Command gh -ErrorAction SilentlyContinue)) {
Write-Error 'GitHub CLI (gh) is not installed. See https://cli.github.com/'
exit 1
}

# ---------------------------------------------------------------------------
# Step 1 — Fetch all label node IDs from the repository
# ---------------------------------------------------------------------------
$labelQuery = 'query($owner:String!, $repo:String!) { repository(owner:$owner, name:$repo) { labels(first:100) { nodes { id name } } } }'
$labelData = gh api graphql -F owner="$Owner" -F repo="$Repo" -f query="$labelQuery" | ConvertFrom-Json

$L = @{}
foreach ($node in $labelData.data.repository.labels.nodes) {
$L[$node.name] = $node.id
}

Write-Host "Loaded $($L.Count) labels from $Owner/$Repo" -ForegroundColor Cyan

# ---------------------------------------------------------------------------
# Step 2 — Fetch all discussion IDs and their current labels
# ---------------------------------------------------------------------------
$discussionQuery = 'query($owner:String!, $repo:String!) { repository(owner:$owner, name:$repo) { discussions(first:50) { nodes { id number title labels(first:20) { nodes { id name } } } } } }'
$discussionData = gh api graphql -F owner="$Owner" -F repo="$Repo" -f query="$discussionQuery" | ConvertFrom-Json
$discussions = $discussionData.data.repository.discussions.nodes

# ---------------------------------------------------------------------------
# Step 3 — Define the target label set per discussion title keyword
#
# Keys are matched against the discussion title (case-insensitive substring).
# The FIRST matching rule wins. Use specific keywords to avoid false matches.
# ---------------------------------------------------------------------------
$rules = @(
[pscustomobject]@{
match = 'Welcome to TeamBuilder'
labels = @('area:community','help wanted')
}
[pscustomobject]@{
match = 'Vision, Mission, and Roadmap'
labels = @('area:product','roadmap','help wanted')
}
[pscustomobject]@{
match = 'How to Contribute'
labels = @('area:community','area:docs','documentation','help wanted','good first issue')
}
[pscustomobject]@{
match = 'Architecture: API-First'
labels = @('area:architecture','area:api','area:database','help wanted')
}
[pscustomobject]@{
match = 'Roster Language Proposal'
labels = @('area:roster-language','area:product','enhancement','help wanted','question')
}
[pscustomobject]@{
match = 'Product Use Cases'
labels = @('area:product','area:community','question')
}
[pscustomobject]@{
match = 'Team Refill'
labels = @('area:product','enhancement','question')
}
[pscustomobject]@{
match = 'Frontend Clients'
labels = @('area:frontend','area:api','enhancement','help wanted')
}
[pscustomobject]@{
match = 'DevOps, Environments'
labels = @('area:devops','area:database','area:github-actions','help wanted')
}
[pscustomobject]@{
match = 'Ask TeamBuilder Questions'
labels = @('question','area:community')
}
[pscustomobject]@{
match = 'Show and Tell'
labels = @('area:community','area:frontend','help wanted')
}
)

# ---------------------------------------------------------------------------
# Step 4 — GraphQL mutations (single-label calls to avoid array issues)
# ---------------------------------------------------------------------------
$addQ = 'mutation($lid:ID!, $labelId:ID!) { addLabelsToLabelable(input:{labelableId:$lid, labelIds:[$labelId]}) { clientMutationId } }'
$removeQ = 'mutation($lid:ID!, $labelId:ID!) { removeLabelsFromLabelable(input:{labelableId:$lid, labelIds:[$labelId]}) { clientMutationId } }'

function Invoke-AddLabel($discussionId, $labelName) {
$labelId = $L[$labelName]
if (-not $labelId) { Write-Host " SKIP (unknown label): $labelName" -ForegroundColor DarkYellow; return }
$out = gh api graphql -F lid="$discussionId" -F labelId="$labelId" -f query="$addQ" 2>&1
if ($LASTEXITCODE -ne 0) { Write-Host " ERR add '$labelName': $out" -ForegroundColor Red }
else { Write-Host " + $labelName" -ForegroundColor Green }
}

function Invoke-RemoveLabel($discussionId, $labelName) {
$labelId = $L[$labelName]
if (-not $labelId) { return }
$out = gh api graphql -F lid="$discussionId" -F labelId="$labelId" -f query="$removeQ" 2>&1
if ($LASTEXITCODE -ne 0) { Write-Host " ERR remove '$labelName': $out" -ForegroundColor DarkYellow }
else { Write-Host " - $labelName" -ForegroundColor Yellow }
}

# ---------------------------------------------------------------------------
# Step 5 — Apply diffs
# ---------------------------------------------------------------------------
$successCount = 0
$skipCount = 0

foreach ($d in ($discussions | Sort-Object number)) {
$rule = $rules | Where-Object { $d.title -match [regex]::Escape($_.match) } | Select-Object -First 1

if (-not $rule) {
Write-Host "#$($d.number): $($d.title) — no matching rule, skipped" -ForegroundColor DarkYellow
$skipCount++
continue
}

Write-Host "`n#$($d.number): $($d.title)" -ForegroundColor Cyan

$currentNames = $d.labels.nodes | ForEach-Object { $_.name }
$targetNames = $rule.labels

$toAdd = $targetNames | Where-Object { $_ -notin $currentNames }
$toRemove = $currentNames | Where-Object { $_ -notin $targetNames }

if ($toAdd.Count -eq 0 -and $toRemove.Count -eq 0) {
Write-Host ' (already correct)' -ForegroundColor DarkGray
} else {
foreach ($lbl in $toAdd) { Invoke-AddLabel $d.id $lbl }
foreach ($lbl in $toRemove) { Invoke-RemoveLabel $d.id $lbl }
}

$successCount++
}

Write-Host ''
Write-Host "Done. Processed: $successCount Skipped (no rule): $skipCount" -ForegroundColor Cyan
Loading