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:
parent
15c667132b
commit
3f1378f6ba
@ -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
|
d'unicité dès la moindre donnée existante → import échouait. → mode
|
||||||
ÉCRASEMENT (effacement ordonné enfants→parents puis restauration) +
|
ÉCRASEMENT (effacement ordonné enfants→parents puis restauration) +
|
||||||
restauration du tConfig + reprogrammation des rappels.
|
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) :
|
36. **Sens des boutons de zoom inversé** (v1.2.10, retour utilisateur) :
|
||||||
le « + » dézoomait (fenêtre plus longue) et le « − » zoomait — contre
|
le « + » dézoomait (fenêtre plus longue) et le « − » zoomait — contre
|
||||||
la convention carte. → « + » = zoom avant (fenêtre courte), « − » =
|
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) ;
|
- 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
|
- 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) ;
|
DEUX FOIS pour publier les deux APK (noms distincts) ;
|
||||||
⚠️ renommage défensif : un asset peut se retrouver au nom générique après
|
⚠️ vérification intégrée (`ensure_asset`) : le `?name=` de l'upload et/ou
|
||||||
une session d'upload interrompue (extinction machine — arrivé en v1.2.9) →
|
le PATCH peuvent être ignorés par les instances (assets au nom générique /
|
||||||
le script RENOMME l'asset via l'API d'édition (`PATCH …/assets/{id}`)
|
APK disparu — v1.2.6, v1.2.10) → le script vérifie **nom ET taille** après
|
||||||
après chaque upload — ne pas retirer ;
|
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-release.apk` (**recommandé**, R8) et
|
||||||
`HormoneTrack-vX.Y.Z-debug.apk` ;
|
`HormoneTrack-vX.Y.Z-debug.apk` ;
|
||||||
- lit le token Gitea dans le trousseau macOS (`security find-internet-password`) ;
|
- lit le token Gitea dans le trousseau macOS (`security find-internet-password`) ;
|
||||||
|
|||||||
@ -20,6 +20,7 @@ Pour ajouter un token au trousseau :
|
|||||||
-w <TOKEN> -U
|
-w <TOKEN> -U
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@ -83,6 +84,36 @@ def changelog_section(version: str) -> str:
|
|||||||
return m.group(1).strip()
|
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():
|
def main():
|
||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
# Premier argument = instance s'il n'est pas un tag (un tag commence par « v »)
|
# 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:
|
if asset["name"] == asset_name:
|
||||||
request(f"{base}/releases/{release_id}/assets/{asset['id']}",
|
request(f"{base}/releases/{release_id}/assets/{asset['id']}",
|
||||||
"DELETE", token=token)
|
"DELETE", token=token)
|
||||||
with open(apk_path, "rb") as f:
|
# ⚠️ Renommage défensif + VÉRIFICATION : le ?name= de l'upload et/ou le
|
||||||
asset = request(
|
# PATCH peuvent être ignorés par certaines instances (nom générique
|
||||||
f"{base}/releases/{release_id}/assets?name={asset_name}",
|
# observé en v1.2.6/10) → vérifier nom + taille après upload, retenter
|
||||||
"POST", token=token,
|
# le PATCH une fois, et ÉCHOUER bruyamment si l'asset ne colle pas.
|
||||||
files={"name": asset_name, "data": f.read()},
|
ensure_asset(base, token, release_id, asset_name, apk_path)
|
||||||
)
|
|
||||||
# ⚠️ 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.
|
|
||||||
request(f"{base}/releases/{release_id}/assets/{asset['id']}",
|
request(f"{base}/releases/{release_id}/assets/{asset['id']}",
|
||||||
"PATCH", token=token, data={"name": asset_name})
|
"PATCH", token=token, data={"name": asset_name})
|
||||||
link = asset.get("browser_download_url", "?")
|
link = asset.get("browser_download_url", "?")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user