#!/usr/bin/env bash # gitea skill — listar runs de Gitea Actions de un repo. # # Gitea 1.24 expone `/actions/tasks` (NO `/actions/runs`). El endpoint solo # acepta `page` y `limit` — los demás filtros (workflow, branch, status, # event, actor) se aplican client-side acá. # # Uso: # actions-list-runs.sh / [--workflow ] # [--branch ] [--status ] # [--event ] [--actor ] # [--limit N] set -euo pipefail # Forzar UTF-8 en stdout de Python (Windows defaultea a cp1252 y crashea con # literales no-ASCII). Ver memoria feedback_api_utf8_encoding.md. export PYTHONIOENCODING=utf-8 SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" QUERY="$SKILL_DIR/scripts/query.sh" repo_arg="" workflow="" branch="" status="" event="" limit=10 while [[ $# -gt 0 ]]; do case "$1" in --workflow) workflow="$2"; shift 2 ;; --branch) branch="$2"; shift 2 ;; --status) status="$2"; shift 2 ;; --event) event="$2"; shift 2 ;; --limit) limit="$2"; shift 2 ;; -h|--help) cat </ [opciones] Opciones (todas client-side, el server solo respeta page+limit): --workflow ej. deploy-infra.yml (matchea workflow_id) --branch ej. main (matchea head_branch) --status success | failure | running | waiting | cancelled --event push | pull_request | workflow_dispatch --limit N cuántos traer del server (default 10) NOTA: Gitea no devuelve el actor del run en /actions/tasks, así que no hay filtro --actor. Si necesitás esa info, mirá el merge commit con git log. EOF exit 0 ;; -*) echo "ERROR: flag desconocida: $1" >&2; exit 2 ;; *) repo_arg="$1"; shift ;; esac done if [[ -z "$repo_arg" ]]; then echo "ERROR: pasá /." >&2 exit 2 fi if [[ "$repo_arg" == */* ]]; then owner="${repo_arg%%/*}" repo="${repo_arg##*/}" else set -a; source "$SKILL_DIR/.env"; set +a owner="${GITEA_DEFAULT_OWNER:?owner no especificado y GITEA_DEFAULT_OWNER vacío}" repo="$repo_arg" fi resp="$("$QUERY" "/repos/${owner}/${repo}/actions/tasks?limit=${limit}")" # export ANTES del pipe — el VAR=val inline no llega al python downstream. export PY_WORKFLOW="$workflow" PY_BRANCH="$branch" PY_STATUS="$status" \ PY_EVENT="$event" PY_OWNER="$owner" PY_REPO="$repo" echo "$resp" | PYTHONIOENCODING=utf-8 python -c ' import json, os, sys d = json.load(sys.stdin) runs = d.get("workflow_runs", []) total = d.get("total_count", len(runs)) owner = os.environ.get("PY_OWNER", "") repo = os.environ.get("PY_REPO", "") def keep(r): wf = os.environ.get("PY_WORKFLOW") if wf: rwf = r.get("workflow_id","") if rwf != wf and not rwf.endswith(wf): return False if os.environ.get("PY_BRANCH") and r.get("head_branch") != os.environ["PY_BRANCH"]: return False if os.environ.get("PY_STATUS") and r.get("status") != os.environ["PY_STATUS"]: return False if os.environ.get("PY_EVENT") and r.get("event") != os.environ["PY_EVENT"]: return False return True filtered = [r for r in runs if keep(r)] any_filter = any(os.environ.get(k) for k in ("PY_WORKFLOW","PY_BRANCH","PY_STATUS","PY_EVENT")) filt_label = f" (filtered: {len(filtered)}/{len(runs)})" if any_filter else "" print(f"{len(filtered)} run(s) en {owner}/{repo}{filt_label}, total disponible={total}") print() if not filtered: sys.exit(0) for r in filtered: tid = r.get("id") rnum = r.get("run_number") title = r.get("display_title") or r.get("name","?") if len(title) > 70: title = title[:67] + "..." st = r.get("status","?") branch = r.get("head_branch") or "?" ev = r.get("event","?") wf = r.get("workflow_id","?") created = (r.get("created_at") or "")[:19].replace("T"," ") sha = (r.get("head_sha") or "")[:7] print(f" #{rnum:<4} task={tid:<5} {st:<10} {ev:<18} {branch:<14} {wf}") print(f" {title}") print(f" sha={sha} created={created}") '