From 94843311b4acc1832d354c1ec01eaf0b4a2abc2b Mon Sep 17 00:00:00 2001 From: Alberto Balbo Date: Tue, 4 Aug 2026 22:38:45 +0200 Subject: [PATCH] Il tag git diventa la fonte unica della versione MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il .csproj non viene piu' riscritto a ogni rilascio: i quattro numeri di versione arrivano a dotnet publish come proprieta' da riga di comando, ricavati dal tag su HEAD. Tag, eseguibile, installatore e release portano lo stesso numero per costruzione invece che per disciplina, e un rilascio non lascia piu' una modifica al progetto da committare dopo il fatto. Se HEAD non ha un tag la catena lo crea da se' — numero chiesto o minor successiva alla piu' alta esistente — ma lo fa in fondo, a installatore prodotto: un giro fallito non lascia dietro il tag di una versione mai costruita. Rifiuta di taggare un albero con modifiche non committate, perche' sarebbe un tag che non indica niente di ricostruibile. La configurazione di Gitea viene letta prima di compilare: senza token ci si ferma in due secondi invece che dopo tre minuti di build. Co-Authored-By: Claude Opus 5 --- Mimante/.vscode/tasks.json | 6 +- Mimante/build/Release.proj | 285 ++++++++++++++++++++++++------------- 2 files changed, 189 insertions(+), 102 deletions(-) diff --git a/Mimante/.vscode/tasks.json b/Mimante/.vscode/tasks.json index d36b62c..42ab7e3 100644 --- a/Mimante/.vscode/tasks.json +++ b/Mimante/.vscode/tasks.json @@ -68,7 +68,7 @@ }, { "label": "crea installatore", - "detail": "Chiede la versione, verifica, pubblica ed esegue Inno Setup: bin/installer/AutoBidder--setup.exe. Non tocca Gitea.", + "detail": "Verifica, pubblica ed esegue Inno Setup: bin/installer/AutoBidder--setup.exe. Crea il tag a pacchetto pronto. Non tocca Gitea.", "type": "process", "command": "dotnet", "args": [ @@ -101,7 +101,7 @@ }, { "label": "rilascia su Gitea", - "detail": "Chiede versione e note, poi: verifica, pubblica, installatore, tag e release su Gitea con i file allegati. Richiede build/gitea.json.", + "detail": "Verifica, pubblica, installatore, tag e release su Gitea con i file allegati. La versione viene dal tag su HEAD. Richiede build/gitea.json.", "type": "process", "command": "dotnet", "args": [ @@ -121,7 +121,7 @@ { "id": "versione", "type": "promptString", - "description": "Versione (vuoto = incrementa la minor, es. 4.11.0 → 4.12.0)", + "description": "Versione — lascia vuoto se hai già taggato (git tag v4.14.0), o per la minor successiva", "default": "" }, { diff --git a/Mimante/build/Release.proj b/Mimante/build/Release.proj index 2c8fcec..17fdda0 100644 --- a/Mimante/build/Release.proj +++ b/Mimante/build/Release.proj @@ -18,12 +18,24 @@ parlare con Gitea) sta in attività C# in linea: si legge come codice, non come una successione di comandi. - ── Chi chiede la versione ─────────────────────────────────────────────── - MSBuild non può chiedere niente a nessuno: è un motore di compilazione, non - un programma interattivo. La domanda la fa l'attività di VS Code, che passa - qui la risposta in `-p:Versione=`. Lasciandola vuota si prende la minor - successiva a quella scritta nel .csproj — che è il caso normale, e infatti è - il predefinito del prompt. + ── Da dove viene la versione ──────────────────────────────────────────── + Dal tag git, e da nient'altro. Il .csproj non viene mai riscritto: il numero + arriva a `dotnet publish` come proprietà da riga di comando, quindi tag, + eseguibile, installatore e release portano lo stesso numero per costruzione, + non per disciplina. + + Il modo previsto è taggare e poi rilasciare: + + git tag v4.14.0 + dotnet msbuild build/Release.proj -t:Rilascia + + Se HEAD non ha un tag di versione la catena lo crea da sé — con il numero + passato in `-p:Versione=`, oppure la minor successiva all'ultimo tag — e lo + fa in fondo, quando l'installatore esiste davvero. Un giro andato male non + lascia dietro un tag per una versione che non è mai stata costruita. + + Il numero nel .csproj resta quello delle compilazioni di sviluppo, e non + c'entra più con i rilasci. --> @@ -51,7 +63,8 @@ alterni, senza che il codice fosse cambiato. --> $([System.IO.Path]::GetTempPath())AutoBidder.Verifica - + @@ -74,84 +87,129 @@ false false false + + + false - + - + + - - - + + + + + ([^<]+)"); - if (!m.Success) { Log.LogError(" non trovato in " + Csproj); return false; } + // ConsoleToMSBuild unisce le righe con il punto e virgola; git le separa + // con a-capo. Si accettano entrambi e non si fanno domande. + Func spezza = s => + (s ?? "").Split(new[] { ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - Attuale = m.Groups[1].Value.Trim(); + var forma = new Regex(@"^v(\d+)\.(\d+)\.(\d+)$"); - // Rilettura secca: serve solo a sapere cosa c'e' scritto adesso, senza - // proporre incrementi e senza scrivere una riga di registro fuorviante. - if (SoloLettura) { Effettiva = Attuale; return true; } + var suHead = new List(); + foreach (var t in spezza(TagsHead)) + if (forma.IsMatch(t.Trim())) suHead.Add(t.Trim()); + + if (suHead.Count > 1) + { + Log.LogError( + "HEAD ha piu' di un tag di versione (" + string.Join(", ", suHead) + ").\n" + + "Non si puo' sapere quale sia il rilascio: tienine uno solo con git tag -d "); + return false; + } var chiesta = (Richiesta ?? "").Trim(); - if (chiesta.Length > 0) + if (chiesta.Length > 0 && !Regex.IsMatch(chiesta, @"^\d+\.\d+\.\d+$")) { - // Una versione scritta a mano si accetta solo se ha la forma giusta: - // un refuso qui produrrebbe un tag e un pacchetto sbagliati. - if (!Regex.IsMatch(chiesta, @"^\d+\.\d+\.\d+$")) + // Un refuso qui produrrebbe un tag e un pacchetto sbagliati. + Log.LogError("Versione '" + chiesta + "' non valida: serve la forma X.Y.Z"); + return false; + } + + if (suHead.Count == 1) + { + var dalTag = suHead[0].Substring(1); + + if (chiesta.Length > 0 && chiesta != dalTag) { - Log.LogError("Versione '" + chiesta + "' non valida: serve la forma X.Y.Z"); + Log.LogError( + "HEAD e' gia' taggato " + suHead[0] + ", ma e' stata chiesta la versione " + chiesta + ".\n" + + "Il tag e' la fonte: o rilasci " + dalTag + " lasciando vuota la versione,\n" + + "oppure togli il tag con git tag -d " + suHead[0] + " e rilancia."); return false; } - Effettiva = chiesta; + + Versione = dalTag; + Tag = suHead[0]; + DaCreare = "false"; + Log.LogMessage(MessageImportance.High, " versione " + Versione + " — dal tag " + Tag + " su HEAD"); + return true; + } + + if (chiesta.Length > 0) + { + Versione = chiesta; } else { - // Vuota: la minor successiva. E' il caso normale di una sessione di lavoro. - var p = Attuale.Split('.'); - if (p.Length < 2) { Log.LogError("Versione attuale illeggibile: " + Attuale); return false; } - Effettiva = p[0] + "." + (int.Parse(p[1]) + 1) + ".0"; + // La minor successiva al tag piu' alto: cosi' il numero proposto e' + // sempre libero, anche se il tag piu' alto sta su un altro ramo. + int maggiore = -1, minore = -1; + foreach (var t in spezza(TagsTutti)) + { + var m = forma.Match(t.Trim()); + if (!m.Success) continue; + + int ma = int.Parse(m.Groups[1].Value), mi = int.Parse(m.Groups[2].Value); + if (ma > maggiore || (ma == maggiore && mi > minore)) { maggiore = ma; minore = mi; } + } + + if (maggiore < 0) + { + // Primo rilascio: non c'e' nessun tag da cui ripartire, e l'unico + // numero che esiste e' quello delle compilazioni di sviluppo. + var testo = File.Exists(Csproj) ? File.ReadAllText(Csproj) : ""; + var m = Regex.Match(testo, @"(\d+)\.(\d+)\.\d+"); + if (!m.Success) + { + Log.LogError( + "Nessun tag di versione nel repository e illeggibile in " + Csproj + ".\n" + + "Indica la versione con -p:Versione=X.Y.Z"); + return false; + } + + maggiore = int.Parse(m.Groups[1].Value); + minore = int.Parse(m.Groups[2].Value); + } + + Versione = maggiore + "." + (minore + 1) + ".0"; } - Log.LogMessage(MessageImportance.High, " versione: " + Attuale + " -> " + Effettiva); - ]]> - - - - - - - - - - - - - - - [^<]+", "" + Versione + ""); - testo = Regex.Replace(testo, @"[^<]+", "" + Versione + ".0"); - testo = Regex.Replace(testo, @"[^<]+", "" + Versione + ".0"); - testo = Regex.Replace(testo, @"[^<]+", "" + Versione + ""); - - File.WriteAllText(Csproj, testo); - Log.LogMessage(MessageImportance.High, " .csproj aggiornato a " + Versione); + Tag = "v" + Versione; + DaCreare = "true"; + Log.LogMessage(MessageImportance.High, " versione " + Versione + " — il tag " + Tag + " sara' creato a pacchetto pronto"); ]]> @@ -238,7 +296,8 @@ "Configurazione di Gitea incompleta. Servono url, owner, repo, token:\n" + " copia build/gitea.example.json in build/gitea.json e riempilo,\n" + " oppure imposta GITEA_URL, GITEA_OWNER, GITEA_REPO, GITEA_TOKEN.\n" + - "Il pacchetto e' comunque pronto in bin/installer."); + "Non e' stato costruito niente: si controlla prima di compilare, non dopo.\n" + + "Per il solo installatore, senza Gitea, usa il target Pacchetto."); // Senza questo il target prosegue lo stesso: git tag, poi curl con // l'indirizzo vuoto, e infine un "codice 3" che non dice niente a @@ -316,7 +375,7 @@ - + - - + + + + Command="dotnet publish "$(Csproj)" -c Release -r win-x64 --nologo -v q --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -p:PublishReadyToRun=true -p:PublishTrimmed=false -p:Version=$(V) -p:AssemblyVersion=$(V).0 -p:FileVersion=$(V).0 -p:InformationalVersion=$(V) -o "$(CartellaPubblicazione)"" /> @@ -366,26 +432,17 @@ - - - - - + - - - - - @@ -393,47 +450,79 @@ + Command=""$(Iscc)" /Qp "/DAppVersion=$(V)" "/DSourceExe=$(CartellaPubblicazione)\AutoBidder.exe" "/DOutputDir=$(CartellaPacchetti)" "$(Iss)"" /> - $(CartellaPacchetti)\AutoBidder-$(VersioneEffettiva)-setup.exe + $(CartellaPacchetti)\AutoBidder-$(V)-setup.exe + + + - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + - v$(V) $(GUrl)/api/v1/repos/$(GOwner)/$(GRepo) $([System.IO.Path]::GetTempPath())AutoBidder.Rilascio $(Tmp)\curl.cfg @@ -448,11 +537,9 @@ - - +