Files
Deploymentcenter/public/docs/release.md
T
Deploymentcenter BotandClaude Opus 5 a8b9f6f7c9 feat(docs): Changelog mit "was ist seit meiner Fassung neu"
Bisher musste ein Agent, der eine Anbindung aktualisiert, die gesamte Historie
lesen - oder er las gar nichts und uebersah eine brechende Aenderung. Beides
schlecht.

- public/docs/changelog.json ist die einzige Quelle. Je Fassung eine
  Zusammenfassung, je Aenderung Bereich, ein "breaking"-Kennzeichen und vor
  allem ein Feld "action" mit dem, was konkret zu tun ist. Steht dort null,
  ist nichts zu tun - das ist die haeufigste und nuetzlichste Antwort.
- GET /api/updateservice/v1/changelog?since=2.2.0 liefert nur die neueren
  Fassungen, dazu die Anzahl der Punkte mit Handlungsbedarf und der
  brechenden Aenderungen. count:0 heisst "du bist auf Stand" - dann muss gar
  nichts gelesen werden. Optional nach Bereich filterbar (?area=packager).
- /docs/changelog.php rendert dieselbe Datei fuer Menschen, mit Eingabefeld
  fuer die eigene Fassung. Bewusst dieselbe Quelle: zwei Fassungen zu pflegen
  hiesse, sie auseinanderlaufen zu lassen.
- DeploymentcenterSdk.Version im SDK ist der Bezugspunkt. Damit muss die
  Fassung nicht abgetippt werden.
- AGENT_PROMPT_TEMPLATE.md verpflichtet dazu, sie in der AGENTS.md des
  Projekts festzuhalten und vor jeder Aenderung an der Anbindung den
  Unterschied abzufragen. Auch in der Kurzfassung fuer knappe Prompt-Budgets.

Die Historie ist rueckwirkend bis 2.0.0 gefuellt: 6 Fassungen, 25 Punkte mit
Handlungsbedarf, 13 brechende Aenderungen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 16:45:50 +02:00

258 lines
8.9 KiB
Markdown

# 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.
> **Aktualisierst du eine bestehende Anbindung?** Dann lies nicht alles neu.
> Frag zuerst, was seit deiner Fassung dazugekommen ist:
>
> ```
> GET /api/updateservice/v1/changelog?since=2.2.0
> ```
>
> `count: 0` heißt: du bist auf Stand. Sonst nennt jeder Eintrag unter
> `action`, was konkret zu tun ist. Für Menschen: [/docs/changelog.php](./changelog.php).
> Die eigene Fassung steht in `DeploymentcenterSdk.Version` — **und gehört in
> die `AGENTS.md` deines Projekts**, sonst fängst du beim nächsten Mal wieder
> von vorn an.
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
### `<Version>` in die `Directory.Build.props`
```xml
<Project>
<PropertyGroup>
<Version>1.4.3</Version>
</PropertyGroup>
</Project>
```
**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` bricht ab, wenn es Zugangsdaten findet** — geprüft werden
Dateinamen (`appsettings.Local.json`, `master.key`, `*.pfx`, `*.db`, …) und der
Inhalt (gefüllte `Password=`-Felder, `sk-…`, `dc_master_…`, private Schlüssel).
Platzhalter wie `sk-DEIN-SCHLUESSEL-HIER` lösen nicht aus.
Häufigste Ursache: eine `CopyToOutputDirectory`-Regel in der `.csproj`, die
eine lokale Konfigurationsdatei ins Ausgabeverzeichnis kopiert. Dann gehört sie
in `excludePatterns` — und die Regel auf den Prüfstand.
Notausgang: `--allow-secrets`. **Nur benutzen, wenn du weißt, warum.**
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/<slug>/` 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 | `<Version>` 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 |