From 99b3030180ac2bf75a3ff1527f6729eab62f027b Mon Sep 17 00:00:00 2001 From: Alberto Balbo Date: Tue, 4 Aug 2026 22:15:53 +0200 Subject: [PATCH] Porta la catena di rilascio nel repository ufficiale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il progetto viveva in una cartella senza alcun commit: tutto il lavoro stava solo sul disco. Trasferito qui, dove esiste una storia e un remoto. Mancava soltanto build/: i sorgenti erano gia' allineati e identici. Nessun percorso e' stato toccato — Release.proj usa MSBuildThisFileDirectory e tasks.json usa ${workspaceFolder}, quindi il trasloco non li riguarda. - build/Release.proj: verifica, backtest, pacchetto e rilascio in un solo file MSBuild, senza script. Provato dalla nuova posizione: 287 test verdi, installatore prodotto, backtest eseguito. - build/gitea.example.json: url, owner e repo veri; resta da mettere il token in build/gitea.json, che e' ignorato da git. - build/sostituiti/: i quattro .ps1 rimpiazzati da Release.proj. Entrano qui solo per non perderli con la vecchia cartella: da ora sono recuperabili dalla storia e si possono cancellare. - .gitignore: gitea.json (contiene una credenziale), i riepiloghi del backtest e i pacchetti in bin/installer. Versione 4.12.0 -> 4.13.0. Co-Authored-By: Claude Opus 5 --- .gitignore | 11 + Mimante/AutoBidder.csproj | 8 +- Mimante/build/AutoBidder.iss | 123 ++++++ Mimante/build/README.md | 87 +++++ Mimante/build/Release.proj | 513 ++++++++++++++++++++++++++ Mimante/build/gitea.example.json | 10 + Mimante/build/sostituiti/backtest.ps1 | 83 +++++ Mimante/build/sostituiti/pubblica.ps1 | 36 ++ Mimante/build/sostituiti/rilascia.ps1 | 284 ++++++++++++++ Mimante/build/sostituiti/verifica.ps1 | 40 ++ 10 files changed, 1191 insertions(+), 4 deletions(-) create mode 100644 Mimante/build/AutoBidder.iss create mode 100644 Mimante/build/README.md create mode 100644 Mimante/build/Release.proj create mode 100644 Mimante/build/gitea.example.json create mode 100644 Mimante/build/sostituiti/backtest.ps1 create mode 100644 Mimante/build/sostituiti/pubblica.ps1 create mode 100644 Mimante/build/sostituiti/rilascia.ps1 create mode 100644 Mimante/build/sostituiti/verifica.ps1 diff --git a/.gitignore b/.gitignore index 7e2e97c..239e675 100644 --- a/.gitignore +++ b/.gitignore @@ -414,3 +414,14 @@ FodyWeavers.xsd # Built Visual Studio Code Extensions *.vsix + +# ---> AutoBidder / Mimante + +# Configurazione di rilascio: contiene un token di Gitea +gitea.json + +# Riepilogo della rigiocata sui dossier (target Backtest) +*backtest-report.txt + +# Pacchetti prodotti da build/Release.proj +bin/installer/ diff --git a/Mimante/AutoBidder.csproj b/Mimante/AutoBidder.csproj index d12a269..b5f8e31 100644 --- a/Mimante/AutoBidder.csproj +++ b/Mimante/AutoBidder.csproj @@ -17,10 +17,10 @@ WebView2 runtime è preinstallato su Windows 11. --> - 4.12.0 - 4.12.0.0 - 4.12.0.0 - 4.12.0 + 4.13.0 + 4.13.0.0 + 4.13.0.0 + 4.13.0 win-x64 diff --git a/Mimante/build/AutoBidder.iss b/Mimante/build/AutoBidder.iss new file mode 100644 index 0000000..e780581 --- /dev/null +++ b/Mimante/build/AutoBidder.iss @@ -0,0 +1,123 @@ +; ───────────────────────────────────────────────────────────────────────────── +; AutoBidder — script di installazione (Inno Setup 6) +; +; Non si compila a mano: lo lancia build/Release.proj, che prima pubblica +; l'eseguibile e poi passa qui versione e percorsi con /D. Compilarlo da solo +; produrrebbe un pacchetto con la versione sbagliata, perché il numero vive +; nel .csproj. +; +; dotnet msbuild build/Release.proj -t:Pacchetto +; +; L'applicazione è un unico .exe self-contained: l'installazione copia quel file, +; crea i collegamenti e registra la voce per "App installate". I dati dell'utente +; stanno in %LocalAppData%\AutoBidder e NON vengono toccati né dall'aggiornamento +; né dalla disinstallazione — sono aste, statistiche e sessione, cioè mesi di +; raccolta che nessun disinstallatore ha il diritto di buttare via. +; ───────────────────────────────────────────────────────────────────────────── + +#ifndef AppVersion + #define AppVersion "0.0.0" +#endif + +#ifndef SourceExe + #define SourceExe "..\bin\publish\win-x64\AutoBidder.exe" +#endif + +#ifndef OutputDir + #define OutputDir "..\bin\installer" +#endif + +#define AppName "AutoBidder" +#define AppPublisher "Alberto Balbo" +#define AppExeName "AutoBidder.exe" + +[Setup] +; L'AppId identifica il prodotto fra una versione e l'altra: cambiarlo farebbe +; comparire due voci in "App installate" invece di un aggiornamento. +AppId={{9F2C6E14-8B3D-4A57-9E10-6C4B0D7A1F52} +AppName={#AppName} +AppVersion={#AppVersion} +AppVerName={#AppName} {#AppVersion} +AppPublisher={#AppPublisher} +VersionInfoVersion={#AppVersion} + +; Installazione per utente: niente richiesta di amministratore, e i dati stanno +; comunque nel profilo di chi la usa. +PrivilegesRequired=lowest +DefaultDirName={autopf}\{#AppName} +DefaultGroupName={#AppName} +DisableProgramGroupPage=yes +DisableDirPage=auto + +OutputDir={#OutputDir} +OutputBaseFilename=AutoBidder-{#AppVersion}-setup +SetupIconFile=..\Icon\favicon.ico +UninstallDisplayIcon={app}\{#AppExeName} +UninstallDisplayName={#AppName} {#AppVersion} + +; L'eseguibile è già compresso (single-file compresso): comprimerlo di nuovo +; costa minuti di build e guadagna poco. +Compression=lzma2/normal +SolidCompression=no +WizardStyle=modern +ArchitecturesAllowed=x64compatible +ArchitecturesInstallIn64BitMode=x64compatible + +[Languages] +Name: "italiano"; MessagesFile: "compiler:Languages\Italian.isl" + +[Tasks] +Name: "desktopicon"; Description: "Crea un collegamento sul desktop"; GroupDescription: "Collegamenti:" +Name: "avvioautomatico"; Description: "Avvia AutoBidder all'accesso a Windows"; GroupDescription: "Avvio:"; Flags: unchecked + +[Files] +Source: "{#SourceExe}"; DestDir: "{app}"; Flags: ignoreversion + +[Icons] +Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}" +Name: "{group}\Disinstalla {#AppName}"; Filename: "{uninstallexe}" +Name: "{autodesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: desktopicon + +[Registry] +Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Run"; \ + ValueType: string; ValueName: "{#AppName}"; ValueData: """{app}\{#AppExeName}"""; \ + Flags: uninsdeletevalue; Tasks: avvioautomatico + +[Run] +Filename: "{app}\{#AppExeName}"; Description: "Avvia {#AppName}"; \ + Flags: nowait postinstall skipifsilent + +[UninstallDelete] +; Solo quello che ha creato l'installazione. I dati restano. +Type: dirifempty; Name: "{app}" + +[Code] +{ Un'installazione sopra una copia in esecuzione lascerebbe il vecchio eseguibile + al suo posto senza dirlo, e l'utente si ritroverebbe la versione precedente + convinto di aver aggiornato. Meglio fermarsi e chiedere. } +function IsAppRunning(): Boolean; +var + ResultCode: Integer; +begin + Result := Exec('cmd.exe', + '/C tasklist /FI "IMAGENAME eq {#AppExeName}" | find /I "{#AppExeName}"', + '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0); +end; + +function InitializeSetup(): Boolean; +begin + Result := True; + + if IsAppRunning() then + begin + if MsgBox('AutoBidder è in esecuzione e sta forse seguendo delle aste.' + #13#10#13#10 + + 'Chiudilo prima di continuare, altrimenti il file non può essere sostituito.' + #13#10#13#10 + + 'Riprovo?', mbConfirmation, MB_YESNO) = IDYES then + Result := not IsAppRunning() + else + Result := False; + + if not Result then + MsgBox('Installazione annullata: AutoBidder è ancora aperto.', mbInformation, MB_OK); + end; +end; diff --git a/Mimante/build/README.md b/Mimante/build/README.md new file mode 100644 index 0000000..e1f34fc --- /dev/null +++ b/Mimante/build/README.md @@ -0,0 +1,87 @@ +# Catena di verifica, pacchetto e rilascio + +Tutto quello che serve a controllare, impacchettare e pubblicare AutoBidder sta in questa +cartella. La radice del progetto non contiene script. + +| File | Cos'è | +|---|---| +| `Release.proj` | La catena. Un solo file MSBuild, nessuno script. | +| `AutoBidder.iss` | Lo script di Inno Setup. Non si compila a mano: lo lancia `Release.proj`. | +| `gitea.example.json` | Modello per `gitea.json` (che è escluso dal controllo di versione). | +| `sostituiti/` | I vecchi script PowerShell, tenuti finché non si fa la pulizia. Non servono più. | + +## Da VS Code + +**Terminale ▸ Esegui attività…** + +| Attività | Cosa fa | +|---|---| +| `verifica` | Compila e lancia i test. | +| `backtest` | Rigioca i dossier delle aste concluse. | +| `backtest (giro veloce, 400 aste)` | Come sopra, su un campione. | +| `crea installatore` | Chiede la versione, verifica, pubblica, esegue Inno Setup. | +| `crea installatore (senza rieseguire i test)` | Solo pubblicazione e installatore. | +| `rilascia su Gitea` | Tutto quanto sopra, più tag e release con i file allegati. | + +## Da riga di comando + +```powershell +dotnet msbuild build/Release.proj -t:Verifica +dotnet msbuild build/Release.proj -t:Backtest -p:MaxDossier=400 +dotnet msbuild build/Release.proj -t:Pacchetto +dotnet msbuild build/Release.proj -t:Rilascia -p:Versione=4.13.0 -p:Note="Cosa cambia" +``` + +| Proprietà | Predefinito | A cosa serve | +|---|---|---| +| `Versione` | vuoto | Vuoto = incrementa la minor (4.12.0 → 4.13.0). Altrimenti la scrive, se ha la forma `X.Y.Z`. | +| `Note` | vuoto | Note di rilascio. | +| `SaltaVerifica` | `false` | Non rieseguire i test. | +| `Sovrascrivi` | `false` | Sostituisci una release Gitea con lo stesso tag. | +| `Bozza` | `false` | Crea la release come bozza. | +| `MaxDossier` | `0` | Quanti dossier leggere nel backtest (0 = tutti). | +| `CartellaDossier` | `%LocalAppData%\AutoBidder\Dati\Registri\Aste` | Dove stanno i dossier. | + +## Chi chiede la versione + +MSBuild non può chiedere niente a nessuno: è un motore di compilazione. La domanda la fa +l'attività di VS Code (`inputs` in `.vscode/tasks.json`) e passa la risposta in +`-p:Versione=`. Lasciando il campo vuoto si prende la minor successiva, che è il caso +normale di fine sessione. + +**La versione si scrive solo dopo che i test sono passati.** Il contrario sembra più +naturale — decidi il numero, poi costruisci — ma lascia il `.csproj` alzato quando la +verifica fallisce, e il tentativo dopo riparte da lì: il numero sale senza che sia mai +esistito un pacchetto con quella versione. + +## Gitea + +Servono quattro valori. Le variabili d'ambiente hanno la precedenza sul file, così una +macchina condivisa può rilasciare senza scrivere un token su disco: + +- `GITEA_URL`, `GITEA_OWNER`, `GITEA_REPO`, `GITEA_TOKEN` +- oppure `build/gitea.json`, copiato da `gitea.example.json` + +Il token si crea in Gitea da *Impostazioni ▸ Applicazioni ▸ Genera nuovo token*, con il +permesso `repository: read and write`. + +Nella release vengono caricati **sia l'installatore sia l'eseguibile nudo**: chi non vuole +installare niente deve continuare a poter scaricare il solo `.exe`. + +## Una trappola già pagata + +`dotnet test` e `dotnet publish` lanciati **da dentro** MSBuild ereditano l'ambiente del +processo padre. La compilazione WPF crea un progetto temporaneo (`_wpftmp.csproj`) e con +`MSBUILD_EXE_PATH` puntata al build in corso non genera più le classi parziali dello XAML: +si ottengono decine di *«AuctionMonitorControl non contiene una definizione di …»* che non +hanno niente a che vedere col codice. + +Per questo gli `Exec` azzerano `MSBUILD_EXE_PATH` e `MSBuildLoadMicrosoftTargetsReadOnly`. +**Solo quelle due**: la ricetta che gira in rete azzera anche `MSBuildExtensionsPath` e +`MSBuildSDKsPath`, e così il figlio perde la posizione dell'SDK — *«l'SDK Microsoft.NET.Sdk +specificato non è stato trovato»*. Serve isolare il motore, non nascondergli dove abita. + +## Prerequisiti + +- .NET SDK 10 +- [Inno Setup 6](https://jrsoftware.org/isinfo.php) — `winget install -e --id JRSoftware.InnoSetup` diff --git a/Mimante/build/Release.proj b/Mimante/build/Release.proj new file mode 100644 index 0000000..2c8fcec --- /dev/null +++ b/Mimante/build/Release.proj @@ -0,0 +1,513 @@ + + + + + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..')) + $(Radice)\AutoBidder.csproj + $(Radice)\Tests\AutoBidder.Tests.csproj + $(MSBuildThisFileDirectory)AutoBidder.iss + + $(Radice)\bin\publish\win-x64 + $(Radice)\bin\installer + + + $([System.IO.Path]::GetTempPath())AutoBidder.Verifica + + + + + + + + + MSBUILD_EXE_PATH=;MSBuildLoadMicrosoftTargetsReadOnly= + + false + false + false + + + + + + + + + + + + + + + + + + ([^<]+)"); + if (!m.Success) { Log.LogError(" non trovato in " + Csproj); return false; } + + Attuale = m.Groups[1].Value.Trim(); + + // 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 chiesta = (Richiesta ?? "").Trim(); + if (chiesta.Length > 0) + { + // 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+$")) + { + Log.LogError("Versione '" + chiesta + "' non valida: serve la forma X.Y.Z"); + return false; + } + Effettiva = 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"; + } + + 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); + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + leggi = (env, campo) => + { + var v = Environment.GetEnvironmentVariable(env); + if (!string.IsNullOrWhiteSpace(v)) return v.Trim(); + + var m = Regex.Match(testo, "\"" + campo + "\"\\s*:\\s*\"([^\"]*)\""); + return m.Success ? m.Groups[1].Value.Trim() : ""; + }; + + Url = leggi("GITEA_URL", "url").TrimEnd('/'); + Owner = leggi("GITEA_OWNER", "owner"); + Repo = leggi("GITEA_REPO", "repo"); + Token = leggi("GITEA_TOKEN", "token"); + + if (Url.Length == 0 || Owner.Length == 0 || Repo.Length == 0 || Token.Length == 0) + { + Log.LogError( + "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."); + + // 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 + // nessuno. Un errore va fermato dove si capisce ancora cos'era. + return false; + } + ]]> + + + + + + + + + + + + + + + + + esc = t => (t ?? "") + .Replace("\\", "\\\\").Replace("\"", "\\\"") + .Replace("\r", "").Replace("\n", "\\n").Replace("\t", " "); + + var note = string.IsNullOrWhiteSpace(Note) ? "Versione " + Versione + "." : Note; + + File.WriteAllText(Destinazione, + "{\"tag_name\":\"" + esc(Tag) + "\"," + + "\"name\":\"AutoBidder " + esc(Versione) + "\"," + + "\"body\":\"" + esc(note) + "\"," + + "\"draft\":" + (Bozza ? "true" : "false") + "," + + "\"prerelease\":false}"); + ]]> + + + + + + + + + + + + + + + + 200 ? testo.Substring(0, 200) : testo); + } + ]]> + + + + + + + + + + + + + + + + + + + + $(LOCALAPPDATA)\AutoBidder\Dati\Registri\Aste + 0 + $(Radice)\bin\backtest-report.txt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(CartellaPacchetti)\AutoBidder-$(VersioneEffettiva)-setup.exe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + v$(V) + $(GUrl)/api/v1/repos/$(GOwner)/$(GRepo) + $([System.IO.Path]::GetTempPath())AutoBidder.Rilascio + $(Tmp)\curl.cfg + $(Tmp)\release.json + $(Tmp)\risposta.json + $(CartellaPacchetti)\AutoBidder-$(V)-setup.exe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mimante/build/gitea.example.json b/Mimante/build/gitea.example.json new file mode 100644 index 0000000..a6427e2 --- /dev/null +++ b/Mimante/build/gitea.example.json @@ -0,0 +1,10 @@ +{ + "_commento": "Copia questo file in gitea.json e metti il token. gitea.json e' escluso dal controllo di versione perche' contiene una credenziale. In alternativa usa le variabili d'ambiente GITEA_URL, GITEA_OWNER, GITEA_REPO, GITEA_TOKEN, che hanno la precedenza su questo file.", + + "url": "http://192.168.30.23:3000", + "owner": "Alby96", + "repo": "Mimante", + + "_token": "Gitea > Impostazioni > Applicazioni > Genera nuovo token, permesso 'repository: read and write'.", + "token": "INSERISCI_QUI_IL_TOKEN" +} diff --git a/Mimante/build/sostituiti/backtest.ps1 b/Mimante/build/sostituiti/backtest.ps1 new file mode 100644 index 0000000..c8f3c8c --- /dev/null +++ b/Mimante/build/sostituiti/backtest.ps1 @@ -0,0 +1,83 @@ +# Rigioca i dossier delle aste concluse e stampa quanto costerebbe ciascun anticipo. +# +# powershell -ExecutionPolicy Bypass -File .\backtest.ps1 +# powershell -ExecutionPolicy Bypass -File .\backtest.ps1 -Max 200 +# +# Il motore punta solo nei cicli che arrivano fino all'anticipo senza che nessun altro +# abbia puntato: la rigiocata conta quei cicli, quindi dice quante puntate si +# spenderebbero e quante ne bloccherebbero le strategie. +# +# NON dice se avresti vinto: una nostra puntata rimette in gioco l'asta, e la reazione +# degli avversari non e' registrata da nessuna parte. + +[CmdletBinding()] +param( + # Cartella dei dossier. Vuota = quella in uso dall'applicazione. + [string] $Cartella = "", + + # Quanti dossier al massimo (0 = tutti). I file sono grossi: per un giro veloce, 200. + [int] $Max = 0, + + # Dove scrivere il riepilogo. + [string] $Report = "" +) + +$ErrorActionPreference = 'Stop' +Set-Location $PSScriptRoot + +if (-not $Cartella) { + # Stessa struttura di Utilities/AppPaths: Dati\Registri\Aste sotto %LocalAppData%. + $Cartella = Join-Path $env:LOCALAPPDATA "AutoBidder\Dati\Registri\Aste" +} + +if (-not (Test-Path $Cartella)) { + Write-Host "Cartella dei dossier non trovata: $Cartella" -ForegroundColor Red + Write-Host "Indicala con -Cartella, oppure accendi 'Dossier per ogni asta' nelle Impostazioni." -ForegroundColor Yellow + exit 1 +} + +$conteggio = (Get-ChildItem -Path $Cartella -Filter *.jsonl -File | Measure-Object).Count +if ($conteggio -eq 0) { + Write-Host "Nessun dossier (*.jsonl) in $Cartella" -ForegroundColor Red + exit 1 +} + +if (-not $Report) { + $Report = Join-Path $PSScriptRoot "backtest-report.txt" +} + +Write-Host "== Rigiocata sui dossier ==" -ForegroundColor Cyan +Write-Host " cartella : $Cartella" +Write-Host " dossier : $conteggio$(if ($Max -gt 0) { " (ne verranno letti $Max)" })" +Write-Host " riepilogo: $Report" +Write-Host "" + +$env:AUTOBIDDER_BACKTEST_DIR = $Cartella +$env:AUTOBIDDER_BACKTEST_MAX = "$Max" +$env:AUTOBIDDER_BACKTEST_OUT = $Report + +# L'applicazione potrebbe essere aperta e tenere bloccato AutoBidder.exe: si compila +# in una cartella a parte, come fa la verifica. +$out = Join-Path ([System.IO.Path]::GetTempPath()) "AutoBidder.Backtest" + +& dotnet test Tests\AutoBidder.Tests.csproj --nologo -v q -o $out ` + --filter "FullyQualifiedName~RealDossierBacktest" ` + --logger "console;verbosity=detailed" 2>&1 | + Where-Object { $_ -notmatch '^\s*$' } | + ForEach-Object { Write-Host $_ } + +$esito = $LASTEXITCODE + +Write-Host "" +if (Test-Path $Report) { + Write-Host "== Riepilogo ==" -ForegroundColor Cyan + Get-Content $Report | ForEach-Object { Write-Host $_ } +} + +if ($esito -ne 0) { + Write-Host "`nLa rigiocata ha trovato dei problemi (vedi sopra)." -ForegroundColor Red + exit 1 +} + +Write-Host "`nRigiocata completata." -ForegroundColor Green +exit 0 diff --git a/Mimante/build/sostituiti/pubblica.ps1 b/Mimante/build/sostituiti/pubblica.ps1 new file mode 100644 index 0000000..cb755b2 --- /dev/null +++ b/Mimante/build/sostituiti/pubblica.ps1 @@ -0,0 +1,36 @@ +<# + pubblica.ps1 - Crea AutoBidder come UNICO file .exe self-contained per Windows x64. + + Uso: + powershell -ExecutionPolicy Bypass -File .\pubblica.ps1 + + Risultato: + bin\publish\win-x64\AutoBidder.exe (doppio click per avviare, nessuna installazione) +#> + +$ErrorActionPreference = "Stop" +$here = Split-Path -Parent $MyInvocation.MyCommand.Definition +Set-Location $here + +Write-Host "==> Pubblicazione AutoBidder (self-contained, single-file, win-x64)..." -ForegroundColor Cyan + +dotnet publish "AutoBidder.csproj" -c Release -r win-x64 ` + --self-contained true ` + -p:PublishSingleFile=true ` + -p:IncludeNativeLibrariesForSelfExtract=true ` + -p:EnableCompressionInSingleFile=true ` + -p:PublishReadyToRun=true ` + -p:PublishTrimmed=false ` + -o "bin\publish\win-x64" + +$exe = Join-Path $here "bin\publish\win-x64\AutoBidder.exe" +if (Test-Path $exe) { + $sizeMB = [math]::Round((Get-Item $exe).Length / 1MB, 1) + Write-Host "" + Write-Host "==> FATTO. Eseguibile pronto:" -ForegroundColor Green + Write-Host " $exe ($sizeMB MB)" -ForegroundColor Green + Write-Host " Fai doppio click per avviare. Nessun software da installare." -ForegroundColor Green +} else { + Write-Host "==> ERRORE: eseguibile non trovato." -ForegroundColor Red + exit 1 +} diff --git a/Mimante/build/sostituiti/rilascia.ps1 b/Mimante/build/sostituiti/rilascia.ps1 new file mode 100644 index 0000000..1096e4e --- /dev/null +++ b/Mimante/build/sostituiti/rilascia.ps1 @@ -0,0 +1,284 @@ +<# + rilascia.ps1 — dalla sorgente al pacchetto pubblicato su Gitea, in un comando. + + Passi: + 1. verifica.ps1 (compila + test) — saltabile con -SaltaVerifica + 2. pubblica l'eseguibile self-contained + 3. compila l'installatore con Inno Setup + 4. crea il tag, la release su Gitea e carica i file + + Uso: + powershell -ExecutionPolicy Bypass -File .\rilascia.ps1 + powershell -ExecutionPolicy Bypass -File .\rilascia.ps1 -SoloPacchetto + powershell -ExecutionPolicy Bypass -File .\rilascia.ps1 -Note "Cosa cambia" + + Configurazione di Gitea (una volta sola): copia gitea.example.json in + gitea.json e riempilo. Il file è escluso dal controllo di versione perché + contiene un token. In alternativa, le variabili d'ambiente + GITEA_URL / GITEA_OWNER / GITEA_REPO / GITEA_TOKEN hanno la precedenza. +#> + +[CmdletBinding()] +param( + # Ferma dopo aver creato l'installatore, senza toccare Gitea. + [switch] $SoloPacchetto, + + # Salta compilazione e test. Da usare solo se li hai appena eseguiti. + [switch] $SaltaVerifica, + + # Testo delle note di rilascio. Vuoto = generate dai commit dall'ultimo tag. + [string] $Note = "", + + # Contrassegna la release come bozza o come anteprima. + [switch] $Bozza, + [switch] $Anteprima, + + # Sovrascrive una release con lo stesso tag invece di fermarsi. + [switch] $Sovrascrivi +) + +$ErrorActionPreference = 'Stop' +Set-Location $PSScriptRoot + +function Titolo($t) { Write-Host "`n== $t ==" -ForegroundColor Cyan } +function Ok($t) { Write-Host " $t" -ForegroundColor Green } +function Nota($t) { Write-Host " $t" -ForegroundColor DarkGray } +function Errore($t) { Write-Host "`n$t" -ForegroundColor Red } + +# ── 0. Versione ────────────────────────────────────────────────────────────── +# La versione ha una sola origine: il .csproj. Ripeterla qui vorrebbe dire +# vederle divergere al primo rilascio fatto di fretta. +[xml] $csproj = Get-Content .\AutoBidder.csproj +$versione = ($csproj.Project.PropertyGroup | Where-Object { $_.Version } | Select-Object -First 1).Version + +if (-not $versione) { + Errore "Versione non trovata in AutoBidder.csproj ()." + exit 1 +} + +$tag = "v$versione" +Titolo "AutoBidder $versione" + +# ── 1. Verifica ────────────────────────────────────────────────────────────── +if ($SaltaVerifica) { + Nota "verifica saltata su richiesta" +} else { + Titolo "Verifica (compilazione + test)" + & powershell -ExecutionPolicy Bypass -File .\verifica.ps1 + if ($LASTEXITCODE -ne 0) { + Errore "Verifica fallita: niente da rilasciare." + exit 1 + } +} + +# ── 2. Eseguibile ──────────────────────────────────────────────────────────── +Titolo "Pubblicazione dell'eseguibile" + +$pubDir = Join-Path $PSScriptRoot "bin\publish\win-x64" +$exe = Join-Path $pubDir "AutoBidder.exe" + +dotnet publish .\AutoBidder.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 ` + -o $pubDir + +if ($LASTEXITCODE -ne 0 -or -not (Test-Path $exe)) { + Errore "Pubblicazione fallita." + exit 1 +} + +Ok ("AutoBidder.exe ({0:N1} MB)" -f ((Get-Item $exe).Length / 1MB)) + +# La versione scritta nell'eseguibile deve corrispondere: se qualcuno cambia il +# .csproj senza ricompilare, il pacchetto porterebbe un numero e il programma un altro. +$versioneExe = (Get-Item $exe).VersionInfo.ProductVersion +if ($versioneExe -and -not $versioneExe.StartsWith($versione)) { + Errore "L'eseguibile dichiara $versioneExe ma il progetto dice $versione." + exit 1 +} + +# ── 3. Installatore ────────────────────────────────────────────────────────── +Titolo "Creazione dell'installatore" + +$iscc = @( + "$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe", + "$env:ProgramFiles\Inno Setup 6\ISCC.exe", + "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe" +) | Where-Object { Test-Path $_ } | Select-Object -First 1 + +if (-not $iscc) { + Errore "Inno Setup 6 non trovato." + Write-Host "Installalo con: winget install -e --id JRSoftware.InnoSetup" -ForegroundColor Yellow + exit 1 +} + +$outDir = Join-Path $PSScriptRoot "bin\installer" +New-Item -ItemType Directory -Force -Path $outDir | Out-Null + +& $iscc /Qp ` + "/DAppVersion=$versione" ` + "/DSourceExe=$exe" ` + "/DOutputDir=$outDir" ` + ".\installer\AutoBidder.iss" + +if ($LASTEXITCODE -ne 0) { + Errore "Inno Setup ha restituito un errore." + exit 1 +} + +$setup = Join-Path $outDir "AutoBidder-$versione-setup.exe" +if (-not (Test-Path $setup)) { + Errore "Installatore non trovato: $setup" + exit 1 +} + +Ok ("{0} ({1:N1} MB)" -f (Split-Path $setup -Leaf), ((Get-Item $setup).Length / 1MB)) + +if ($SoloPacchetto) { + Write-Host "`nPacchetto pronto in $outDir" -ForegroundColor Green + Write-Host "Gitea non è stato toccato (-SoloPacchetto)." -ForegroundColor DarkGray + exit 0 +} + +# ── 4. Gitea ───────────────────────────────────────────────────────────────── +Titolo "Pubblicazione su Gitea" + +$cfgFile = Join-Path $PSScriptRoot "gitea.json" +$cfg = if (Test-Path $cfgFile) { Get-Content $cfgFile -Raw | ConvertFrom-Json } else { $null } + +function Impostazione($nomeEnv, $campo) { + $v = [Environment]::GetEnvironmentVariable($nomeEnv) + if ($v) { return $v } + if ($cfg -and $cfg.PSObject.Properties.Name -contains $campo) { return $cfg.$campo } + return $null +} + +$url = Impostazione 'GITEA_URL' 'url' +$owner = Impostazione 'GITEA_OWNER' 'owner' +$repo = Impostazione 'GITEA_REPO' 'repo' +$token = Impostazione 'GITEA_TOKEN' 'token' + +if (-not $url -or -not $owner -or -not $repo -or -not $token) { + Errore "Configurazione di Gitea incompleta." + Write-Host @" +Servono quattro valori: url, owner, repo, token. + + 1. copia gitea.example.json in gitea.json e riempilo, oppure + 2. imposta GITEA_URL, GITEA_OWNER, GITEA_REPO, GITEA_TOKEN. + +Il token si crea in Gitea da Impostazioni > Applicazioni > Genera nuovo token, +con il permesso 'repository: read and write'. + +L'installatore è comunque pronto in: + $setup +"@ -ForegroundColor Yellow + exit 1 +} + +$url = $url.TrimEnd('/') +$api = "$url/api/v1/repos/$owner/$repo" +$headers = @{ Authorization = "token $token" } + +Nota "$url/$owner/$repo tag $tag" + +# Note di rilascio: se non le detta l'utente, si prendono dai commit. +if (-not $Note) { + $ultimo = & git describe --tags --abbrev=0 2>$null + $intervallo = if ($LASTEXITCODE -eq 0 -and $ultimo) { "$ultimo..HEAD" } else { "HEAD" } + $commit = & git log $intervallo --pretty=format:"- %s" --no-merges 2>$null + + $Note = if ($commit) { ($commit | Out-String).Trim() } else { "Versione $versione." } +} + +# Il tag deve esistere sul server: la release ci si appoggia. +$rami = & git rev-parse --git-dir 2>$null +if ($LASTEXITCODE -eq 0) { + $esiste = & git tag -l $tag + if (-not $esiste) { + & git tag -a $tag -m "AutoBidder $versione" + Ok "tag $tag creato" + } + + $remoto = & git remote 2>$null | Select-Object -First 1 + if ($remoto) { + & git push $remoto $tag 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { Ok "tag inviato a $remoto" } + else { Nota "tag non inviato (probabilmente c'era già)" } + } else { + Nota "nessun remoto git configurato: la release userà il tag come nome" + } +} + +# Release già presente? +$esistente = $null +try { + $esistente = Invoke-RestMethod -Uri "$api/releases/tags/$tag" -Headers $headers -Method Get +} catch { + # 404 = non c'è, ed è il caso normale. +} + +if ($esistente) { + if (-not $Sovrascrivi) { + Errore "La release $tag esiste già su Gitea." + Write-Host "Alza in AutoBidder.csproj, oppure rilancia con -Sovrascrivi." -ForegroundColor Yellow + exit 1 + } + + Nota "release $tag esistente: verrà sostituita" + Invoke-RestMethod -Uri "$api/releases/$($esistente.id)" -Headers $headers -Method Delete | Out-Null +} + +$corpo = @{ + tag_name = $tag + name = "AutoBidder $versione" + body = $Note + draft = [bool]$Bozza + prerelease = [bool]$Anteprima +} | ConvertTo-Json + +try { + $release = Invoke-RestMethod -Uri "$api/releases" -Headers $headers -Method Post ` + -ContentType 'application/json' -Body $corpo +} catch { + Errore "Creazione della release non riuscita: $($_.Exception.Message)" + if ($_.ErrorDetails.Message) { Write-Host $_.ErrorDetails.Message -ForegroundColor DarkGray } + Write-Host "`nL'installatore resta disponibile in:`n $setup" -ForegroundColor Yellow + exit 1 +} + +Ok "release creata" + +# ── 5. File allegati ───────────────────────────────────────────────────────── +# Si carica sia l'installatore sia l'eseguibile nudo: chi non vuole installare +# nulla continua a poter scaricare il solo .exe, che è come l'app è nata. +function Carica($percorso) { + $nome = Split-Path $percorso -Leaf + $uri = "$api/releases/$($release.id)/assets?name=$([uri]::EscapeDataString($nome))" + + try { + Invoke-RestMethod -Uri $uri -Headers $headers -Method Post -Form @{ + attachment = Get-Item $percorso + } | Out-Null + Ok ("caricato {0} ({1:N1} MB)" -f $nome, ((Get-Item $percorso).Length / 1MB)) + return $true + } catch { + Errore "Caricamento di $nome non riuscito: $($_.Exception.Message)" + if ($_.ErrorDetails.Message) { Write-Host $_.ErrorDetails.Message -ForegroundColor DarkGray } + return $false + } +} + +$tuttoOk = (Carica $setup) -and (Carica $exe) + +if (-not $tuttoOk) { + Errore "Release creata ma senza tutti i file. Caricali a mano da $url/$owner/$repo/releases" + exit 1 +} + +Write-Host "`nRilascio completato." -ForegroundColor Green +Write-Host " $url/$owner/$repo/releases/tag/$tag" -ForegroundColor Green +exit 0 diff --git a/Mimante/build/sostituiti/verifica.ps1 b/Mimante/build/sostituiti/verifica.ps1 new file mode 100644 index 0000000..541e656 --- /dev/null +++ b/Mimante/build/sostituiti/verifica.ps1 @@ -0,0 +1,40 @@ +# Verifica rapida: compila tutto e lancia i test. +# Da eseguire dopo ogni modifica, prima di avviare l'applicazione. +# +# powershell -ExecutionPolicy Bypass -File .\verifica.ps1 +# +# Restituisce 0 se tutto è a posto, 1 altrimenti: utilizzabile in un hook. + +$ErrorActionPreference = 'Continue' +Set-Location $PSScriptRoot + +Write-Host "== Compilazione ==" -ForegroundColor Cyan +$build = & dotnet build AutoBidder.sln --nologo -v q 2>&1 +$buildOk = $LASTEXITCODE -eq 0 + +$problemi = $build | Select-String -Pattern 'error|warning CS' +if ($problemi) { $problemi | ForEach-Object { Write-Host $_.Line -ForegroundColor Yellow } } + +if (-not $buildOk) { + Write-Host "COMPILAZIONE FALLITA" -ForegroundColor Red + exit 1 +} +Write-Host "compilazione ok, 0 errori" -ForegroundColor Green + +Write-Host "`n== Test ==" -ForegroundColor Cyan +$test = & dotnet test Tests\AutoBidder.Tests.csproj --nologo -v q --no-build 2>&1 +$testOk = $LASTEXITCODE -eq 0 + +$riga = $test | Select-String -Pattern 'Superato!|Non superato|Failed!|Passed!' +if ($riga) { $riga | ForEach-Object { Write-Host $_.Line } } + +if (-not $testOk) { + # In caso di rosso servono i dettagli, non il riassunto. + $test | Select-String -Pattern 'Assert|Error Message|Stack Trace|\[FAIL\]' | + Select-Object -First 30 | ForEach-Object { Write-Host $_.Line -ForegroundColor Red } + Write-Host "`nTEST FALLITI" -ForegroundColor Red + exit 1 +} + +Write-Host "`nTutto a posto." -ForegroundColor Green +exit 0