181 lines
7.5 KiB
YAML
181 lines
7.5 KiB
YAML
name: Triage Issue
|
|
description: "Analizza una issue con opencode: classifica, riassume, gap analisi e produce report MD."
|
|
|
|
inputs:
|
|
issue-title:
|
|
description: Titolo dell'issue.
|
|
required: true
|
|
issue-body:
|
|
description: Corpo dell'issue.
|
|
required: true
|
|
issue-number:
|
|
description: Numero dell'issue.
|
|
required: true
|
|
repository:
|
|
description: Repository in formato owner/repo.
|
|
required: true
|
|
api-token:
|
|
description: Token API Gitea.
|
|
required: true
|
|
gitea-host:
|
|
description: URL del server Gitea.
|
|
required: false
|
|
default: "https://git.incloud.ovh"
|
|
api-key:
|
|
description: API key per opencode.
|
|
required: true
|
|
model:
|
|
description: Modello AI in formato provider/model.
|
|
required: false
|
|
default: ""
|
|
|
|
outputs:
|
|
label:
|
|
description: Label attribuita (bug o richiesta).
|
|
value: ${{ steps.classifica.outputs.label }}
|
|
comment:
|
|
description: Commento di triage pubblicato.
|
|
value: ${{ steps.classifica.outputs.comment }}
|
|
|
|
runs:
|
|
using: composite
|
|
steps:
|
|
- name: Configura autenticazione opencode
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if ! command -v jq &> /dev/null; then
|
|
echo "jq non trovato, tentativo di installazione..."
|
|
if command -v apt-get &> /dev/null; then
|
|
apt-get update -qq && apt-get install -y -qq jq 2>&1 || true
|
|
elif command -v apk &> /dev/null; then
|
|
apk add --no-cache jq 2>&1 || true
|
|
fi
|
|
command -v jq &> /dev/null || { echo "ERRORE: impossibile installare jq."; exit 1; }
|
|
fi
|
|
mkdir -p ~/.local/share/opencode
|
|
jq -n \
|
|
--arg provider "opencode-go" \
|
|
--arg key "${{ inputs.api-key }}" \
|
|
'{($provider): {type: "api", key: $key}}' > ~/.local/share/opencode/auth.json
|
|
chmod 600 ~/.local/share/opencode/auth.json
|
|
|
|
- name: Classifica issue
|
|
id: classifica
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
# Assicura curl (potrebbe mancare su Alpine)
|
|
if ! command -v curl &> /dev/null; then
|
|
echo "curl non trovato, tentativo di installazione..."
|
|
if command -v apt-get &> /dev/null; then
|
|
apt-get update -qq && apt-get install -y -qq curl 2>&1 || true
|
|
elif command -v apk &> /dev/null; then
|
|
apk add --no-cache curl 2>&1 || true
|
|
fi
|
|
command -v curl &> /dev/null || { echo "ERRORE: impossibile installare curl."; exit 1; }
|
|
fi
|
|
|
|
HOST="${{ inputs.gitea-host }}"
|
|
TOKEN="${{ inputs.api-token }}"
|
|
REPO="${{ inputs.repository }}"
|
|
ISSUE_NUM="${{ inputs.issue-number }}"
|
|
MODEL_ARG=()
|
|
[ -n "${{ inputs.model }}" ] && MODEL_ARG=(--model "${{ inputs.model }}")
|
|
|
|
BODY="$(echo "${{ inputs.issue-body }}" | head -c 10000)"
|
|
[ "${#BODY}" -ge 10000 ] && BODY+=$'\n... (troncato)'
|
|
|
|
printf -v PROMPT '%s\n\n%s\n\n%s\n\n%s\n\n%s\n\n%s\n\n%s\n\n%s\n\n%s: %s\n%s:\n%s' \
|
|
"Sei un sistema di triage automatico per repository software." \
|
|
"Analizza il codice sorgente nel repository ed esamina la seguente issue." \
|
|
"Classifica con UNA delle label:" \
|
|
'- "bug": malfunzionamento, errore, crash, anomalia, comportamento inaspettato' \
|
|
'- "richiesta": nuova funzionalità, miglioramento, refactoring, ottimizzazione, o dubbio' \
|
|
"Rispondi ESCLUSIVAMENTE con un JSON valido su una SINGOLA riga, senza nessun altro testo:" \
|
|
'{"label":"bug","comment":"**Riassunto**: ...\\n\\n**Gap analisi**: codice mancante...\\n\\n**Domande aperte**:\\n1. ...","md":"# Analisi tecnica\\n\\n## Codice coinvolto\\n..."}' \
|
|
"Il campo 'comment' deve contenere: riassunto della issue, gap analisi (cosa manca rispetto al codice), domande aperte. Usa \\n per i newline." \
|
|
"Il campo 'md' deve contenere: analisi tecnica completa in formato markdown, file/moduli coinvolti, ipotesi root cause, proposta di fix. Usa \\n per i newline." \
|
|
"Titolo" "${{ inputs.issue-title }}" \
|
|
"Corpo" "$BODY"
|
|
|
|
OUTFILE="$GITHUB_WORKSPACE/opencode-triage.txt"
|
|
opencode run "$PROMPT" "${MODEL_ARG[@]}" --dangerously-skip-permissions 2>&1 | tee "$OUTFILE"
|
|
|
|
# Estrai la riga JSON dalla risposta (salta banner ANSI)
|
|
JSON_LINE=$(grep -E '^\{"label":"(bug|richiesta)","comment":' "$OUTFILE" | head -1)
|
|
JSON=""
|
|
if [ -n "$JSON_LINE" ]; then
|
|
JSON=$(echo "$JSON_LINE" | jq -c . 2>/dev/null)
|
|
fi
|
|
|
|
if [ -z "$JSON" ] || ! echo "$JSON" | jq empty 2>/dev/null; then
|
|
echo "WARN: Impossibile estrarre JSON valido, default a richiesta"
|
|
LABEL="richiesta"
|
|
COMMENT="Classificazione automatica non riuscita. Label impostata a richiesta per default."
|
|
MD_CONTENT=""
|
|
else
|
|
LABEL=$(echo "$JSON" | jq -r '.label // "richiesta"')
|
|
COMMENT=$(echo "$JSON" | jq -r '.comment // "Classificazione automatica."')
|
|
MD_CONTENT=$(echo "$JSON" | jq -r '.md // ""')
|
|
fi
|
|
|
|
case "$LABEL" in
|
|
bug|richiesta) ;;
|
|
*) echo "WARN: Label '$LABEL' sconosciuta, default a richiesta"; LABEL="richiesta" ;;
|
|
esac
|
|
|
|
echo "label=$LABEL" >> "$GITHUB_OUTPUT"
|
|
{
|
|
echo "comment<<EOF"
|
|
echo "$COMMENT"
|
|
echo "EOF"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
echo "Label determinata: $LABEL"
|
|
|
|
LABEL_ID=$(curl -sS -f "$HOST/api/v1/repos/$REPO/labels" \
|
|
-H "Authorization: token $TOKEN" \
|
|
-H "Accept: application/json" \
|
|
2>/dev/null | jq -r ".[] | select(.name==\"$LABEL\") | .id" | head -1)
|
|
|
|
if [ -n "$LABEL_ID" ]; then
|
|
curl -sS -X PUT "$HOST/api/v1/repos/$REPO/issues/$ISSUE_NUM/labels" \
|
|
-H "Authorization: token $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"labels\":[$LABEL_ID]}" 2>/dev/null && \
|
|
echo "Label '$LABEL' applicata" || \
|
|
echo "WARN: Impossibile applicare la label '$LABEL'"
|
|
else
|
|
echo "WARN: Label '$LABEL' non trovata nel repo. Creala manualmente o passa un token con permessi."
|
|
fi
|
|
|
|
# Carica il file MD come asset (prima del commento per poterlo linkare)
|
|
LINK_MD=""
|
|
if [ -n "$MD_CONTENT" ] && [ "$MD_CONTENT" != "null" ]; then
|
|
MD_FILE="/tmp/triage-issue-${ISSUE_NUM}.md"
|
|
printf '%s\n' "$MD_CONTENT" > "$MD_FILE"
|
|
|
|
ASSET_RESP=$(curl -sS -X POST "$HOST/api/v1/repos/$REPO/issues/$ISSUE_NUM/assets" \
|
|
-H "Authorization: token $TOKEN" \
|
|
-H "Accept: application/json" \
|
|
-F "attachment=@${MD_FILE};filename=triage-issue-${ISSUE_NUM}.md" 2>/dev/null)
|
|
ASSET_URL=$(echo "$ASSET_RESP" | jq -r '.download_url // empty' 2>/dev/null)
|
|
if [ -n "$ASSET_URL" ] && [ "$ASSET_URL" != "null" ]; then
|
|
LINK_MD="\n\n---\n📎 **Analisi tecnica completa**: [triage-issue-${ISSUE_NUM}.md](${ASSET_URL})"
|
|
echo "Asset MD caricato: $ASSET_URL"
|
|
else
|
|
echo "WARN: Impossibile caricare il file MD come asset"
|
|
fi
|
|
fi
|
|
|
|
COMMENT_BODY="${COMMENT}${LINK_MD}"
|
|
COMMENT_JSON=$(jq -n --arg body "$COMMENT_BODY" '{body: $body}')
|
|
curl -sS -X POST "$HOST/api/v1/repos/$REPO/issues/$ISSUE_NUM/comments" \
|
|
-H "Authorization: token $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$COMMENT_JSON" 2>/dev/null && \
|
|
echo "Commento pubblicato sull'issue #$ISSUE_NUM" || \
|
|
echo "WARN: Impossibile pubblicare il commento"
|