Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)
Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
187 lines
8.0 KiB
PowerShell
187 lines
8.0 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Exportiert Jira-Daten fuer pathOS (O2CBS Projekt)
|
|
.DESCRIPTION
|
|
Zieht Boards, Sprints, Backlog-Items und Team-Zuordnungen.
|
|
Kompatibel mit PowerShell 5.1.
|
|
.USAGE
|
|
powershell -ExecutionPolicy Bypass -File ".\project-audit\scripts\jira-export.ps1"
|
|
#>
|
|
|
|
$JiraUrl = "https://arija.jaas.service.deutschebahn.com"
|
|
$ProjectKey = "O2CBS"
|
|
$ApiBase = "$JiraUrl/rest"
|
|
$OutputDir = "project-audit\data\jira-export"
|
|
|
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
|
|
Write-Host "============================================================"
|
|
Write-Host " pathOS Jira Export - Projekt $ProjectKey"
|
|
Write-Host "============================================================"
|
|
Write-Host ""
|
|
|
|
$Token = Read-Host "Jira PAT (Personal Access Token)"
|
|
$Headers = @{ "Authorization" = "Bearer $Token"; "Content-Type" = "application/json" }
|
|
|
|
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
|
|
|
|
function Invoke-JiraApi {
|
|
param([string]$Uri)
|
|
try {
|
|
$Resp = Invoke-WebRequest -Uri $Uri -Headers $Headers -UseBasicParsing -ErrorAction Stop
|
|
return ($Resp.Content | ConvertFrom-Json)
|
|
} catch {
|
|
Write-Host " FEHLER: $($_.Exception.Message)" -ForegroundColor Red
|
|
return $null
|
|
}
|
|
}
|
|
|
|
# 1. Projekt-Info
|
|
Write-Host ">> Projekt-Info..." -ForegroundColor Yellow
|
|
$Project = Invoke-JiraApi "$ApiBase/api/2/project/$ProjectKey"
|
|
if ($null -eq $Project) { Write-Host "Verbindung fehlgeschlagen. Token pruefen." -ForegroundColor Red; exit 1 }
|
|
Write-Host " Projekt: $($Project.name) ($($Project.key))" -ForegroundColor Green
|
|
|
|
# 2. Boards finden
|
|
Write-Host ">> Boards suchen..." -ForegroundColor Yellow
|
|
$Boards = Invoke-JiraApi "$ApiBase/agile/1.0/board?projectKeyOrId=$ProjectKey&maxResults=50"
|
|
if ($Boards -and $Boards.values) {
|
|
Write-Host " $($Boards.values.Count) Boards gefunden" -ForegroundColor Green
|
|
$Boards.values | ForEach-Object { Write-Host " - $($_.name) (ID: $($_.id), Typ: $($_.type))" }
|
|
$Boards | ConvertTo-Json -Depth 5 | Out-File "$OutputDir\boards.json" -Encoding UTF8
|
|
}
|
|
|
|
# 3. Aktuelle Sprints pro Board
|
|
Write-Host ">> Sprints laden..." -ForegroundColor Yellow
|
|
$AllSprints = @()
|
|
if ($Boards -and $Boards.values) {
|
|
foreach ($Board in $Boards.values) {
|
|
$Sprints = Invoke-JiraApi "$ApiBase/agile/1.0/board/$($Board.id)/sprint?state=active,closed&maxResults=10"
|
|
if ($Sprints -and $Sprints.values) {
|
|
foreach ($S in $Sprints.values) {
|
|
$S | Add-Member -NotePropertyName "boardName" -NotePropertyValue $Board.name -Force
|
|
$S | Add-Member -NotePropertyName "boardId" -NotePropertyValue $Board.id -Force
|
|
$AllSprints += $S
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Write-Host " $($AllSprints.Count) Sprints gefunden" -ForegroundColor Green
|
|
$AllSprints | ConvertTo-Json -Depth 5 | Out-File "$OutputDir\sprints.json" -Encoding UTF8
|
|
|
|
# 4. Backlog-Items mit Team-Labels (JQL)
|
|
Write-Host ">> Backlog-Items laden (letzte 2 PIs)..." -ForegroundColor Yellow
|
|
$Jql = "project = $ProjectKey AND updated >= -90d ORDER BY updated DESC"
|
|
$StartAt = 0
|
|
$AllIssues = @()
|
|
do {
|
|
$SearchUri = "$ApiBase/api/2/search?jql=$([Uri]::EscapeDataString($Jql))&startAt=$StartAt&maxResults=100&fields=summary,status,issuetype,assignee,labels,components,fixVersions,priority,created,updated,customfield_10100"
|
|
$Result = Invoke-JiraApi $SearchUri
|
|
if ($null -eq $Result) { break }
|
|
$AllIssues += $Result.issues
|
|
$StartAt += 100
|
|
Write-Host " $($AllIssues.Count) / $($Result.total) Issues geladen..."
|
|
} while ($AllIssues.Count -lt $Result.total -and $AllIssues.Count -lt 1000)
|
|
|
|
Write-Host " $($AllIssues.Count) Issues geladen" -ForegroundColor Green
|
|
$AllIssues | ConvertTo-Json -Depth 10 | Out-File "$OutputDir\issues.json" -Encoding UTF8
|
|
|
|
# 5. Komponenten (= Teams?)
|
|
Write-Host ">> Komponenten laden..." -ForegroundColor Yellow
|
|
$Components = Invoke-JiraApi "$ApiBase/api/2/project/$ProjectKey/components"
|
|
if ($Components) {
|
|
Write-Host " $($Components.Count) Komponenten" -ForegroundColor Green
|
|
$Components | ForEach-Object { Write-Host " - $($_.name) (Lead: $(if ($_.lead) { $_.lead.displayName } else { '-' }))" }
|
|
$Components | ConvertTo-Json -Depth 5 | Out-File "$OutputDir\components.json" -Encoding UTF8
|
|
}
|
|
|
|
# 6. Versionen (Releases)
|
|
Write-Host ">> Versionen laden..." -ForegroundColor Yellow
|
|
$Versions = Invoke-JiraApi "$ApiBase/api/2/project/$ProjectKey/versions"
|
|
if ($Versions) {
|
|
$RecentVersions = $Versions | Where-Object { $_.released -eq $true -or $_.archived -eq $false } | Sort-Object -Property releaseDate -Descending | Select-Object -First 20
|
|
Write-Host " $($Versions.Count) Versionen ($($RecentVersions.Count) aktuelle)" -ForegroundColor Green
|
|
$Versions | ConvertTo-Json -Depth 5 | Out-File "$OutputDir\versions.json" -Encoding UTF8
|
|
}
|
|
|
|
# 7. Analyse: Issues nach Team/Komponente
|
|
Write-Host ""
|
|
Write-Host ">> Analyse..." -ForegroundColor Yellow
|
|
|
|
$ByType = $AllIssues | Group-Object { $_.fields.issuetype.name }
|
|
$ByStatus = $AllIssues | Group-Object { $_.fields.status.name }
|
|
$ByComponent = @{}
|
|
foreach ($Issue in $AllIssues) {
|
|
if ($Issue.fields.components) {
|
|
foreach ($Comp in $Issue.fields.components) {
|
|
$ByComponent[$Comp.name] = ($ByComponent[$Comp.name]) + 1
|
|
}
|
|
}
|
|
}
|
|
$ByLabel = @{}
|
|
foreach ($Issue in $AllIssues) {
|
|
if ($Issue.fields.labels) {
|
|
foreach ($Label in $Issue.fields.labels) {
|
|
$ByLabel[$Label] = ($ByLabel[$Label]) + 1
|
|
}
|
|
}
|
|
}
|
|
|
|
# Ergebnis-Markdown
|
|
$Md = @()
|
|
$Md += "# Jira-Analyse pathOS ($ProjectKey)"
|
|
$Md += ""
|
|
$Md += "> Exportiert: $(Get-Date -Format 'yyyy-MM-dd HH:mm')"
|
|
$Md += "> Issues (letzte 90 Tage): $($AllIssues.Count)"
|
|
$Md += ""
|
|
$Md += "## Issues nach Typ"
|
|
foreach ($G in ($ByType | Sort-Object Count -Descending)) { $Md += "- $($G.Name): $($G.Count)" }
|
|
$Md += ""
|
|
$Md += "## Issues nach Status"
|
|
foreach ($G in ($ByStatus | Sort-Object Count -Descending)) { $Md += "- $($G.Name): $($G.Count)" }
|
|
$Md += ""
|
|
$Md += "## Issues nach Komponente (Team)"
|
|
foreach ($E in ($ByComponent.GetEnumerator() | Sort-Object Value -Descending)) { $Md += "- $($E.Key): $($E.Value)" }
|
|
$Md += ""
|
|
$Md += "## Issues nach Label"
|
|
foreach ($E in ($ByLabel.GetEnumerator() | Sort-Object Value -Descending | Select-Object -First 30)) { $Md += "- $($E.Key): $($E.Value)" }
|
|
$Md += ""
|
|
$Md += "## Boards"
|
|
if ($Boards -and $Boards.values) { foreach ($B in $Boards.values) { $Md += "- $($B.name) (ID: $($B.id), Typ: $($B.type))" } }
|
|
$Md += ""
|
|
$Md += "## Aktuelle Sprints"
|
|
foreach ($S in ($AllSprints | Sort-Object startDate -Descending | Select-Object -First 10)) {
|
|
$Md += "- $($S.name) | Board: $($S.boardName) | Status: $($S.state) | $($S.startDate) - $($S.endDate)"
|
|
}
|
|
$Md += ""
|
|
$Md += "## Letzte Releases"
|
|
if ($RecentVersions) { foreach ($V in $RecentVersions) { $Md += "- $($V.name) | Released: $($V.released) | Datum: $($V.releaseDate)" } }
|
|
|
|
$Md -join "`n" | Out-File "$OutputDir\jira-analyse.md" -Encoding UTF8
|
|
|
|
# CSV fuer Issues
|
|
$CsvData = $AllIssues | ForEach-Object {
|
|
$Comps = if ($_.fields.components) { ($_.fields.components | ForEach-Object { $_.name }) -join ", " } else { "" }
|
|
$Labels = if ($_.fields.labels) { $_.fields.labels -join ", " } else { "" }
|
|
$Fix = if ($_.fields.fixVersions) { ($_.fields.fixVersions | ForEach-Object { $_.name }) -join ", " } else { "" }
|
|
[PSCustomObject]@{
|
|
Key = $_.key
|
|
Typ = $_.fields.issuetype.name
|
|
Status = $_.fields.status.name
|
|
Prioritaet = if ($_.fields.priority) { $_.fields.priority.name } else { "" }
|
|
Zusammenfassung = $_.fields.summary
|
|
Komponenten = $Comps
|
|
Labels = $Labels
|
|
FixVersion = $Fix
|
|
Assignee = if ($_.fields.assignee) { $_.fields.assignee.displayName } else { "" }
|
|
Erstellt = $_.fields.created
|
|
Aktualisiert = $_.fields.updated
|
|
}
|
|
}
|
|
$CsvData | Export-Csv "$OutputDir\issues.csv" -Delimiter ";" -Encoding UTF8 -NoTypeInformation
|
|
|
|
Write-Host ""
|
|
Write-Host "============================================================" -ForegroundColor Green
|
|
Write-Host " FERTIG! Ergebnisse in $OutputDir" -ForegroundColor Green
|
|
Write-Host "============================================================" -ForegroundColor Green
|