refactor: remove Tauri auto-updater, replace with simple GitHub-check modal

Remove tauri-plugin-updater, relaunch_after_update command, generate-update-
manifest.js, the generate-manifest CI job, and all signing/upload steps for
.sig files and latest.json. The updater plugin, its pubkey and endpoint config
in tauri.conf.json, and the updater:default capability are all gone.

Replace AppUpdater.tsx with a lightweight component that:
- Fetches https://api.github.com/repos/Psychotoxical/psysonic/releases/latest
  after a 4 s delay (no install, no download, no progress bar)
- Shows a dismissible toast with two buttons:
  GitHub → releases/latest
  Website → https://psysonic.psychotoxic.eu/#downloads

i18n: remove updaterInstall/Downloading/Installing/Download/ExperimentalHint
keys from all 7 locales; add updaterWebsite; update updaterVersion wording.

CI: remove generate-manifest job and all .sig signing/upload steps from
build-macos-windows. Also remove TAURI_SIGNING_PRIVATE_KEY env vars (no
longer needed). Release now just builds and uploads the installable binaries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Psychotoxical
2026-04-07 13:42:52 +02:00
parent 3f1b6fd92d
commit 95468fb137
19 changed files with 40 additions and 728 deletions
+21 -100
View File
@@ -1,9 +1,7 @@
import React, { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { check, Update, DownloadEvent } from '@tauri-apps/plugin-updater';
import { invoke } from '@tauri-apps/api/core';
import { open } from '@tauri-apps/plugin-shell';
import { RefreshCw, Download, X } from 'lucide-react';
import { RefreshCw, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { version as currentVersion } from '../../package.json';
@@ -18,85 +16,31 @@ function isNewer(a: string, b: string): boolean {
return false;
}
type State =
| { phase: 'idle' }
| { phase: 'available'; version: string; update: Update | null; error?: string }
| { phase: 'downloading'; pct: number }
| { phase: 'installing' }
| { phase: 'done' };
export default function AppUpdater() {
const { t } = useTranslation();
const [state, setState] = useState<State>({ phase: 'idle' });
const [newVersion, setNewVersion] = useState<string | null>(null);
const [dismissed, setDismissed] = useState(false);
useEffect(() => {
let cancelled = false;
const timer = setTimeout(async () => {
if (cancelled) return;
try {
// Try Tauri native updater first (macOS + Windows)
const update = await check();
if (cancelled) return;
if (update) {
setState({ phase: 'available', version: update.version, update });
return;
}
} catch {
// Tauri updater unavailable or network error — fall through to GitHub check
}
// Fallback: GitHub API check (Linux / offline Tauri updater)
try {
const res = await fetch('https://api.github.com/repos/Psychotoxical/psysonic/releases/latest');
if (!res.ok || cancelled) return;
const data = await res.json();
const tag: string = data.tag_name ?? '';
if (!cancelled && tag && isNewer(tag, currentVersion)) {
setState({ phase: 'available', version: tag.replace(/^[^0-9]*/, ''), update: null });
setNewVersion(tag.replace(/^[^0-9]*/, ''));
}
} catch {
// No update check possible — stay idle
// No network or rate-limited — stay idle
}
}, 3000);
}, 4000);
return () => { cancelled = true; clearTimeout(timer); };
}, []);
if (dismissed || state.phase === 'idle' || state.phase === 'done') return null;
const handleInstall = async () => {
if (state.phase !== 'available' || !state.update) return;
const update = state.update;
const savedVersion = state.version;
let total = 0;
let downloaded = 0;
setState({ phase: 'downloading', pct: 0 });
try {
await update.downloadAndInstall((event: DownloadEvent) => {
if (event.event === 'Started') {
total = event.data.contentLength ?? 0;
} else if (event.event === 'Progress') {
downloaded += event.data.chunkLength;
setState({ phase: 'downloading', pct: total ? Math.round((downloaded / total) * 100) : 0 });
} else if (event.event === 'Finished') {
setState({ phase: 'installing' });
}
});
await invoke('relaunch_after_update');
} catch (e: unknown) {
const msg = e instanceof Error ? e.message : String(e);
console.error('Update failed:', msg);
// Surface the error so the user (and developer) can see what went wrong
setState({ phase: 'available', version: savedVersion, update, error: msg });
}
};
const handleDownload = () => {
open('https://github.com/Psychotoxical/psysonic/releases/latest');
};
const version = state.phase === 'available' ? state.version : '';
const canInstall = state.phase === 'available' && state.update !== null;
const isLinuxFallback = state.phase === 'available' && state.update === null;
if (!newVersion || dismissed) return null;
return createPortal(
<div className="app-updater-toast">
@@ -107,44 +51,21 @@ export default function AppUpdater() {
<X size={12} />
</button>
</div>
<div className="app-updater-version">{t('common.updaterVersion', { version })}</div>
{state.phase === 'downloading' && (
<div className="app-updater-progress-wrap">
<div className="app-updater-progress-bar">
<div className="app-updater-progress-fill" style={{ width: `${state.pct}%` }} />
</div>
<span className="app-updater-pct">{state.pct}%</span>
</div>
)}
{state.phase === 'installing' && (
<div className="app-updater-status">{t('common.updaterInstalling')}</div>
)}
{state.phase === 'available' && (
<div className="app-updater-actions">
{state.error && (
<div className="app-updater-error">{state.error}</div>
)}
{canInstall && (
<>
<p className="app-updater-hint">{t('common.updaterExperimentalHint')}</p>
<button className="app-updater-btn-primary" onClick={handleInstall}>
<Download size={12} /> {t('common.updaterInstall')}
</button>
<button className="app-updater-btn-secondary" onClick={handleDownload}>
<Download size={12} /> {t('common.updaterDownload')}
</button>
</>
)}
{isLinuxFallback && (
<button className="app-updater-btn-primary" onClick={handleDownload}>
<Download size={12} /> {t('common.updaterDownload')}
</button>
)}
</div>
)}
<div className="app-updater-version">{t('common.updaterVersion', { version: newVersion })}</div>
<div className="app-updater-actions">
<button
className="app-updater-btn-primary"
onClick={() => open('https://github.com/Psychotoxical/psysonic/releases/latest')}
>
GitHub
</button>
<button
className="app-updater-btn-secondary"
onClick={() => open('https://psysonic.psychotoxic.eu/#downloads')}
>
{t('common.updaterWebsite')}
</button>
</div>
</div>,
document.body
);
+2 -9
View File
@@ -15,9 +15,6 @@ export const deTranslation = {
help: 'Hilfe',
expand: 'Sidebar einblenden',
collapse: 'Sidebar ausblenden',
updateAvailable: 'Update verfügbar',
updateReady: '{{version}} ist bereit',
updateLink: 'Zum Release →',
downloadingTracks: '{{n}} Tracks werden gecacht…',
offlineLibrary: 'Offline-Bibliothek',
genres: 'Genres',
@@ -328,12 +325,8 @@ export const deTranslation = {
bulkRemoveFromPlaylist: 'Aus Playlist entfernen',
bulkClear: 'Auswahl aufheben',
updaterAvailable: 'Update verfügbar',
updaterVersion: 'v{{version}} ist bereit',
updaterInstall: 'Installieren & Neustart',
updaterDownloading: 'Wird geladen…',
updaterInstalling: 'Wird installiert…',
updaterDownload: 'Von GitHub herunterladen',
updaterExperimentalHint: 'Automatische Updates sind noch in Entwicklung',
updaterVersion: 'v{{version}} verfügbar',
updaterWebsite: 'Website',
},
settings: {
title: 'Einstellungen',
+3 -9
View File
@@ -15,9 +15,7 @@ export const enTranslation = {
help: 'Help',
expand: 'Expand Sidebar',
collapse: 'Collapse Sidebar',
updateAvailable: 'Update available',
updateReady: '{{version}} is ready',
updateLink: 'Go to release →',
downloadingTracks: 'Caching {{n}} tracks…',
offlineLibrary: 'Offline Library',
genres: 'Genres',
@@ -328,12 +326,8 @@ export const enTranslation = {
bulkRemoveFromPlaylist: 'Remove from Playlist',
bulkClear: 'Clear selection',
updaterAvailable: 'Update available',
updaterVersion: 'v{{version}} is ready',
updaterInstall: 'Install & Restart',
updaterDownloading: 'Downloading…',
updaterInstalling: 'Installing…',
updaterDownload: 'Download from GitHub',
updaterExperimentalHint: 'Auto-update is still in development',
updaterVersion: 'v{{version}} is available',
updaterWebsite: 'Website',
},
settings: {
title: 'Settings',
+2 -9
View File
@@ -15,9 +15,6 @@ export const frTranslation = {
help: 'Aide',
expand: 'Développer la barre latérale',
collapse: 'Réduire la barre latérale',
updateAvailable: 'Mise à jour disponible',
updateReady: '{{version}} est prêt',
updateLink: 'Voir la version →',
downloadingTracks: '{{n}} pistes en cache…',
offlineLibrary: 'Bibliothèque hors ligne',
genres: 'Genres',
@@ -328,12 +325,8 @@ export const frTranslation = {
bulkRemoveFromPlaylist: 'Retirer de la playlist',
bulkClear: 'Désélectionner',
updaterAvailable: 'Mise à jour disponible',
updaterVersion: 'v{{version}} est prête',
updaterInstall: 'Installer & Redémarrer',
updaterDownloading: 'Téléchargement…',
updaterInstalling: 'Installation…',
updaterDownload: 'Télécharger depuis GitHub',
updaterExperimentalHint: 'La mise à jour automatique est encore en développement',
updaterVersion: 'v{{version}} est disponible',
updaterWebsite: 'Site Web',
},
settings: {
title: 'Paramètres',
+2 -9
View File
@@ -15,9 +15,6 @@ export const nbTranslation = {
help: 'Hjelp',
expand: 'Utvid sidefelt',
collapse: 'Skjul sidefelt',
updateAvailable: 'Ny versjon er tilgjengelig',
updateReady: '{{version}} er klar',
updateLink: 'Hent nyeste versjon →',
downloadingTracks: 'Bufre {{n}} spor…',
offlineLibrary: 'Frakoblet bibliotek',
genres: 'Sjangere',
@@ -328,12 +325,8 @@ export const nbTranslation = {
bulkRemoveFromPlaylist: 'Fjern fra spilleliste',
bulkClear: 'Tøm utvalg',
updaterAvailable: 'Ny versjon er tilgjengelig',
updaterVersion: 'v{{version}} er lansert',
updaterInstall: 'Installer og start applikasjonen på nytt',
updaterDownloading: 'Laster ned…',
updaterInstalling: 'Installerer…',
updaterDownload: 'Last ned fra GitHub',
updaterExperimentalHint: 'Auto-oppdatering er fortsatt under utvikling',
updaterVersion: 'v{{version}} er tilgjengelig',
updaterWebsite: 'Nettsted',
},
settings: {
title: 'Innstillinger',
+2 -9
View File
@@ -15,9 +15,6 @@ export const nlTranslation = {
help: 'Help',
expand: 'Zijbalk uitklappen',
collapse: 'Zijbalk inklappen',
updateAvailable: 'Update beschikbaar',
updateReady: '{{version}} is klaar',
updateLink: 'Naar release →',
downloadingTracks: '{{n}} nummers worden gecached…',
offlineLibrary: 'Offline bibliotheek',
genres: 'Genres',
@@ -328,12 +325,8 @@ export const nlTranslation = {
bulkRemoveFromPlaylist: 'Verwijderen uit afspeellijst',
bulkClear: 'Selectie wissen',
updaterAvailable: 'Update beschikbaar',
updaterVersion: 'v{{version}} is klaar',
updaterInstall: 'Installeren & Herstarten',
updaterDownloading: 'Downloaden…',
updaterInstalling: 'Installeren…',
updaterDownload: 'Downloaden van GitHub',
updaterExperimentalHint: 'Automatisch bijwerken is nog in ontwikkeling',
updaterVersion: 'v{{version}} beschikbaar',
updaterWebsite: 'Website',
},
settings: {
title: 'Instellingen',
+2 -9
View File
@@ -16,9 +16,6 @@ export const ruTranslation = {
help: 'Справка',
expand: 'Развернуть боковую панель',
collapse: 'Свернуть боковую панель',
updateAvailable: 'Доступно обновление',
updateReady: '{{version}} готово',
updateLink: 'Перейти к релизу →',
downloadingTracks: 'Кэширование {{n}} треков…',
offlineLibrary: 'Офлайн-библиотека',
genres: 'Жанры',
@@ -342,12 +339,8 @@ export const ruTranslation = {
bulkRemoveFromPlaylist: 'Убрать из плейлиста',
bulkClear: 'Снять выделение',
updaterAvailable: 'Доступно обновление',
updaterVersion: 'Версия {{version}} готова',
updaterInstall: 'Установить и перезапустить',
updaterDownloading: 'Скачивание…',
updaterInstalling: 'Установка…',
updaterDownload: 'Скачать с GitHub',
updaterExperimentalHint: 'Автообновление в разработке',
updaterVersion: 'Версия {{version}} доступна',
updaterWebsite: 'Сайт',
},
settings: {
title: 'Настройки',
+2 -9
View File
@@ -15,9 +15,6 @@ export const zhTranslation = {
help: '帮助',
expand: '展开侧边栏',
collapse: '收起侧边栏',
updateAvailable: '有可用更新',
updateReady: '{{version}} 已就绪',
updateLink: '前往发布页面 →',
downloadingTracks: '正在缓存 {{n}} 首歌曲…',
offlineLibrary: '离线音乐库',
genres: '流派',
@@ -324,12 +321,8 @@ export const zhTranslation = {
bulkRemoveFromPlaylist: '从播放列表移除',
bulkClear: '取消选择',
updaterAvailable: '有可用更新',
updaterVersion: 'v{{version}} 已就绪',
updaterInstall: '安装并重启',
updaterDownloading: '下载中…',
updaterInstalling: '安装中…',
updaterDownload: '从 GitHub 下载',
updaterExperimentalHint: '自动更新功能仍在开发中',
updaterVersion: 'v{{version}} 已发布',
updaterWebsite: '官网',
},
settings: {
title: '设置',