Agrega gitea_skill: skill para operar Gitea self-hosted (scripts API BOM-safe + push headless sin persistir token, SKILL/TOOLS/api-index). Actualiza CLAUDE.md, README.md, .env.example.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Low-level Gitea REST API wrapper (curl-based, BOM-safe).
|
||||
|
||||
.DESCRIPTION
|
||||
Mirrors Invoke-GitHubApi.ps1's design: uses curl.exe so JSON bodies are
|
||||
written via --data-binary @file (no PowerShell Set-Content BOM, which
|
||||
Gitea rejects with "invalid character '\u00ef' looking for beginning of
|
||||
value"). Reads GITEA_URL + GITEA_TOKEN from the environment.
|
||||
|
||||
.PARAMETER Method
|
||||
HTTP verb. Default GET.
|
||||
|
||||
.PARAMETER Path
|
||||
Path under /api/v1 (with or without leading slash). Query params allowed.
|
||||
|
||||
.PARAMETER BodyJson
|
||||
JSON body for write verbs. Validated as JSON before sending.
|
||||
|
||||
.PARAMETER Raw
|
||||
Return a pscustomobject with Status, Headers, Body instead of parsed JSON.
|
||||
|
||||
.EXAMPLE
|
||||
.\Invoke-GiteaApi.ps1 -Path "/version"
|
||||
.\Invoke-GiteaApi.ps1 -Path "/user"
|
||||
.\Invoke-GiteaApi.ps1 -Method POST -Path "/user/repos" -BodyJson $json
|
||||
#>
|
||||
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:GITEA_TOKEN)) {
|
||||
throw "Set GITEA_TOKEN before calling the Gitea API. Store it in .env.local.ps1 (gitignored)."
|
||||
}
|
||||
if (-not (Get-Command curl.exe -ErrorAction SilentlyContinue)) {
|
||||
throw "curl.exe not found in PATH."
|
||||
}
|
||||
|
||||
$base = if ($env:GITEA_URL) { $env:GITEA_URL.TrimEnd("/") } else { "https://gitea-hjwh0svsoo9p5w5kj2j6b1bd.urieljareth.org" }
|
||||
$base = "$base/api/v1"
|
||||
$cleanPath = $Path.TrimStart("/")
|
||||
$uri = "$base/$cleanPath"
|
||||
|
||||
# Gitea accepts "token <T>" (canonical) and "Bearer <T>" (since 1.16+).
|
||||
$curlArgs = @(
|
||||
"-sS", "-i",
|
||||
"-X", $Method.ToUpper(),
|
||||
"-H", "Authorization: token $env:GITEA_TOKEN",
|
||||
"-H", "Accept: application/json",
|
||||
"-H", "User-Agent: gitea-skill"
|
||||
)
|
||||
$tmpBody = $null
|
||||
if ($PSBoundParameters.ContainsKey("BodyJson")) {
|
||||
try { $null = $BodyJson | ConvertFrom-Json } catch { throw "BodyJson is not valid JSON: $($_.Exception.Message)" }
|
||||
$tmpBody = [System.IO.Path]::GetTempFileName()
|
||||
[System.IO.File]::WriteAllText($tmpBody, $BodyJson, (New-Object System.Text.UTF8Encoding($false)))
|
||||
$curlArgs += @("--data-binary", "@$tmpBody", "-H", "Content-Type: application/json")
|
||||
}
|
||||
|
||||
try {
|
||||
$output = & curl.exe @curlArgs $uri
|
||||
$exitCode = $LASTEXITCODE
|
||||
if ($exitCode -ne 0) { throw "Gitea 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 500) { $snippet = $snippet.Substring(0, 500) + "..." }
|
||||
throw "Gitea API HTTP error: $statusLine`nURI: $uri`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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user