diff --git a/README.md b/README.md index f19d835..c4f5622 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,11 @@ pointe de `main`. ### Ce qu'est une version Un tag git `vMAJEUR.MINEUR.CORRECTIF` (semver), pose sur `main`. Le meme numero -est ecrit dans quatre fichiers, que l'outillage tient d'accord entre eux : -`pyproject.toml`, `src/plesna_gerance/__init__.py`, `frontend/package.json` et -`packaging/installer.iss` (version affichee par l'installeur Windows). +est ecrit dans cinq fichiers, que l'outillage tient d'accord entre eux : +`pyproject.toml`, `src/plesna_gerance/__init__.py`, `frontend/package.json`, +`frontend/package-lock.json` (qui porte lui aussi la version du paquet racine, +sans quoi `npm ci` peut refuser de tourner en CI) et `packaging/installer.iss` +(version affichee par l'installeur Windows). Quand incrementer quoi : diff --git a/scripts/release.py b/scripts/release.py index e1a918f..e1cc0ef 100644 --- a/scripts/release.py +++ b/scripts/release.py @@ -1,10 +1,10 @@ """Pose une version : aligne les fichiers, commite, tague. -Le numéro de version est écrit à quatre endroits qui doivent rester d'accord -(paquet Python, module, frontend, installeur Windows). Les tenir à jour à la -main, c'est publier tôt ou tard un tag `v0.3.0` sur un code qui se déclare -`0.1.0`. Ce script fait la mise à jour d'un bloc et refuse d'avancer au moindre -doute plutôt que de produire une version à moitié cohérente. +Le numéro de version est écrit dans cinq fichiers qui doivent rester d'accord +(paquet Python, module, frontend, son lock, installeur Windows). Les tenir à +jour à la main, c'est publier tôt ou tard un tag `v0.3.0` sur un code qui se +déclare `0.1.0`. Ce script fait la mise à jour d'un bloc et refuse d'avancer au +moindre doute plutôt que de produire une version à moitié cohérente. uv run python scripts/release.py 0.2.0 @@ -20,25 +20,48 @@ from pathlib import Path RACINE = Path(__file__).resolve().parent.parent -# Un fichier, le motif qui y porte la version, et le remplacement. Le motif doit -# capturer la version dans le groupe 1 pour que l'on puisse relire l'ancienne -# valeur, et matcher **exactement une fois** (vérifié plus bas). +# Un fichier, le motif qui y porte la version, le remplacement, et le nombre +# d'occurrences attendues. Le motif doit capturer la version dans son dernier +# groupe pour que l'on puisse relire l'ancienne valeur ; le compte est verifie +# a chaque passage, pour que le script s'arrete si un fichier change de forme +# plutot que de laisser filer une version a moitie posee. PORTEURS_DE_VERSION = [ - ("pyproject.toml", re.compile(r'^version = "([^"]+)"$', re.M), 'version = "{v}"'), + ( + "pyproject.toml", + re.compile(r'^version = "([^"]+)"$', re.M), + 'version = "{v}"', + 1, + ), ( "src/plesna_gerance/__init__.py", re.compile(r'^__version__ = "([^"]+)"$', re.M), '__version__ = "{v}"', + 1, ), ( "frontend/package.json", re.compile(r'^ "version": "([^"]+)",$', re.M), ' "version": "{v}",', + 1, + ), + # Le lock porte la version du paquet racine a deux endroits (en-tete et + # packages[""]). Les laisser en arriere fait diverger lock et package.json, + # ce que `npm ci` peut refuser en CI selon la version de npm. On s'ancre sur + # le nom du paquet, qui n'apparait qu'a ces deux endroits. + ( + "frontend/package-lock.json", + re.compile( + r'(^(\s*)"name": "plesna-gerance-frontend",\n\s*"version": ")([^"]+)', + re.M, + ), + r"\g<1>{v}", + 2, ), ( "packaging/installer.iss", re.compile(r'^#define AppVersion "([^"]+)"$', re.M), '#define AppVersion "{v}"', + 1, ), ] @@ -86,19 +109,23 @@ def verifier_le_depot(version: str, autoriser_hors_main: bool) -> None: def appliquer_la_version(version: str) -> list[str]: """Écrit la version dans les fichiers porteurs. Renvoie ceux qui ont changé.""" modifies = [] - for chemin_relatif, motif, remplacement in PORTEURS_DE_VERSION: + for chemin_relatif, motif, remplacement, attendues in PORTEURS_DE_VERSION: chemin = RACINE / chemin_relatif contenu = chemin.read_text(encoding="utf-8") - occurrences = motif.findall(contenu) - if len(occurrences) != 1: + # Le dernier groupe capture la version elle-même, quel que soit le + # nombre de groupes servant à l'ancrage. + trouvees = [ + correspondance.groups()[-1] for correspondance in motif.finditer(contenu) + ] + if len(trouvees) != attendues: raise Refus( - f"{chemin_relatif} : {len(occurrences)} ligne(s) de version trouvée(s), " - "une seule attendue. Le fichier a changé de forme : " + f"{chemin_relatif} : {len(trouvees)} version(s) trouvée(s), " + f"{attendues} attendue(s). Le fichier a changé de forme : " "corriger le motif dans scripts/release.py." ) - if occurrences[0] == version: + if all(trouvee == version for trouvee in trouvees): continue chemin.write_text(