Add Gitea issue management utilities for Bash and PowerShell scripts
This commit is contained in:
303
.utlis/gitea_issues.ps1
Normal file
303
.utlis/gitea_issues.ps1
Normal file
@@ -0,0 +1,303 @@
|
||||
<#
|
||||
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
|
||||
}
|
||||
339
.utlis/gitea_issues.sh
Normal file
339
.utlis/gitea_issues.sh
Normal file
@@ -0,0 +1,339 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Gitea Issues Utility (Bash)
|
||||
#
|
||||
# Prérequis:
|
||||
# - curl, jq
|
||||
# - Fichier .env à la racine du projet contenant au minimum:
|
||||
# GITEA_BASE_URL=https://git.example.com/api/v1
|
||||
# GITEA_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# GITEA_DEFAULT_OWNER=owner
|
||||
# GITEA_DEFAULT_REPO=repo
|
||||
#
|
||||
# Utilisation rapide:
|
||||
# ./gitea_issues.sh create -t "Titre" -b "Corps (Markdown)" [-F chemin/fichier.md] [-l label1,label2] [-a user1,user2]
|
||||
# ./gitea_issues.sh list [-s open|closed|all] [-p page] [-P limit]
|
||||
# ./gitea_issues.sh get <index>
|
||||
# ./gitea_issues.sh update <index> [-t titre] [-b corps (Markdown) | -F chemin/fichier.md] [-l labels] [-a assignees]
|
||||
# ./gitea_issues.sh close <index>
|
||||
# ./gitea_issues.sh open <index>
|
||||
# ./gitea_issues.sh comment <index> -m "message (Markdown)" [-M chemin/commentaire.md]
|
||||
#
|
||||
# Notes:
|
||||
# - Les scripts lisent ../.env (depuis ./.utlis/) automatiquement.
|
||||
# - Vous pouvez surcharger owner/repo via variables d'env OWNER/REPO ou flags --owner/--repo.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Assurer un environnement UTF-8 pour les E/S et curl/jq
|
||||
ensure_utf8_locale() {
|
||||
# Si LANG/LC_ALL ne sont pas déjà en UTF-8, essaye de basculer
|
||||
local want="UTF-8"
|
||||
if [[ "${LANG:-}" != *"$want"* || "${LC_ALL:-}" != *"$want"* ]]; then
|
||||
local chosen=""
|
||||
if command -v locale >/dev/null 2>&1; then
|
||||
if locale -a 2>/dev/null | grep -qi '^C\.utf8\|C\.UTF-8$'; then
|
||||
chosen="C.UTF-8"
|
||||
elif locale -a 2>/dev/null | grep -qi '^en_US\.utf8\|en_US\.UTF-8$'; then
|
||||
chosen="en_US.UTF-8"
|
||||
elif locale -a 2>/dev/null | grep -qi '^fr_FR\.utf8\|fr_FR\.UTF-8$'; then
|
||||
chosen="fr_FR.UTF-8"
|
||||
fi
|
||||
fi
|
||||
if [[ -z "$chosen" ]]; then
|
||||
# Fallback raisonnable si locale -a n'est pas dispo
|
||||
chosen="C.UTF-8"
|
||||
fi
|
||||
export LANG="$chosen"
|
||||
export LC_ALL="$chosen"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_utf8_locale
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# Charger .env si présent
|
||||
ENV_FILE="$REPO_ROOT/.env"
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
# shellcheck disable=SC2046
|
||||
export $(grep -E '^[A-Za-z_][A-Za-z0-9_]*=' "$ENV_FILE" | sed 's/#.*//')
|
||||
fi
|
||||
|
||||
BASE_URL="${GITEA_BASE_URL:-}"
|
||||
TOKEN="${GITEA_TOKEN:-}"
|
||||
DEFAULT_OWNER="${GITEA_DEFAULT_OWNER:-}"
|
||||
DEFAULT_REPO="${GITEA_DEFAULT_REPO:-}"
|
||||
|
||||
OWNER_OVERRIDE=""
|
||||
REPO_OVERRIDE=""
|
||||
|
||||
require_tools() {
|
||||
for t in curl jq; do
|
||||
command -v "$t" >/dev/null 2>&1 || { echo "Erreur: outil requis introuvable: $t" >&2; exit 1; }
|
||||
done
|
||||
}
|
||||
|
||||
usage() {
|
||||
sed -n '1,60p' "$0"
|
||||
}
|
||||
|
||||
fail() { echo "Erreur: $*" >&2; exit 1; }
|
||||
|
||||
ensure_env() {
|
||||
[[ -n "$BASE_URL" ]] || fail "GITEA_BASE_URL manquant (.env)"
|
||||
[[ -n "$TOKEN" ]] || fail "GITEA_TOKEN manquant (.env)"
|
||||
}
|
||||
|
||||
resolve_repo() {
|
||||
local owner repo
|
||||
owner="${OWNER_OVERRIDE:-${OWNER:-$DEFAULT_OWNER}}"
|
||||
repo="${REPO_OVERRIDE:-${REPO:-$DEFAULT_REPO}}"
|
||||
[[ -n "$owner" && -n "$repo" ]] || fail "OWNER/REPO non définis (utilisez .env ou --owner/--repo)"
|
||||
echo "$owner" "$repo"
|
||||
}
|
||||
|
||||
api() {
|
||||
local method="$1" path="$2"; shift 2
|
||||
local url="$BASE_URL$path"
|
||||
curl -sS -X "$method" \
|
||||
-H "Authorization: token $TOKEN" \
|
||||
-H "Content-Type: application/json; charset=utf-8" \
|
||||
-H "Accept-Charset: utf-8" \
|
||||
"$url" "$@"
|
||||
}
|
||||
|
||||
api_jq_or_status() {
|
||||
# Lit la sortie JSON et vérifie erreurs Gitea
|
||||
local http_code json
|
||||
# Utilise curl avec -w pour capturer le code
|
||||
json=$(curl -sS -w '\n%{http_code}' \
|
||||
-H "Authorization: token $TOKEN" \
|
||||
-H "Content-Type: application/json; charset=utf-8" \
|
||||
-H "Accept-Charset: utf-8" \
|
||||
"$@")
|
||||
http_code="${json##*$'\n'}"
|
||||
json="${json%$'\n'*}"
|
||||
if [[ "$http_code" -ge 400 ]]; then
|
||||
echo "$json" | jq . 2>/dev/null || true
|
||||
fail "HTTP $http_code"
|
||||
else
|
||||
echo "$json" | jq .
|
||||
fi
|
||||
}
|
||||
|
||||
parse_common_flags() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--owner) OWNER_OVERRIDE="$2"; shift 2;;
|
||||
--repo) REPO_OVERRIDE="$2"; shift 2;;
|
||||
-h|--help) usage; exit 0;;
|
||||
*) break;;
|
||||
esac
|
||||
done
|
||||
echo "$@"
|
||||
}
|
||||
|
||||
cmd_create() {
|
||||
local title="" body="" body_file="" labels="" assignees=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-t|--title) title="$2"; shift 2;;
|
||||
-b|--body) body="$2"; shift 2;;
|
||||
-F|--body-file) body_file="$2"; shift 2;;
|
||||
-l|--labels) labels="$2"; shift 2;;
|
||||
-a|--assignees) assignees="$2"; shift 2;;
|
||||
*) break;;
|
||||
esac
|
||||
done
|
||||
[[ -n "$title" ]] || fail "Titre requis (-t)"
|
||||
read -r owner repo < <(resolve_repo)
|
||||
local data
|
||||
if [[ -n "$body" ]]; then
|
||||
# Inline prioritaire
|
||||
data=$(jq -n \
|
||||
--arg title "$title" \
|
||||
--arg body "$body" \
|
||||
--arg labels_csv "$labels" \
|
||||
--arg assignees_csv "$assignees" '
|
||||
{
|
||||
title: $title,
|
||||
body: $body
|
||||
}
|
||||
+ ( ($labels_csv|length) > 0
|
||||
then { labels: ($labels_csv | split(",") | map(select(. != ""))) } else {} end)
|
||||
+ ( ($assignees_csv|length) > 0
|
||||
then { assignees: ($assignees_csv | split(",") | map(select(. != ""))) } else {} end)
|
||||
')
|
||||
if [[ -n "$body_file" ]]; then
|
||||
echo "Avertissement: -b et -F fournis; -b (inline) sera prioritaire" >&2
|
||||
fi
|
||||
elif [[ -n "$body_file" ]]; then
|
||||
[[ -f "$body_file" ]] || fail "Fichier introuvable: $body_file"
|
||||
data=$(jq -n \
|
||||
--arg title "$title" \
|
||||
--arg labels_csv "$labels" \
|
||||
--arg assignees_csv "$assignees" \
|
||||
--rawfile body "$body_file" '
|
||||
{
|
||||
title: $title,
|
||||
body: $body
|
||||
}
|
||||
+ ( ($labels_csv|length) > 0
|
||||
then { labels: ($labels_csv | split(",") | map(select(. != ""))) } else {} end)
|
||||
+ ( ($assignees_csv|length) > 0
|
||||
then { assignees: ($assignees_csv | split(",") | map(select(. != ""))) } else {} end)
|
||||
')
|
||||
else
|
||||
data=$(jq -n \
|
||||
--arg title "$title" '
|
||||
{ title: $title }
|
||||
')
|
||||
fi
|
||||
api_jq_or_status -X POST "$BASE_URL/repos/$owner/$repo/issues" --data-binary "$data"
|
||||
}
|
||||
|
||||
cmd_list() {
|
||||
local state="open" page="1" limit="30"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-s|--state) state="$2"; shift 2;;
|
||||
-p|--page) page="$2"; shift 2;;
|
||||
-P|--limit) limit="$2"; shift 2;;
|
||||
*) break;;
|
||||
esac
|
||||
done
|
||||
read -r owner repo < <(resolve_repo)
|
||||
api GET "/repos/$owner/$repo/issues?state=$state&page=$page&limit=$limit" | jq '.[].number as $n | {number: $n, title: .title, state: .state, labels: [.labels[].name]}'
|
||||
}
|
||||
|
||||
cmd_get() {
|
||||
local index="$1"; [[ -n "$index" ]] || fail "Index requis"
|
||||
read -r owner repo < <(resolve_repo)
|
||||
api GET "/repos/$owner/$repo/issues/$index" | jq '{number, title, state, body, labels: [.labels[].name], assignees: [.assignees[].login], created_at, updated_at}'
|
||||
}
|
||||
|
||||
cmd_update() {
|
||||
local index title="" body="" body_file="" labels="" assignees="" state=""
|
||||
index="$1"; shift || true
|
||||
[[ -n "$index" ]] || fail "Index requis"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-t|--title) title="$2"; shift 2;;
|
||||
-b|--body) body="$2"; shift 2;;
|
||||
-F|--body-file) body_file="$2"; shift 2;;
|
||||
-l|--labels) labels="$2"; shift 2;;
|
||||
-a|--assignees) assignees="$2"; shift 2;;
|
||||
-s|--state) state="$2"; shift 2;;
|
||||
*) break;;
|
||||
esac
|
||||
done
|
||||
read -r owner repo < <(resolve_repo)
|
||||
local data
|
||||
if [[ -n "$body" ]]; then
|
||||
data=$(jq -n \
|
||||
--arg title "$title" \
|
||||
--arg body "$body" \
|
||||
--arg labels_csv "$labels" \
|
||||
--arg assignees_csv "$assignees" \
|
||||
--arg state "$state" '
|
||||
{}
|
||||
+ ( ($title|length)>0 then {title:$title} else {} end )
|
||||
+ {body:$body}
|
||||
+ ( ($labels_csv|length)>0 then {labels: ($labels_csv|split(",")|map(select(.!="")))} else {} end )
|
||||
+ ( ($assignees_csv|length)>0 then {assignees: ($assignees_csv|split(",")|map(select(.!="")))} else {} end )
|
||||
+ ( ($state|length)>0 then {state:$state} else {} end )
|
||||
')
|
||||
if [[ -n "$body_file" ]]; then
|
||||
echo "Avertissement: -b et -F fournis; -b (inline) sera prioritaire" >&2
|
||||
fi
|
||||
elif [[ -n "$body_file" ]]; then
|
||||
[[ -f "$body_file" ]] || fail "Fichier introuvable: $body_file"
|
||||
data=$(jq -n \
|
||||
--arg title "$title" \
|
||||
--rawfile body "$body_file" \
|
||||
--arg labels_csv "$labels" \
|
||||
--arg assignees_csv "$assignees" \
|
||||
--arg state "$state" '
|
||||
{}
|
||||
+ ( ($title|length)>0 then {title:$title} else {} end )
|
||||
+ {body:$body}
|
||||
+ ( ($labels_csv|length)>0 then {labels: ($labels_csv|split(",")|map(select(.!="")))} else {} end )
|
||||
+ ( ($assignees_csv|length)>0 then {assignees: ($assignees_csv|split(",")|map(select(.!="")))} else {} end )
|
||||
+ ( ($state|length)>0 then {state:$state} else {} end )
|
||||
')
|
||||
else
|
||||
data=$(jq -n \
|
||||
--arg title "$title" '
|
||||
{}
|
||||
+ ( ($title|length)>0 then {title:$title} else {} end )
|
||||
')
|
||||
fi
|
||||
api_jq_or_status -X PATCH "$BASE_URL/repos/$owner/$repo/issues/$index" --data-binary "$data"
|
||||
}
|
||||
|
||||
cmd_close() {
|
||||
local index="$1"; [[ -n "$index" ]] || fail "Index requis"
|
||||
cmd_update "$index" -s closed
|
||||
}
|
||||
|
||||
cmd_open() {
|
||||
local index="$1"; [[ -n "$index" ]] || fail "Index requis"
|
||||
cmd_update "$index" -s open
|
||||
}
|
||||
|
||||
cmd_comment() {
|
||||
local index message="" message_file=""
|
||||
index="$1"; shift || true
|
||||
[[ -n "$index" ]] || fail "Index requis"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-m|--message) message="$2"; shift 2;;
|
||||
-M|--message-file) message_file="$2"; shift 2;;
|
||||
*) break;;
|
||||
esac
|
||||
done
|
||||
# Inline prioritaire
|
||||
if [[ -n "$message" ]]; then
|
||||
read -r owner repo < <(resolve_repo)
|
||||
local data
|
||||
data=$(jq -n --arg body "$message" '{body:$body}')
|
||||
if [[ -n "$message_file" ]]; then
|
||||
echo "Avertissement: -m et -M fournis; -m (inline) sera prioritaire" >&2
|
||||
fi
|
||||
api_jq_or_status -X POST "$BASE_URL/repos/$owner/$repo/issues/$index/comments" --data-binary "$data"
|
||||
return
|
||||
elif [[ -n "$message_file" ]]; then
|
||||
[[ -f "$message_file" ]] || fail "Fichier introuvable: $message_file"
|
||||
read -r owner repo < <(resolve_repo)
|
||||
local data
|
||||
data=$(jq -n --rawfile body "$message_file" '{body:$body}')
|
||||
api_jq_or_status -X POST "$BASE_URL/repos/$owner/$repo/issues/$index/comments" --data-binary "$data"
|
||||
return
|
||||
else
|
||||
fail "Message requis (-m) ou fichier (-M)"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
require_tools
|
||||
ensure_env
|
||||
local args
|
||||
args=( $(parse_common_flags "$@") ) || true
|
||||
set +u
|
||||
local cmd="${args[0]}"; shift || true
|
||||
set -u
|
||||
case "$cmd" in
|
||||
create) shift; cmd_create "$@" ;;
|
||||
list) shift; cmd_list "$@" ;;
|
||||
get) shift; cmd_get "$1" ;;
|
||||
update) shift; cmd_update "$@" ;;
|
||||
close) shift; cmd_close "$1" ;;
|
||||
open) shift; cmd_open "$1" ;;
|
||||
comment) shift; cmd_comment "$@" ;;
|
||||
-h|--help|help|""|*) usage; exit 1;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user