Functions/Get-RepoInfo.ps1
|
function Get-RepoInfo { <# .SYNOPSIS Consulta metadata y estado de deploy de repositorios en la organización. .DESCRIPTION Para repos CON metadata normalizada (type:X|stack:Y|...) parsea la descripción. Para repos SIN metadata, auto-detecta stack y sugiere descripción. Comprueba deploy.yaml + workflow y determina DeployStatus (auto/partial/none). .PARAMETER Name Nombre de un repositorio individual. .PARAMETER List Lista todos los repositorios de la organización. .PARAMETER Filter Hashtable para filtrar resultados de -List. Keys: Type, HasMetadata, DeployStatus, Stack. .PARAMETER Org Organización GitHub. Default: cacsi-dev. .PARAMETER Json Retorna el resultado como JSON string. .EXAMPLE Get-RepoInfo -Name gabinete_ui #> [CmdletBinding(DefaultParameterSetName = 'Single')] param( [Parameter(Mandatory, ParameterSetName = 'Single', Position = 0)] [string]$Name, [Parameter(Mandatory, ParameterSetName = 'List')] [switch]$List, [Parameter(ParameterSetName = 'List')] [hashtable]$Filter, [Parameter(ParameterSetName = 'List')] [int]$First = 0, [string]$Org = 'cacsi-dev', [switch]$Json ) if ($PSCmdlet.ParameterSetName -eq 'List') { $repoNames = & gh api "orgs/$Org/repos" --paginate -q '.[].name' 2>&1 if ($LASTEXITCODE -ne 0) { Write-Error "Failed to list repos: $repoNames" return } # gh --paginate can return one name per line $repoNames = @($repoNames) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } if ($First -gt 0) { $repoNames = $repoNames | Select-Object -First $First } $results = foreach ($repoName in $repoNames) { Get-SingleRepoInfo -RepoName $repoName -Org $Org } $results = @($results | Where-Object { $_ }) # Apply filter if ($Filter) { if ($Filter.ContainsKey('Type')) { $results = $results | Where-Object { $_.Type -eq $Filter.Type } } if ($Filter.ContainsKey('HasMetadata')) { $results = $results | Where-Object { $_.HasMetadata -eq $Filter.HasMetadata } } if ($Filter.ContainsKey('DeployStatus')) { $results = $results | Where-Object { $_.DeployStatus -eq $Filter.DeployStatus } } if ($Filter.ContainsKey('Stack')) { $results = $results | Where-Object { $_.Stack -eq $Filter.Stack } } } if ($Json) { return $results | ConvertTo-Json -Depth 5 } return $results } # Single repo $result = Get-SingleRepoInfo -RepoName $Name -Org $Org if ($Json -and $result) { return $result | ConvertTo-Json -Depth 5 } return $result } function Get-SingleRepoInfo { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$RepoName, [string]$Org = 'cacsi-dev' ) # Get default branch $defaultBranch = & gh api "repos/$Org/$RepoName" --jq '.default_branch' 2>&1 if ($LASTEXITCODE -ne 0) { Write-Error "Repo not found: $Org/$RepoName — $defaultBranch" return $null } # Get repo metadata (description, language, topics, archived, pushed_at) $meta = & gh api "repos/$Org/$RepoName" --jq '.description,.language,.topics,.archived,.pushed_at' 2>&1 if ($LASTEXITCODE -ne 0) { Write-Error "Failed to get repo metadata: $meta" return $null } $metaLines = @($meta) $description = $metaLines[0] $language = $metaLines[1] $topicsRaw = $metaLines[2] $archivedRaw = $metaLines[3] $pushedAtRaw = $metaLines[4] $topics = if ($topicsRaw) { @($topicsRaw -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ } } else { @() } $isArchived = $archivedRaw -eq 'true' $lastPush = if ($pushedAtRaw) { [datetime]::Parse($pushedAtRaw) } else { $null } # Parse metadata if present $hasMetadata = Test-RepoDescription -Description $description $parsed = $null $type = $null $stack = $null $repoType = $null if ($hasMetadata) { $parsed = ConvertFrom-RepoDescription -Description $description $type = $parsed.type $stack = $parsed.stack $repoType = $parsed.model } # Auto-detect stack from repo files $autoStack = $null $suggestedType = $null $treeOutput = & gh api "repos/$Org/$RepoName/git/trees/$defaultBranch" 2>&1 if ($LASTEXITCODE -eq 0) { $tree = ($treeOutput -join '') | ConvertFrom-Json -ErrorAction SilentlyContinue if ($tree -and $tree.tree) { $resolved = Resolve-RepoStack -Files $tree.tree $autoStack = $resolved.Stack $suggestedType = $resolved.SuggestedType } } # Suggested description for repos without metadata $suggestedDescription = $null if (-not $hasMetadata -and $suggestedType) { $sStack = if ($autoStack) { $autoStack } else { 'generic' } $suggestedDescription = "type:$suggestedType|stack:$sStack|deploy:none|model:legacy" } # Deploy status $deploy = Get-RepoDeployStatus -RepoName $RepoName -Org $Org # Template freshness $templateIsCurrent = $null if ($deploy.TemplateHash -and $deploy.TemplateVersion) { # Determine canonical template based on type $tplType = if ($type) { $type } elseif ($suggestedType) { $suggestedType } else { $null } $canonicalHash = Get-CanonicalTemplateHash -Type $tplType if ($canonicalHash) { $templateIsCurrent = ($deploy.TemplateHash -eq $canonicalHash) } else { $templateIsCurrent = $false } } [PSCustomObject]@{ Name = $RepoName URL = "https://github.com/$Org/$RepoName" Description = $description HasMetadata = $hasMetadata Type = $type Stack = $stack RepoType = $repoType GitHubLanguage = $language GitHubTopics = $topics IsArchived = $isArchived LastPush = $lastPush AutoDetectedStack = $autoStack SuggestedType = $suggestedType SuggestedDescription = $suggestedDescription DeployStatus = $deploy.DeployStatus DeployYAMLExists = $deploy.DeployYAMLExists WorkflowExists = $deploy.WorkflowExists TemplateVersion = $deploy.TemplateVersion TemplateIsCurrent = $templateIsCurrent Notes = $null } } function Get-CanonicalTemplateHash { <# .SYNOPSIS Obtiene el hash SHA1 del template canónico según el tipo de repo. #> [CmdletBinding()] param( [string]$Type ) $templateMap = @{ 'flutter-web' = 'deploy.web.yml' 'node-api' = 'deploy.api.yml' 'sqlserver-db' = 'deploy.db.yml' } $templateFile = $templateMap[$Type] if (-not $templateFile) { return $null } # Look for canonical templates in the .github repo templates dir $searchPaths = @( (Join-Path $PSScriptRoot "..\..\..\..\..\.github\templates\workflows\$templateFile") (Join-Path $env:USERPROFILE "Code\cacsi-dev\.github\templates\workflows\$templateFile") ) foreach ($path in $searchPaths) { $resolved = Resolve-Path $path -ErrorAction SilentlyContinue if ($resolved -and (Test-Path $resolved)) { $content = Get-Content $resolved -Raw -Encoding UTF8 return Get-TemplateHash -Content $content } } return $null } |