# Ein Projekt veröffentlichungsfähig machen > Für Coding-Agenten, die den UpdateService in ein Projekt integrieren. > Ergebnis: `./scripts/release.ps1 -Version 1.4.3` baut, packt, lädt hoch und > meldet das Release beim Deploymentcenter an — für alle Zielplattformen. Es gibt bereits ein Werkzeug, das den schwierigen Teil erledigt: **`pack-and-deploy`**. Es berechnet Prüfsummen, erzeugt das Dateimanifest, schreibt die `latest.json` fort und meldet das Release über die API an. **Baue das nicht nach.** Ein zweiter Ort für dieselbe Logik ist ein zweiter Ort, an dem dieselben Fehler wieder entstehen — und dieses Werkzeug hat sie bereits hinter sich. Was fehlt, ist nur die Orchestrierung: pro Zielplattform einmal `dotnet publish`, dann `pack-and-deploy`. Genau das ist die Vorlage. --- ## 1. Einrichten Drei Dateien, einmalig: ```bash mkdir -p scripts .dc-tools # Windows curl -fsSL https://dc.mhdf.de/docs/release-template/release.ps1 -o scripts/release.ps1 # Linux / CI curl -fsSL https://dc.mhdf.de/docs/release-template/release.sh -o scripts/release.sh chmod +x scripts/release.sh # in beiden Fällen curl -fsSL https://dc.mhdf.de/docs/release-template/release.config.example.json \ -o scripts/release.config.json echo '.dc-tools/' >> .gitignore echo 'artifacts/' >> .gitignore ``` **Nur `release.config.json` wird angepasst.** Die Skripte selbst bleiben unverändert — dann lassen sie sich bei einer neuen Fassung einfach ersetzen. ```json { "project": "myapp", "csproj": "src/MyApp/MyApp.csproj", "runtimes": ["win-x64", "linux-x64"], "selfContained": true, "setupJson": "setup.json" } ``` `release.config.json` enthält **keine Zugangsdaten** und darf versioniert werden. ### Zugangsdaten Die kommen aus Umgebungsvariablen: ```bash export DC_FTP_HOST=ftp.example.com export DC_FTP_USER=... export DC_FTP_PASS=... export DC_TOKEN=dc_master_... # braucht das Recht updateservice:publish ``` Alternativ eine `packager.config.json` neben dem Werkzeug — die steht dann in `.gitignore`. Ohne `DC_TOKEN` wird das Paket zwar gebaut und hochgeladen, aber **nicht angemeldet und nicht signiert**; der Rückgabewert ist dann 2. ### Voraussetzungen | | Windows | Linux | |---|---|---| | Skript | `release.ps1` (PowerShell 5.1 genügt) | `release.sh` | | Nötig | .NET SDK | .NET SDK, `curl`, dazu `jq` **oder** Python | `pack-and-deploy` holt sich das Skript beim ersten Lauf selbst von `/installer/`, prüft die Prüfsumme und legt es unter `.dc-tools/` ab. Das Verzeichnis gehört in die `.gitignore`. --- ## 2. Veröffentlichen ```powershell .\scripts\release.ps1 -Version 1.4.3 -Changelog "Behebt den Login-Fehler." ``` ```bash ./scripts/release.sh --version 1.4.3 --changelog "Behebt den Login-Fehler." ``` Ohne `-Version` wird sie aus `Directory.Build.props` oder der `.csproj` gelesen. Weitere Schalter: `-Channel beta`, `-Critical`, `-WhatIf` beziehungsweise `--channel`, `--critical`, `--dry-run`. Je Laufzeitkennung entsteht ein eigenes Release. **Ohne Plattformangabe würden sie sich gegenseitig überschreiben** — bis Version 2.2 war genau das der Fall, und ein Linux-System zog sich das Windows-Paket. --- ## 3. Was du im Projekt vorbereiten musst ### `` in die `Directory.Build.props` ```xml 1.4.3 ``` **Nicht in einzelne `.csproj`-Dateien.** `pack-and-deploy` liest die Version aus der Hauptassembly und **bricht bei einer Abweichung ab**. Das ist Absicht: Wird `1.0.1` als `1.0.2` veröffentlicht, aktualisieren alle Clients, melden danach weiterhin `1.0.1`, halten das Release erneut für neu — und aktualisieren bei jedem Start wieder. Eine Endlosschleife über die gesamte Installationsbasis. ### Konfigurationsdateien schützen Zwei verschiedene Dinge, und die Verwechslung hat schon einen echten API-Schlüssel öffentlich gemacht: | | `excludePatterns` | `preservePatterns` | |---|---|---| | Im Paket? | nein | ja | | Erstinstallation | fehlt | wird geschrieben | | Update | — | vorhandene Datei bleibt unangetastet | Eine `appsettings.json` mit echten Zugangsdaten gehört in **keine** von beiden Listen — sie gehört gar nicht erst ins Publish-Verzeichnis. Was ausgeliefert wird, muss eine **Vorlage mit Platzhaltern** sein. `pack-and-deploy` warnt bei Dateien, die nach Zugangsdaten aussehen. **Nimm die Warnung ernst.** Release-Pakete liegen hinter einem Zugangsschutz, aber jeder lizenzierte Kunde kann sie auspacken. ### `setup.json` anlegen Beschreibt, was die Erstinstallation abfragen muss. Das Skript kopiert sie ins Publish-Verzeichnis, sodass sie im Paket landet. ```json { "schema": 1, "targets": [ { "id": "app", "file": "myapp/Settings.json", "location": "config" } ], "fields": [ { "key": "ConnectionStrings:Main", "label": "Datenbank", "type": "secret" }, { "key": "Deploymentcenter:Token", "source": "provision", "scopes": ["watchdog:ping"] } ] } ``` Vollständig beschrieben in `docs/SETUP_INTEGRATION_GUIDE.md` im Deploymentcenter-Repository. Ohne `setup.json` lässt sich die Anwendung installieren, aber nicht einrichten. ### Das SDK einbinden ```csharp var check = await new UpdateClient().CheckForUpdateAsync( baseUrl: "https://dc.mhdf.de", projectId: "myapp", currentVersion: BuildInfo.Version, channel: "prod", credentials: ReleaseCredentials.FromLicenseKey(meineLizenz)); ``` **Der Lizenzschlüssel ist Pflicht.** Die Release-Ablage liegt hinter HTTP-Basic-Auth; ohne ihn bekommt die Anwendung 401 und keine Updates mehr. Details in `docs/UPDATESERVICE_INTEGRATION_GUIDE.md` §5A (Zugangsschutz der Release-Verzeichnisse). > **Für ein Produkt, das noch nie veröffentlicht hat, gilt eine besondere > Reihenfolge.** `/releases//` existiert noch nicht und ist deshalb auch > nicht geschützt. Das Verzeichnis entsteht mit dem ersten Upload, und der > nächste Abgleich schützt es. Es gibt also kein Zeitfenster, um ein > ungeschütztes Release zu ziehen und danach das SDK nachzurüsten: **der erste > ausgelieferte Build muss die Zugangsdaten schon mitbringen.** --- ## 4. Rückgabewerte | Wert | Bedeutung | |---|---| | `0` | vollständig veröffentlicht | | `1` | Konfigurationsfehler oder Versionskonflikt — **nichts wurde ausgeführt** | | `2` | teilweise: Upload oder Registrierung fehlgeschlagen | Bei `1` bricht das Skript sofort ab, statt die übrigen Plattformen ins Leere laufen zu lassen. Bei `2` läuft es weiter und meldet am Ende, welche Plattformen betroffen sind — dort ist zu prüfen, ob die bereits hochgeladenen Dateien zusammenpassen. --- ## 5. Prüfen, ob es getragen hat ```bash curl "https://dc.mhdf.de/api/updateservice/v1/check?product=myapp&version=0.0.0&channel=prod&platform=win-x64" ``` Erwartet: `update_available: true` mit der neuen Version und `"signed": true` in der Antwort des Publish-Aufrufs. Im WebUI erscheint das Release unter *UpdateService → Releases* mit Plattform-Spalte; steht dort **UNSIGNIERT**, fehlt auf dem Server der Signierschlüssel. Und der Zugangsschutz: ```bash curl -I https://dc.mhdf.de/releases/myapp/prod/win-x64/1.4.3/package.tar.gz # 401 erwartet ``` Antwortet das mit **200**, ist das Paket öffentlich abrufbar — dann im WebUI unter *UpdateService → 🔒 Zugangsschutz* den Selbsttest laufen lassen. --- ## 6. Häufige Stolperstellen | Symptom | Ursache | |---|---| | `Versionskonflikt` beim Packen | `` steht nur in einem von mehreren Projekten — gehört in die `Directory.Build.props` | | Rückgabewert 2, „Registrierung fehlgeschlagen" | `DC_TOKEN` fehlt oder hat nicht `updateservice:publish` | | `unknown_project` | Der Slug ist im Deploymentcenter nicht angelegt | | Client bekommt 401 statt Updates | Kein `licenseKey` übergeben, oder die Lizenz ist abgelaufen | | Linux-Paket startet nicht | Unter Windows gebaut — der Agent setzt das Ausführungsbit beim Anwenden, ein von Hand entpacktes Archiv nicht | | Update lädt endlos erneut | Veröffentlichte Version weicht von der einkompilierten ab |