<# .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