Ampel / ampel (push) Successful in 1m33s
Der Commander: "irgendwie fehlt in rippy selbst der Button 'Auf updates pruefen' oder auch direkt beim start ne meldung 'ey jo update verfuegbar' - waere noch cool wenn das kommt inkl. changelog". Zu Recht. Der Kanal arbeitete korrekt, war aber UNSICHTBAR: Der Update-Stand lag im Haupt und wurde nur beim Laden des Fensters EINMAL mitgeschickt. Fand die Pruefung etwas, aenderte sich am Bildschirm nichts; der einzige Hinweis war ein Satz unter "Update-Adresse", den man kennen musste, um ihn zu finden. Was jetzt da ist: * Eine goldene Karte OBEN in der Uebersicht, sobald etwas gefunden wird — mit neuer Versionsnummer, der laufenden Version, Ladefortschritt und den Aenderungsnotizen. Dazu ein Windows-Toast beim Fund. * Knopf "Jetzt auf Updates pruefen" in den Einstellungen. Waehrend eines Rips ist er AUS, mit Begruendung im Tooltip — Rippy sucht dann bewusst nicht (Paragraph 6.8). * Knopf "Jetzt neu starten und installieren" auf der Karte, sobald das Paket geladen ist. Sagt NEIN mit Grund statt still nichts zu tun (R4). * Der Stand meldet JEDE Aenderung sofort ans Fenster (setzen() ruft immer beiAenderung) — kein Zustand mehr, den niemand erfaehrt. Der Changelog kommt aus bau/release-notes.md. An der installierten Bibliothek geprueft statt aus dem Kopf (Regel D): app-builder-lib out/publish/updateInfoBuilder.js getReleaseInfo() liest genau diesen Dateinamen und legt den Inhalt als releaseNotes in latest.yml; builder-util-runtime deklariert UpdateInfo.releaseNotes als "string | Array<ReleaseNoteInfo> | null". neuerungenText() vertraegt beide Formen, wirft HTML-Marken raus und deckelt die Laenge. UpdateStand ist jetzt EINE Definition in gemeinsam/nachrichten.ts, die sich Haupt und Fenster teilen. 230 Tests gruen (vorher 225), Typpruefung sauber. Version 5.1.2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
62 lines
2.6 KiB
TypeScript
62 lines
2.6 KiB
TypeScript
// Die reinen Update-Regeln (§ 6.8) — getrennt von update.ts, damit die
|
|
// Tests sie ohne Electron laden können.
|
|
|
|
export const STANDARD_UPDATE_URL =
|
|
'https://git.tobisniceshomelab.ddnsfree.com/Hitonabi/rippy/releases/download/aktuell'
|
|
|
|
/** Prüft die Update-Adresse: NUR https:// ist zulässig (§ 6.8 Regel 1) —
|
|
* ohne Code-Signing (Entscheid 5) ist der Transportweg die einzige
|
|
* Absicherung; eine http://-Adresse wird ABGELEHNT, nicht still
|
|
* akzeptiert. */
|
|
export function updateUrlPruefen(url: string): { ok: boolean; grund: string } {
|
|
const sauber = url.trim()
|
|
if (sauber.length === 0) return { ok: true, grund: '' } // Standard gilt
|
|
if (sauber.startsWith('https://')) return { ok: true, grund: '' }
|
|
return {
|
|
ok: false,
|
|
grund:
|
|
'Die Update-Adresse muss mit https:// beginnen — ohne Signatur ist der sichere Transportweg die einzige Absicherung (KONZEPT § 6.8).',
|
|
}
|
|
}
|
|
|
|
/** So viel Änderungstext zeigt das Fenster höchstens — der Rest wird
|
|
* abgeschnitten, damit eine ausufernde Datei die Karte nicht sprengt. */
|
|
export const NEUERUNGEN_HOECHSTENS = 4000
|
|
|
|
/** Die Änderungsnotizen aus `latest.yml` in reinen Text.
|
|
*
|
|
* `UpdateInfo.releaseNotes` ist laut builder-util-runtime
|
|
* `string | Array<ReleaseNoteInfo> | null` — bei uns ein String
|
|
* (electron-builder legt den Inhalt von `bau/release-notes.md` hinein),
|
|
* bei mehreren übersprungenen Versionen eine Liste. Beides muss hier
|
|
* ankommen können, sonst steht im Fenster „[object Object]".
|
|
*
|
|
* HTML-Marken fliegen raus: Der Text kommt von einem Server, und auch
|
|
* wenn React ihn ohnehin nur als Text setzt, wird hier nichts
|
|
* weitergereicht, was wie Auszeichnung aussieht. */
|
|
export function neuerungenText(roh: unknown): string {
|
|
const saubern = (t: string): string =>
|
|
t
|
|
.replace(/<[^>]*>/g, ' ')
|
|
.split('\n')
|
|
.map((zeile) => zeile.replace(/[ \t]+$/, ''))
|
|
.join('\n')
|
|
.replace(/\n{3,}/g, '\n\n')
|
|
.trim()
|
|
|
|
if (typeof roh === 'string') return saubern(roh).slice(0, NEUERUNGEN_HOECHSTENS)
|
|
if (Array.isArray(roh)) {
|
|
const teile: string[] = []
|
|
for (const eintrag of roh) {
|
|
if (eintrag === null || typeof eintrag !== 'object') continue
|
|
const e = eintrag as { version?: unknown; note?: unknown }
|
|
const version = typeof e.version === 'string' ? e.version : ''
|
|
const notiz = typeof e.note === 'string' ? saubern(e.note) : ''
|
|
if (notiz.length === 0) continue
|
|
teile.push(version.length > 0 ? `${version}\n${notiz}` : notiz)
|
|
}
|
|
return teile.join('\n\n').slice(0, NEUERUNGEN_HOECHSTENS)
|
|
}
|
|
return ''
|
|
}
|