param( [ValidateSet("GET", "POST", "PATCH", "PUT", "DELETE")] [string]$Method = "GET", [Parameter(Mandatory = $true)] [string]$Path, [string]$BodyJson, [switch]$Raw ) $ErrorActionPreference = "Stop" if ([string]::IsNullOrWhiteSpace($env:GITHUB_TOKEN)) { throw "Set GITHUB_TOKEN before calling the GitHub API. Generate a PAT at https://github.com/settings/tokens (scope 'repo') and store it in .env.local.ps1." } if (-not (Get-Command curl.exe -ErrorAction SilentlyContinue)) { throw "curl.exe not found in PATH." } $base = if ($env:GITHUB_API_URL) { $env:GITHUB_API_URL.TrimEnd("/") } else { "https://api.github.com" } $cleanPath = $Path.TrimStart("/") $uri = "$base/$cleanPath" $tmpBody = $null $args = @("-sS", "-i", "-X", $Method.ToUpper(), "-H", "Authorization: Bearer $env:GITHUB_TOKEN", "-H", "Accept: application/vnd.github+json", "-H", "X-GitHub-Api-Version: 2022-11-28", "-H", "User-Agent: coolify-deploy-skill") if ($PSBoundParameters.ContainsKey("BodyJson")) { try { $null = $BodyJson | ConvertFrom-Json } catch { throw "BodyJson is not valid JSON: $($_.Exception.Message)" } $tmpBody = [System.IO.Path]::GetTempFileName() Set-Content -LiteralPath $tmpBody -Value $BodyJson -NoNewline -Encoding utf8 $args += @("--data", "@$tmpBody", "-H", "Content-Type: application/json") } try { $output = & curl.exe @args $uri $exitCode = $LASTEXITCODE if ($exitCode -ne 0) { throw "GitHub API request failed with exit code $exitCode." } $rawStr = ($output | Out-String) $parts = $rawStr -split "(?:`r`n`r`n|`n`n)", 2 $headersBlock = if ($parts.Count -ge 1) { $parts[0] } else { "" } $bodyBlock = if ($parts.Count -ge 2) { $parts[1] } else { "{}" } $statusLine = ($headersBlock -split "`n" | Select-Object -First 1).Trim() if ($statusLine -notmatch " 20[04-9] ") { $snippet = $bodyBlock.Trim() if ($snippet.Length -gt 400) { $snippet = $snippet.Substring(0, 400) + "..." } throw "GitHub API HTTP error: $statusLine`nBody: $snippet" } if ($Raw) { [pscustomobject]@{ Status = $statusLine; Headers = $headersBlock; Body = $bodyBlock } } else { try { $bodyBlock | ConvertFrom-Json } catch { $bodyBlock } } } finally { if ($tmpBody -and (Test-Path -LiteralPath $tmpBody)) { Remove-Item -LiteralPath $tmpBody -Force -ErrorAction SilentlyContinue } }