Files
memegoat/.utlis/gitea_issues.ps1

304 lines
11 KiB
PowerShell

<#
Gitea Issues Utility (PowerShell)
Prérequis:
- PowerShell 5+ (ou 7+)
- Fichier .env à la racine du dépôt avec:
GITEA_BASE_URL=https://git.example.com/api/v1
GITEA_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
GITEA_DEFAULT_OWNER=owner
GITEA_DEFAULT_REPO=repo
Utilisation rapide:
.\gitea_issues.ps1 create -Title "Titre" \
[-Body "CorpsMarkdown" | -BodyFromStdin | -BodyFile chemin\\fichier.md | -BodyBase64 "..." ] \
-Labels "bug,help wanted" -Assignees "user1,user2" [-Owner o] [-Repo r]
.\gitea_issues.ps1 list [-State open|closed|all] [-Page 1] [-Limit 30] [-Owner o] [-Repo r]
.\gitea_issues.ps1 get -Index 12 [-Owner o] [-Repo r]
.\gitea_issues.ps1 update -Index 12 [-Title x] \
[-Body y | -BodyFromStdin | -BodyFile chemin\\fichier.md | -BodyBase64 "..."] \
[-Labels csv] [-Assignees csv] [-State open|closed] [-Owner o] [-Repo r]
.\gitea_issues.ps1 close -Index 12 [-Owner o] [-Repo r]
.\gitea_issues.ps1 open -Index 12 [-Owner o] [-Repo r]
.\gitea_issues.ps1 comment -Index 12 \
[-Message "TexteMarkdown" | -MessageFromStdin | -MessageFile chemin\\commentaire.md | -MessageBase64 "..."] \
[-Owner o] [-Repo r]
Priorité des sources de contenu (inline prioritaire):
Body: -Body > -BodyFromStdin > -BodyFile > -BodyBase64
Message: -Message > -MessageFromStdin > -MessageFile > -MessageBase64
Astuce: pour éviter les problèmes d'échappement avec les ``` en PowerShell, utilisez un here-string
ou passez le contenu via un pipe et -BodyFromStdin / -MessageFromStdin.
#>
[CmdletBinding(DefaultParameterSetName='help')]
param(
[Parameter(ParameterSetName='create')][switch]$create,
[Parameter(ParameterSetName='list')][switch]$list,
[Parameter(ParameterSetName='get')][switch]$get,
[Parameter(ParameterSetName='update')][switch]$update,
[Parameter(ParameterSetName='close')][switch]$close,
[Parameter(ParameterSetName='open')][switch]$open,
[Parameter(ParameterSetName='comment')][switch]$comment,
# communs
[string]$Owner,
[string]$Repo,
# create/update
[string]$Title,
[string]$Body,
[switch]$BodyFromStdin,
[string]$BodyFile,
[string]$BodyBase64,
[string]$Labels,
[string]$Assignees,
[ValidateSet('open','closed')][string]$State,
# list
[ValidateSet('open','closed','all')][string]$StateList='open',
[int]$Page=1,
[int]$Limit=30,
# get/update/close/open/comment
[int]$Index,
# comment
[string]$Message,
[switch]$MessageFromStdin,
[string]$MessageFile,
[string]$MessageBase64
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
# Capture éventuellement l'entrée du pipeline vers le script
# (permet d'utiliser: Get-Content -Raw file | .\gitea_issues.ps1 ... -BodyFromStdin)
try {
$script:__pipedInput = $null
# $input est un énumérateur; si le script reçoit un pipe, on le matérialise en texte
$script:__pipedInput = ($input | Out-String)
if ([string]::IsNullOrWhiteSpace($script:__pipedInput)) { $script:__pipedInput = $null }
} catch { $script:__pipedInput = $null }
function Load-Env {
# Détection robuste du dossier du script (compatibilité pwsh/host)
$scriptDir = $null
if ($PSScriptRoot) {
$scriptDir = $PSScriptRoot
} elseif ($PSCommandPath) {
$scriptDir = Split-Path -Parent $PSCommandPath
} elseif ($MyInvocation -and $MyInvocation.MyCommand -and $MyInvocation.MyCommand.Path) {
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
} else {
$scriptDir = (Get-Location).Path
}
$repoRoot = Resolve-Path (Join-Path $scriptDir '..')
$envFile = Join-Path $repoRoot '.env'
if (Test-Path $envFile) {
Get-Content $envFile | ForEach-Object {
if ($_ -match '^[A-Za-z_][A-Za-z0-9_]*=') {
$kv = $_ -replace '\r',''
$name,$value = $kv.Split('=',2)
if ($null -ne $value) {
$value = $value.Trim()
if ($value.StartsWith('"') -and $value.EndsWith('"') -and $value.Length -ge 2) {
$value = $value.Substring(1, $value.Length - 2)
}
Set-Item -Path ("Env:{0}" -f $name) -Value $value
}
}
}
}
}
function Ensure-UTF8 {
try {
# Forcer l'encodage console en UTF-8 (sans BOM)
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[Console]::OutputEncoding = $utf8NoBom
[Console]::InputEncoding = $utf8NoBom
$script:OutputEncoding = $utf8NoBom
$global:OutputEncoding = $utf8NoBom
# Sur Windows, fixer la page de code à 65001 si possible (silencieux)
if ($IsWindows) { cmd /c chcp 65001 > $null 2>&1 }
} catch { }
}
function Ensure-Env {
if (-not $env:GITEA_BASE_URL) { throw 'GITEA_BASE_URL manquant dans .env' }
if (-not $env:GITEA_TOKEN) { throw 'GITEA_TOKEN manquant dans .env' }
}
function Decode-Base64Utf8 {
param([string]$b64)
if (-not $b64) { return $null }
try {
$bytes = [Convert]::FromBase64String($b64)
return [System.Text.Encoding]::UTF8.GetString($bytes)
} catch {
throw 'Chaîne Base64 invalide'
}
}
function Read-AllStdin {
try {
# 1) Si l'entrée provient du pipeline (capturée au niveau script)
if ($script:__pipedInput) { return $script:__pipedInput }
# 2) Sinon, lecture directe de STDIN (pipe vers PowerShell)
$content = $null
try { $content = [Console]::In.ReadToEnd() } catch { $content = $null }
return $content
} catch {
return $null
}
}
function Resolve-Content {
param(
[string]$Inline,
[switch]$FromStdin,
[string]$FromFile,
[string]$FromBase64,
[string]$Kind # 'Body' | 'Message'
)
# Avertissements de priorité si plusieurs sources sont présentes
$provided = @()
if ($Inline) { $provided += 'Inline' }
if ($FromStdin) { $provided += 'Stdin' }
if ($FromFile) { $provided += 'File' }
if ($FromBase64) { $provided += 'Base64' }
if ($provided.Count -gt 1) {
Write-Warning ("Plusieurs sources pour {0} détectées ({1}); la priorité est Inline > Stdin > File > Base64." -f $Kind, ($provided -join ', '))
}
if ($Inline) { return $Inline }
if ($FromStdin) {
$stdin = Read-AllStdin
if ($stdin) { return $stdin }
Write-Warning ("{0} demandé depuis stdin mais aucun contenu lu; on continue sur la source suivante." -f $Kind)
}
if ($FromFile) {
if (-not (Test-Path -Path $FromFile -PathType Leaf)) { throw "Fichier introuvable: $FromFile" }
return Get-Content -LiteralPath $FromFile -Raw -Encoding UTF8
}
if ($FromBase64) { return Decode-Base64Utf8 $FromBase64 }
return $null
}
function Resolve-Repo {
param([string]$OwnerIn,[string]$RepoIn)
$o = if ($OwnerIn) { $OwnerIn } elseif ($env:OWNER) { $env:OWNER } elseif ($env:GITEA_DEFAULT_OWNER) { $env:GITEA_DEFAULT_OWNER } else { '' }
$r = if ($RepoIn) { $RepoIn } elseif ($env:REPO) { $env:REPO } elseif ($env:GITEA_DEFAULT_REPO) { $env:GITEA_DEFAULT_REPO } else { '' }
if (-not $o -or -not $r) { throw 'OWNER/REPO non définis (utilisez .env ou paramètres -Owner/-Repo)' }
return @($o,$r)
}
function Invoke-Gitea {
param(
[ValidateSet('GET','POST','PATCH','PUT','DELETE')][string]$Method,
[string]$Path,
$BodyObj
)
$base = $env:GITEA_BASE_URL.TrimEnd('/')
$uri = "$base$Path"
$headers = @{ Authorization = "token $($env:GITEA_TOKEN)" }
if ($PSBoundParameters.ContainsKey('BodyObj') -and $BodyObj -ne $null) {
$json = $BodyObj | ConvertTo-Json -Depth 10
# Encodage explicite UTF-8 pour le corps JSON
$bytes = [System.Text.Encoding]::UTF8.GetBytes($json)
$headers['Content-Type'] = 'application/json; charset=utf-8'
return Invoke-RestMethod -Method $Method -Uri $uri -Headers $headers -Body $bytes
} else {
return Invoke-RestMethod -Method $Method -Uri $uri -Headers $headers
}
}
function Write-Help {
Get-Content -TotalCount 40 $MyInvocation.MyCommand.Path | ForEach-Object { $_ }
}
try {
Ensure-UTF8
Load-Env
Ensure-Env
if ($create) {
if (-not $Title) { throw 'Paramètre -Title requis' }
$owner,$repo = Resolve-Repo -OwnerIn $Owner -RepoIn $Repo
$Body = Resolve-Content -Inline $Body -FromStdin:$BodyFromStdin -FromFile $BodyFile -FromBase64 $BodyBase64 -Kind 'Body'
$bodyObj = @{ title = $Title }
if ($Body) { $bodyObj.body = $Body }
if ($Labels) { $bodyObj.labels = ($Labels -split ',') | Where-Object { $_ -ne '' } }
if ($Assignees) { $bodyObj.assignees = ($Assignees -split ',') | Where-Object { $_ -ne '' } }
$res = Invoke-Gitea -Method POST -Path "/repos/$owner/$repo/issues" -BodyObj $bodyObj
$res | ConvertTo-Json -Depth 10 | Write-Output
return
}
if ($list) {
$owner,$repo = Resolve-Repo -OwnerIn $Owner -RepoIn $Repo
$res = Invoke-Gitea -Method GET -Path "/repos/$owner/$repo/issues?state=$StateList&page=$Page&limit=$Limit"
$res | ForEach-Object { [PSCustomObject]@{ number = $_.number; title = $_.title; state = $_.state; labels = ($_.labels | ForEach-Object { $_.name }) -join ',' } } | Format-Table -AutoSize
return
}
if ($get) {
if (-not $Index) { throw 'Paramètre -Index requis' }
$owner,$repo = Resolve-Repo -OwnerIn $Owner -RepoIn $Repo
$res = Invoke-Gitea -Method GET -Path "/repos/$owner/$repo/issues/$Index"
$res | ConvertTo-Json -Depth 10 | Write-Output
return
}
if ($update) {
if (-not $Index) { throw 'Paramètre -Index requis' }
$owner,$repo = Resolve-Repo -OwnerIn $Owner -RepoIn $Repo
$Body = Resolve-Content -Inline $Body -FromStdin:$BodyFromStdin -FromFile $BodyFile -FromBase64 $BodyBase64 -Kind 'Body'
$bodyObj = @{}
if ($Title) { $bodyObj.title = $Title }
if ($Body) { $bodyObj.body = $Body }
if ($Labels) { $bodyObj.labels = ($Labels -split ',') | Where-Object { $_ -ne '' } }
if ($Assignees) { $bodyObj.assignees = ($Assignees -split ',') | Where-Object { $_ -ne '' } }
if ($State) { $bodyObj.state = $State }
if ($bodyObj.Keys.Count -eq 0) { throw 'Aucun champ à mettre à jour' }
$res = Invoke-Gitea -Method PATCH -Path "/repos/$owner/$repo/issues/$Index" -BodyObj $bodyObj
$res | ConvertTo-Json -Depth 10 | Write-Output
return
}
if ($close) {
if (-not $Index) { throw 'Paramètre -Index requis' }
$owner,$repo = Resolve-Repo -OwnerIn $Owner -RepoIn $Repo
$res = Invoke-Gitea -Method PATCH -Path "/repos/$owner/$repo/issues/$Index" -BodyObj @{ state = 'closed' }
$res | ConvertTo-Json -Depth 10 | Write-Output
return
}
if ($open) {
if (-not $Index) { throw 'Paramètre -Index requis' }
$owner,$repo = Resolve-Repo -OwnerIn $Owner -RepoIn $Repo
$res = Invoke-Gitea -Method PATCH -Path "/repos/$owner/$repo/issues/$Index" -BodyObj @{ state = 'open' }
$res | ConvertTo-Json -Depth 10 | Write-Output
return
}
if ($comment) {
if (-not $Index) { throw 'Paramètre -Index requis' }
$Message = Resolve-Content -Inline $Message -FromStdin:$MessageFromStdin -FromFile $MessageFile -FromBase64 $MessageBase64 -Kind 'Message'
if (-not $Message) { throw 'Paramètre -Message (ou -MessageFromStdin / -MessageFile / -MessageBase64) requis' }
$owner,$repo = Resolve-Repo -OwnerIn $Owner -RepoIn $Repo
$res = Invoke-Gitea -Method POST -Path "/repos/$owner/$repo/issues/$Index/comments" -BodyObj @{ body = $Message }
$res | ConvertTo-Json -Depth 10 | Write-Output
return
}
Write-Help
}
catch {
Write-Error $_
exit 1
}