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>
8.9 KiB
Ein Projekt veröffentlichungsfähig machen
Für Coding-Agenten, die den UpdateService in ein Projekt integrieren. Ergebnis:
./scripts/release.ps1 -Version 1.4.3baut, 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: 0heißt: du bist auf Stand. Sonst nennt jeder Eintrag unteraction, was konkret zu tun ist. Für Menschen: /docs/changelog.php. Die eigene Fassung steht inDeploymentcenterSdk.Version— und gehört in dieAGENTS.mddeines 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:
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.
{
"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:
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
.\scripts\release.ps1 -Version 1.4.3 -Changelog "Behebt den Login-Fehler."
./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
<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.
{
"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
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
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:
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 |