Files
arboretum/.gitea/scripts/attach-release-assets.sh
T
johanleroy 9624270d9b
CI / No em/en dashes (push) Successful in 3s
Deploy site (production) / build-and-deploy (push) Successful in 47s
CI / Build & test (Node 24) (push) Successful in 10m12s
CI / Build & test (Node 22) (push) Successful in 10m23s
CI / Pack & boot smoke (Node 22) (push) Successful in 9m55s
ci: rendre lisible l'échec d'attache de release (401 / 403 distingués)
Le parsing JSON du script n'était pas tolérant à une réponse vide : sur un 401, deux
« SyntaxError: Unexpected end of JSON input » de Node s'affichaient AVANT le message d'erreur utile et
noyaient le diagnostic. La lecture de champ est désormais tolérante, et le script nomme la cause selon
le code HTTP : 401 = token invalide ou expiré, 403 = portées insuffisantes.

Testé contre un faux serveur pour les trois cas (401, 403, succès) : messages attendus et codes de
sortie corrects (1 en échec, 0 en succès).
2026-08-04 15:44:12 +02:00

99 lines
4.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# Attache des fichiers à une release Gitea, de façon idempotente (re-run friendly).
#
# Usage : attach-release-assets.sh <tag> <release-name> <fichier...>
# Env : RELEASE_TOKEN (token Gitea avec write:repository), GITHUB_SERVER_URL, GITHUB_REPOSITORY.
#
# Partagé par tous les jobs de release desktop et par le VSIX : la logique était dupliquée, et toute
# correction devait être faite trois fois.
set -uo pipefail
tag="${1:?tag manquant}"
release_name="${2:?nom de release manquant}"
shift 2
# Token ABSENT : cas légitime (fork, run sans secret) → on sort proprement.
# Token PRÉSENT mais refusé par l'API : anomalie, on doit ÉCHOUER. Sinon le job reste vert alors
# qu'aucun asset n'est attaché et qu'aucune release n'est créée, ce qui s'est produit avec un token
# expiré : trois workflows « réussis » et zéro fichier publié.
if [ -z "${RELEASE_TOKEN:-}" ]; then
echo "::notice::RELEASE_TOKEN absent, aucun asset attaché (les artefacts du run restent disponibles)."
exit 0
fi
api="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
auth="Authorization: token ${RELEASE_TOKEN}"
body=$(mktemp)
trap 'rm -f "$body"' EXIT
# Lecture d'un champ JSON TOLÉRANTE : une réponse vide ou non-JSON (401, 403, 404) doit donner une
# chaîne vide, pas une pile d'appels Node. Sans ça, deux `SyntaxError: Unexpected end of JSON input`
# s'affichaient avant le vrai message d'erreur et noyaient le diagnostic.
json_field() {
node -e "let s='';process.stdin.on('data',(d)=>{s+=d}).on('end',()=>{try{const o=JSON.parse(s);const v=o?.[process.argv[1]];process.stdout.write(v==null?'':String(v))}catch{process.stdout.write('')}})" "$1"
}
# `curl` silencieux qui écrit le corps dans $body et renvoie le code HTTP sur stdout.
http_call() {
curl -sS -o "$body" -w '%{http_code}' "$@"
}
# --- résolution de la release (existante, sinon création) -------------------------------------
code=$(http_call -H "$auth" "${api}/releases/tags/${tag}")
release_id=$(json_field id < "$body")
if [ -z "$release_id" ]; then
# 401/403 sur une simple lecture : inutile de tenter la création, le token est en cause.
case "$code" in
401)
echo "::error::le token de release est refusé (HTTP 401) : il est invalide, révoqué ou expiré."
echo "::error::régénérer un token Gitea et mettre à jour le secret NPM_TOKEN du dépôt."
exit 1
;;
403)
echo "::error::le token de release manque de droits (HTTP 403) sur ${GITHUB_REPOSITORY}."
echo "::error::portées attendues : write:repository (releases et assets) et write:package (publication npm)."
exit 1
;;
esac
create_code=$(http_call -X POST -H "$auth" -H 'Content-Type: application/json' \
-d "{\"tag_name\":\"${tag}\",\"name\":\"${release_name}\"}" "${api}/releases")
release_id=$(json_field id < "$body")
if [ -z "$release_id" ]; then
echo "::error::impossible de créer la release ${tag} (HTTP ${create_code})."
echo "::error::réponse de l'API : $(head -c 300 "$body")"
exit 1
fi
echo "release ${tag} créée (id ${release_id})."
else
echo "release ${tag} trouvée (id ${release_id})."
fi
# --- attache des fichiers ----------------------------------------------------------------------
failed=0
for f in "$@"; do
[ -f "$f" ] || continue
name=$(basename "$f")
# L'API Gitea refuse un asset de même nom : on supprime l'ancien pour que le dernier build gagne.
http_call -H "$auth" "${api}/releases/${release_id}/assets" > /dev/null
existing=$(node -e "let s='';process.stdin.on('data',(d)=>{s+=d}).on('end',()=>{try{const a=JSON.parse(s);const m=Array.isArray(a)?a.find((x)=>x.name===process.argv[1]):null;process.stdout.write(m?String(m.id):'')}catch{process.stdout.write('')}})" "$name" < "$body")
if [ -n "$existing" ]; then
echo "remplacement de $name (asset $existing)"
http_call -X DELETE -H "$auth" "${api}/releases/${release_id}/assets/${existing}" > /dev/null
fi
upload_code=$(http_call -X POST -H "$auth" -F "attachment=@${f}" "${api}/releases/${release_id}/assets?name=${name}")
if [ "$upload_code" -ge 200 ] && [ "$upload_code" -lt 300 ]; then
echo "attaché : $name"
else
echo "::error::échec de l'upload de ${name} (HTTP ${upload_code}) : $(head -c 200 "$body")"
failed=1
fi
done
if [ "$failed" != "0" ]; then
echo "::error::au moins un asset n'a pas pu être attaché à ${tag}."
exit 1
fi
echo "Assets attachés à la release ${tag}."