ci: rendre lisible l'échec d'attache de release (401 / 403 distingués)
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 / 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
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).
This commit is contained in:
@@ -4,8 +4,8 @@
|
|||||||
# Usage : attach-release-assets.sh <tag> <release-name> <fichier...>
|
# Usage : attach-release-assets.sh <tag> <release-name> <fichier...>
|
||||||
# Env : RELEASE_TOKEN (token Gitea avec write:repository), GITHUB_SERVER_URL, GITHUB_REPOSITORY.
|
# Env : RELEASE_TOKEN (token Gitea avec write:repository), GITHUB_SERVER_URL, GITHUB_REPOSITORY.
|
||||||
#
|
#
|
||||||
# Partagé par tous les jobs de release desktop (Linux, Windows, canal flottant) : la logique était
|
# Partagé par tous les jobs de release desktop et par le VSIX : la logique était dupliquée, et toute
|
||||||
# dupliquée dans chaque job, et toute correction devait être faite trois fois.
|
# correction devait être faite trois fois.
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
tag="${1:?tag manquant}"
|
tag="${1:?tag manquant}"
|
||||||
@@ -23,40 +23,74 @@ fi
|
|||||||
|
|
||||||
api="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
|
api="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
|
||||||
auth="Authorization: token ${RELEASE_TOKEN}"
|
auth="Authorization: token ${RELEASE_TOKEN}"
|
||||||
|
body=$(mktemp)
|
||||||
|
trap 'rm -f "$body"' EXIT
|
||||||
|
|
||||||
release_id=$(curl -fsSL -H "$auth" "${api}/releases/tags/${tag}" \
|
# Lecture d'un champ JSON TOLÉRANTE : une réponse vide ou non-JSON (401, 403, 404) doit donner une
|
||||||
| node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).id || ''" || true)
|
# 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
|
if [ -z "$release_id" ]; then
|
||||||
release_id=$(curl -fsSL -X POST -H "$auth" -H 'Content-Type: application/json' \
|
# 401/403 sur une simple lecture : inutile de tenter la création, le token est en cause.
|
||||||
-d "{\"tag_name\":\"${tag}\",\"name\":\"${release_name}\"}" \
|
case "$code" in
|
||||||
"${api}/releases" | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).id || ''")
|
401)
|
||||||
fi
|
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
|
if [ -z "$release_id" ]; then
|
||||||
echo "::error::impossible de résoudre ou créer la release ${tag} avec le token fourni."
|
echo "::error::impossible de créer la release ${tag} (HTTP ${create_code})."
|
||||||
echo "::error::vérifier que le secret porte les portées write:repository et write:package, et qu'il n'a pas expiré."
|
echo "::error::réponse de l'API : $(head -c 300 "$body")"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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
|
for f in "$@"; do
|
||||||
[ -f "$f" ] || continue
|
[ -f "$f" ] || continue
|
||||||
name=$(basename "$f")
|
name=$(basename "$f")
|
||||||
# L'API Gitea refuse un asset de même nom : on supprime l'ancien pour que le dernier build gagne.
|
# L'API Gitea refuse un asset de même nom : on supprime l'ancien pour que le dernier build gagne.
|
||||||
existing=$(curl -fsSL -H "$auth" "${api}/releases/${release_id}/assets" \
|
http_call -H "$auth" "${api}/releases/${release_id}/assets" > /dev/null
|
||||||
| node -e "const a=JSON.parse(require('fs').readFileSync(0,'utf8'));const m=Array.isArray(a)?a.find(x=>x.name===process.argv[1]):null;process.stdout.write(m?String(m.id):'')" "$name" || true)
|
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
|
if [ -n "$existing" ]; then
|
||||||
echo "replacing existing $name (asset $existing)"
|
echo "remplacement de $name (asset $existing)"
|
||||||
curl -fsSL -X DELETE -H "$auth" "${api}/releases/${release_id}/assets/${existing}" || true
|
http_call -X DELETE -H "$auth" "${api}/releases/${release_id}/assets/${existing}" > /dev/null
|
||||||
fi
|
fi
|
||||||
echo "attaching $name"
|
upload_code=$(http_call -X POST -H "$auth" -F "attachment=@${f}" "${api}/releases/${release_id}/assets?name=${name}")
|
||||||
if ! curl -fsSL -X POST -H "$auth" -F "attachment=@${f}" "${api}/releases/${release_id}/assets?name=${name}"; then
|
if [ "$upload_code" -ge 200 ] && [ "$upload_code" -lt 300 ]; then
|
||||||
echo "::error::échec de l'upload de ${name}"
|
echo "attaché : $name"
|
||||||
|
else
|
||||||
|
echo "::error::échec de l'upload de ${name} (HTTP ${upload_code}) : $(head -c 200 "$body")"
|
||||||
failed=1
|
failed=1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "${failed:-0}" != "0" ]; then
|
if [ "$failed" != "0" ]; then
|
||||||
echo "::error::au moins un asset n'a pas pu être attaché à ${tag}."
|
echo "::error::au moins un asset n'a pas pu être attaché à ${tag}."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user