scripts/gitea-release.py : ensure_asset (upload + vérif nom/taille + retry PATCH + échec bruyant) — les ?name=/PATCH pouvaient être ignorés (v1.2.6/v1.2.10) ; docs §16.bis/§14 #37

This commit is contained in:
Siphonight 2026-09-05 23:45:45 +02:00
parent c422c801ae
commit 8aa4fe9ac0
2 changed files with 45 additions and 14 deletions

View File

@ -665,6 +665,10 @@ Historique complet — **à ne pas réintroduire** (utile pour diff/revert) :
d'unicité dès la moindre donnée existante → import échouait. → mode
ÉCRASEMENT (effacement ordonné enfants→parents puis restauration) +
restauration du tConfig + reprogrammation des rappels.
37. **Upload d'asset Gitea instable** (v1.2.10) : le `?name=` du POST et le
PATCH de renommage peuvent être ignorés (asset au nom générique, APK
release disparu de la release). → `ensure_asset` dans le script :
upload + vérification nom/taille + retry PATCH + échec bruyant.
36. **Sens des boutons de zoom inversé** (v1.2.10, retour utilisateur) :
le « + » dézoomait (fenêtre plus longue) et le « − » zoomait — contre
la convention carte. → « + » = zoom avant (fenêtre courte), « − » =
@ -788,10 +792,11 @@ Le script :
- crée la release si absente, sinon **met à jour le corps** (PATCH) ;
- attache l'APK (remplace l'asset du même nom si présent) — appeler le script
DEUX FOIS pour publier les deux APK (noms distincts) ;
⚠️ renommage défensif : un asset peut se retrouver au nom générique après
une session d'upload interrompue (extinction machine — arrivé en v1.2.9) →
le script RENOMME l'asset via l'API d'édition (`PATCH …/assets/{id}`)
après chaque upload — ne pas retirer ;
⚠️ vérification intégrée (`ensure_asset`) : le `?name=` de l'upload et/ou
le PATCH peuvent être ignorés par les instances (assets au nom générique /
APK disparu — v1.2.6, v1.2.10) → le script vérifie **nom ET taille** après
chaque upload, retente le PATCH une fois, et **échoue bruyamment** si
l'asset ne colle pas — ne pas retirer ;
`HormoneTrack-vX.Y.Z-release.apk` (**recommandé**, R8) et
`HormoneTrack-vX.Y.Z-debug.apk` ;
- lit le token Gitea dans le trousseau macOS (`security find-internet-password`) ;

View File

@ -20,6 +20,7 @@ Pour ajouter un token au trousseau :
-w <TOKEN> -U
"""
import json
import os
import re
import subprocess
import sys
@ -83,6 +84,36 @@ def changelog_section(version: str) -> str:
return m.group(1).strip()
def ensure_asset(base: str, token: str, release_id: int, name: str, path: str) -> dict:
"""Upload + vérification (nom ET taille) + retry PATCH — échec bruyant.
Le ?name= de l'upload et/ou le PATCH peuvent être ignorés par certaines
instances (v1.2.6/v1.2.10 : asset au nom générique, APK release disparu)."""
import uuid
expected = os.path.getsize(path)
boundary = uuid.uuid4().hex
with open(path, "rb") as f:
data = (f"--{boundary}\r\nContent-Disposition: form-data; name=\"attachment\"; "
f"filename=\"{name}\"\r\n"
f"Content-Type: application/vnd.android.package-archive\r\n\r\n").encode() \
+ f.read() + f"\r\n--{boundary}--\r\n".encode()
req = urllib.request.Request(f"{base}/releases/{release_id}/assets?name={name}",
method="POST",
headers={"Authorization": f"token {token}",
"Content-Type": f"multipart/form-data; boundary={boundary}"})
req.data = data
uploaded = json.loads(urllib.request.urlopen(req, timeout=300).read().decode())
for _ in range(2):
release = request(f"{base}/releases/{release_id}", token=token)
current = next((a for a in release["assets"] if a["id"] == uploaded["id"]), None)
if current and current["name"] == name and abs(current["size"] - expected) < 1000:
return current
request(f"{base}/releases/{release_id}/assets/{uploaded['id']}",
"PATCH", token=token, data={"name": name})
sys.exit(f"✗ ÉCHEC : l'asset {name} n'a pas le bon nom/taille après retry — "
f"la release est incomplète, publier manuellement.")
def main():
args = sys.argv[1:]
# Premier argument = instance s'il n'est pas un tag (un tag commence par « v »)
@ -128,16 +159,11 @@ def main():
if asset["name"] == asset_name:
request(f"{base}/releases/{release_id}/assets/{asset['id']}",
"DELETE", token=token)
with open(apk_path, "rb") as f:
asset = request(
f"{base}/releases/{release_id}/assets?name={asset_name}",
"POST", token=token,
files={"name": asset_name, "data": f.read()},
)
# ⚠️ Renommage défensif : le nom d'asset peut se retrouver générique
# (ex. nom générique observé après une session d'upload INTERROMPUE —
# extinction machine en plein script) → renommage explicite via l'API
# d'édition d'asset, systématique après chaque upload.
# ⚠️ Renommage défensif + VÉRIFICATION : le ?name= de l'upload et/ou le
# PATCH peuvent être ignorés par certaines instances (nom générique
# observé en v1.2.6/10) → vérifier nom + taille après upload, retenter
# le PATCH une fois, et ÉCHOUER bruyamment si l'asset ne colle pas.
ensure_asset(base, token, release_id, asset_name, apk_path)
request(f"{base}/releases/{release_id}/assets/{asset['id']}",
"PATCH", token=token, data={"name": asset_name})
link = asset.get("browser_download_url", "?")