En Windows el stdout de Python defaultea a cp1252 y crashea con
UnicodeEncodeError al imprimir literales no-ASCII (e.g. el ✓ del print
de éxito de pr-create.sh). El sintoma era que el PR se creaba OK pero
el script terminaba con exit 1 por el crash del print posterior.
Agrego export PYTHONIOENCODING=utf-8 después de set -euo pipefail en
los 10 scripts que invocan python (todos menos query.sh). Cubre tanto
las invocaciones inline como futuras sin tener que acordarse caso por
caso. Mantiene los literales unicode existentes (✓, →, ─, ⚠️).
185 lines
6.1 KiB
Bash
185 lines
6.1 KiB
Bash
#!/usr/bin/env bash
|
|
# gitea skill — setup inicial.
|
|
#
|
|
# Idempotente. Cada corrida:
|
|
# 1. Busca el PAT en la skill bitwarden (item "claudecode0 · Gitea PAT
|
|
# claude-agent-gitops").
|
|
# 2. Si hay duplicados, toma el primero y avisa al usuario que limpie.
|
|
# 3. Escribe ~/.claude/skills/gitea/.env con BASE_URL + PAT + DEFAULT_OWNER.
|
|
# 4. Valida con GET /api/v1/version → debe responder.
|
|
#
|
|
# Re-corré esto cuando rotás el PAT en Gitea y lo guardás de nuevo en bitwarden.
|
|
|
|
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)"
|
|
ENV_FILE="$SKILL_DIR/.env"
|
|
ENV_EXAMPLE="$SKILL_DIR/.env.example"
|
|
|
|
BW_QUERY="$HOME/.claude/skills/bitwarden/scripts/query.sh"
|
|
# Matchamos por substring porque los items reales en bitwarden tienen el `·`
|
|
# doble-encodeado (corrupción histórica del web vault). El substring esquiva
|
|
# el carácter roto.
|
|
PAT_NAME_SUBSTR="Gitea PAT claude-agent-gitops"
|
|
PAT_ITEM_DESCRIPTION="claudecode0 · Gitea PAT claude-agent-gitops"
|
|
GITEA_BASE_URL="${GITEA_BASE_URL:-https://gitea.nucleoriofrio.com}"
|
|
DEFAULT_OWNER="${GITEA_DEFAULT_OWNER:-NucleOS}"
|
|
|
|
if [[ ! -x "$BW_QUERY" ]]; then
|
|
cat >&2 <<EOF
|
|
ERROR: la skill bitwarden no está configurada en $BW_QUERY.
|
|
|
|
Setup necesario primero:
|
|
bash ~/.claude/skills/bitwarden/scripts/setup.sh
|
|
|
|
Después re-corré esto.
|
|
EOF
|
|
exit 1
|
|
fi
|
|
|
|
echo "→ Buscando PAT en el vault de bitwarden (claudecode0)..."
|
|
|
|
# Search por substring corto (el `·` en el nombre URL-encoded confunde a
|
|
# bw serve). Filtramos por nombre exacto en el python downstream.
|
|
list_resp="$("$BW_QUERY" "/list/object/items?search=Gitea+PAT+claude-agent-gitops")" || {
|
|
echo "ERROR: query a bitwarden falló. ¿bw serve arriba? ¿session expirada?" >&2
|
|
echo " Probá: bash ~/.claude/skills/bitwarden/scripts/setup.sh" >&2
|
|
exit 2
|
|
}
|
|
|
|
# Filtrar matches por substring del nombre. Python lee JSON por stdin y
|
|
# el substring por env var (export antes del pipe — sino el VAR=val inline
|
|
# solo aplica al `printf`, no al `python`).
|
|
export PAT_NAME_SUBSTR
|
|
mapfile -t pat_ids < <(
|
|
printf '%s' "$list_resp" | python -c '
|
|
import json, os, sys
|
|
target = os.environ["PAT_NAME_SUBSTR"]
|
|
data = json.load(sys.stdin)
|
|
items = (data.get("data") or {}).get("data") or data.get("data") or []
|
|
if isinstance(items, dict):
|
|
items = items.get("data", [])
|
|
for it in items:
|
|
name = it.get("name", "") or ""
|
|
if target in name:
|
|
print(it.get("id", ""))
|
|
' | tr -d '\r'
|
|
)
|
|
|
|
case "${#pat_ids[@]}" in
|
|
0)
|
|
cat >&2 <<EOF
|
|
ERROR: no encontré ningún item con substring "$PAT_NAME_SUBSTR" en el nombre.
|
|
|
|
Pasos:
|
|
1. Generá un PAT en https://gitea.nucleoriofrio.com/user/settings/applications
|
|
con scopes: read:repo, write:repo, read:issue, write:issue, read:user.
|
|
2. Guardalo en bitwarden con un nombre que contenga "$PAT_NAME_SUBSTR".
|
|
(Idealmente: "$PAT_ITEM_DESCRIPTION".)
|
|
3. Re-corré este setup.
|
|
|
|
EOF
|
|
exit 3
|
|
;;
|
|
1)
|
|
pat_id="${pat_ids[0]}"
|
|
;;
|
|
*)
|
|
pat_id="${pat_ids[0]}"
|
|
cat >&2 <<EOF
|
|
⚠️ Hay ${#pat_ids[@]} items en bitwarden con substring "$PAT_NAME_SUBSTR".
|
|
Tomé el primero (id: $pat_id). Si no es el activo, este setup va a fallar
|
|
en la validación más abajo.
|
|
|
|
Limpiá los duplicados desde la web (https://vault.nucleoriofrio.com) — la
|
|
skill bitwarden bloquea DELETE así que no podemos borrarlos desde acá.
|
|
|
|
EOF
|
|
;;
|
|
esac
|
|
|
|
echo "→ Extrayendo password del item $pat_id..."
|
|
pat_value="$("$BW_QUERY" "/object/password/${pat_id}" | python -c "
|
|
import json,sys
|
|
d=json.load(sys.stdin)
|
|
v=d.get('data')
|
|
if isinstance(v, dict):
|
|
v=v.get('data')
|
|
print(v or '')
|
|
" | tr -d '\r')"
|
|
|
|
if [[ -z "$pat_value" ]]; then
|
|
echo "ERROR: el item existe pero el password vino vacío. Revisá en web." >&2
|
|
exit 4
|
|
fi
|
|
|
|
echo "→ Escribiendo $ENV_FILE..."
|
|
{
|
|
echo "# gitea skill — generado por setup.sh"
|
|
echo "# NO editar a mano (re-corré setup.sh para refrescar el PAT)."
|
|
echo ""
|
|
echo "GITEA_BASE_URL=$GITEA_BASE_URL"
|
|
echo "GITEA_PAT=$pat_value"
|
|
echo "GITEA_DEFAULT_OWNER=$DEFAULT_OWNER"
|
|
} > "$ENV_FILE"
|
|
chmod 600 "$ENV_FILE" 2>/dev/null || true
|
|
|
|
echo "→ Validando con GET /api/v1/version..."
|
|
version_resp="$(GITEA_PAT="$pat_value" GITEA_BASE_URL="$GITEA_BASE_URL" \
|
|
curl -sS -m 10 \
|
|
-H "Authorization: token $pat_value" \
|
|
-H "Accept: application/json" \
|
|
"${GITEA_BASE_URL}/api/v1/version")" || {
|
|
echo "ERROR: GET /version falló. ¿base URL correcta? ¿conexión?" >&2
|
|
exit 5
|
|
}
|
|
|
|
version="$(echo "$version_resp" | python -c "import json,sys; print(json.load(sys.stdin).get('version','?'))" 2>/dev/null || echo "?")"
|
|
|
|
if [[ "$version" == "?" || -z "$version" ]]; then
|
|
cat >&2 <<EOF
|
|
ERROR: el server respondió pero no pude parsear la versión.
|
|
Respuesta cruda:
|
|
$version_resp
|
|
|
|
Posibles causas:
|
|
- PAT inválido / revocado (limpiá el duplicado en bitwarden)
|
|
- Base URL mal en .env
|
|
|
|
EOF
|
|
exit 6
|
|
fi
|
|
|
|
# Sanity check del user actual con el PAT — capturamos el login para
|
|
# escribirlo a .env (lo usa repo-create.sh para detectar user vs. org).
|
|
user_resp="$(curl -sS -m 10 \
|
|
-H "Authorization: token $pat_value" \
|
|
-H "Accept: application/json" \
|
|
"${GITEA_BASE_URL}/api/v1/user")"
|
|
username="$(echo "$user_resp" | python -c "import json,sys; print(json.load(sys.stdin).get('login','?'))" 2>/dev/null || echo "?")"
|
|
|
|
if [[ "$username" != "?" && -n "$username" ]]; then
|
|
# Append/replace GITEA_BOT_USER en .env
|
|
if grep -q '^GITEA_BOT_USER=' "$ENV_FILE" 2>/dev/null; then
|
|
# Reemplazar línea existente (sed -i no es portable en Git Bash, usar archivo temp)
|
|
grep -v '^GITEA_BOT_USER=' "$ENV_FILE" > "$ENV_FILE.tmp"
|
|
echo "GITEA_BOT_USER=$username" >> "$ENV_FILE.tmp"
|
|
mv "$ENV_FILE.tmp" "$ENV_FILE"
|
|
else
|
|
echo "GITEA_BOT_USER=$username" >> "$ENV_FILE"
|
|
fi
|
|
chmod 600 "$ENV_FILE" 2>/dev/null || true
|
|
fi
|
|
|
|
echo ""
|
|
echo "→ Listo: Gitea $version | user=$username | $GITEA_BASE_URL"
|
|
echo ""
|
|
echo "Próximos pasos:"
|
|
echo " bash $SKILL_DIR/scripts/query.sh /version"
|
|
echo " bash $SKILL_DIR/scripts/pr-list.sh NucleOS/nucleo-infra"
|
|
echo " bash $SKILL_DIR/scripts/actions-list-runs.sh NucleOS/nucleo-infra"
|