mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-08 23:21:58 +02:00
fix(updates): switch Windows installer installs to release-asset update flow
In-app "Install Update" on Windows installer installs failed with "Could
not find git executable" because (1) _find_executable's fallback paths
are Unix-only, and (2) the installer stages backend/ via shutil.copytree
so there is no .git directory — even with Git for Windows installed, the
fetch would die on "not a git repository". Adding Windows paths would
only have changed which error users saw.
Switches the Windows installer path to a fourth update_method
("windows_installer") that mirrors the existing docker / ha_addon
branches — surface a link to the release .exe and let the user re-run
the installer, matching the Discord / Spotify Windows update model.
Backend:
- New _is_windows_installer_install() — true iff sys.platform == "win32"
AND no .git in app_dir, so Windows devs with a real git clone keep
the git path.
- New _find_windows_installer_asset() picks the matching release asset
(prefers versioned bambuddy-<ver>-windows-x64-setup.exe, falls back
to the unversioned alias on non-daily tags).
- /updates/check now returns is_windows_installer / update_method /
installer_download_url.
- /updates/apply short-circuits with a friendly message after the
existing HA / Docker guards — defense in depth, the frontend swaps
the button so the POST should not fire on Windows.
Frontend:
- UpdateCheckResult extended with the new fields and 'windows_installer'
in the update_method union.
- SettingsPage renders a Bambu-green styled <a target="_blank"
rel="noopener"> between the Docker snippet and the in-app Update
button, with installer_download_url falling back to release_url then
the tag page so the link is never broken.
- applyUpdateMutation onSuccess toast guard extended to treat
is_windows_installer the same as HA / Docker.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -120,6 +120,50 @@ def _is_ha_addon() -> bool:
|
||||
return bool(os.environ.get("SUPERVISOR_TOKEN"))
|
||||
|
||||
|
||||
def _is_windows_installer_install() -> bool:
|
||||
"""Detect a Windows install that came from the Inno Setup installer.
|
||||
|
||||
The installer stages backend source via ``shutil.copytree`` (no ``.git``
|
||||
directory) and does not bundle ``git.exe`` — so the git-fetch-and-reset
|
||||
update path used everywhere else is structurally inoperable here. We
|
||||
surface this as a distinct ``update_method`` and direct the user at the
|
||||
release asset instead.
|
||||
|
||||
A Windows developer running from a real ``git clone`` keeps the git
|
||||
path (``.git`` present), so this only catches installer users.
|
||||
"""
|
||||
if sys.platform != "win32":
|
||||
return False
|
||||
return not (settings.app_dir / ".git").exists()
|
||||
|
||||
|
||||
def _find_windows_installer_asset(release_data: dict) -> str | None:
|
||||
"""Pick the Windows installer .exe out of a GitHub release's assets list.
|
||||
|
||||
Both filenames the workflow uploads end in ``windows-x64-setup.exe``
|
||||
(versioned ``bambuddy-<version>-windows-x64-setup.exe`` and the
|
||||
unversioned alias ``bambuddy-windows-x64-setup.exe`` on non-daily tags
|
||||
only). Either works as a download URL; we prefer the versioned form
|
||||
because it's the one guaranteed to exist on every release including
|
||||
dailies.
|
||||
"""
|
||||
assets = release_data.get("assets") or []
|
||||
versioned: str | None = None
|
||||
unversioned: str | None = None
|
||||
for asset in assets:
|
||||
name = asset.get("name") or ""
|
||||
url = asset.get("browser_download_url")
|
||||
if not isinstance(name, str) or not isinstance(url, str):
|
||||
continue
|
||||
if not name.endswith("windows-x64-setup.exe"):
|
||||
continue
|
||||
if name == "bambuddy-windows-x64-setup.exe":
|
||||
unversioned = url
|
||||
else:
|
||||
versioned = url
|
||||
return versioned or unversioned
|
||||
|
||||
|
||||
def _find_executable(name: str) -> str | None:
|
||||
"""Find an executable in PATH or common locations."""
|
||||
# Try standard PATH first
|
||||
@@ -459,10 +503,15 @@ async def check_for_updates(
|
||||
|
||||
is_docker = _is_docker_environment()
|
||||
is_ha_addon = _is_ha_addon()
|
||||
is_windows_installer = _is_windows_installer_install()
|
||||
installer_download_url: str | None = None
|
||||
if is_ha_addon:
|
||||
update_method = "ha_addon"
|
||||
elif is_docker:
|
||||
update_method = "docker"
|
||||
elif is_windows_installer:
|
||||
update_method = "windows_installer"
|
||||
installer_download_url = _find_windows_installer_asset(release_data)
|
||||
else:
|
||||
update_method = "git"
|
||||
return {
|
||||
@@ -475,7 +524,9 @@ async def check_for_updates(
|
||||
"published_at": published_at,
|
||||
"is_docker": is_docker,
|
||||
"is_ha_addon": is_ha_addon,
|
||||
"is_windows_installer": is_windows_installer,
|
||||
"update_method": update_method,
|
||||
"installer_download_url": installer_download_url,
|
||||
}
|
||||
|
||||
except httpx.HTTPError as e:
|
||||
@@ -827,6 +878,19 @@ async def apply_update(
|
||||
"git pull && docker compose build --pull && docker compose up -d"
|
||||
),
|
||||
}
|
||||
if _is_windows_installer_install():
|
||||
# The installer layout has no ``.git`` and no bundled ``git.exe`` —
|
||||
# the git-fetch path would fail. Frontend swaps the "Update now"
|
||||
# button for a Download Installer link via update_method, so this
|
||||
# branch is only reached if /apply is hit directly.
|
||||
return {
|
||||
"success": False,
|
||||
"is_windows_installer": True,
|
||||
"message": (
|
||||
"Windows installations are updated by re-running the installer. "
|
||||
"Download the latest installer from the Bambuddy releases page."
|
||||
),
|
||||
}
|
||||
|
||||
# Discover which release tag to install. Resolved here (where we have
|
||||
# a DB session) and passed into the background task; the BG task can't
|
||||
|
||||
@@ -637,3 +637,144 @@ class TestUpdatesAPI:
|
||||
assert all(s == f"safe.directory={app_dir}" for s in safe_dir_configs), (
|
||||
f"safe.directory must point at app_dir ({app_dir}); got {safe_dir_configs}"
|
||||
)
|
||||
|
||||
# --- Windows installer update_method ---
|
||||
# The Inno-Setup installer stages backend source via ``copytree`` (no
|
||||
# ``.git``) and does not bundle ``git.exe``. The git-fetch update path
|
||||
# therefore can't run on those installs — surface a distinct
|
||||
# ``update_method`` and a release-asset download link instead.
|
||||
|
||||
def test_is_windows_installer_install_true_when_no_dot_git(self, tmp_path: Path):
|
||||
from backend.app.api.routes import updates as updates_module
|
||||
|
||||
with (
|
||||
patch.object(updates_module.sys, "platform", "win32"),
|
||||
patch.object(updates_module.settings, "app_dir", tmp_path),
|
||||
):
|
||||
assert updates_module._is_windows_installer_install() is True
|
||||
|
||||
def test_is_windows_installer_install_false_on_dev_checkout(self, tmp_path: Path):
|
||||
"""A Windows developer with a real ``git clone`` keeps the git path."""
|
||||
from backend.app.api.routes import updates as updates_module
|
||||
|
||||
(tmp_path / ".git").mkdir()
|
||||
with (
|
||||
patch.object(updates_module.sys, "platform", "win32"),
|
||||
patch.object(updates_module.settings, "app_dir", tmp_path),
|
||||
):
|
||||
assert updates_module._is_windows_installer_install() is False
|
||||
|
||||
def test_is_windows_installer_install_false_off_windows(self, tmp_path: Path):
|
||||
from backend.app.api.routes import updates as updates_module
|
||||
|
||||
with (
|
||||
patch.object(updates_module.sys, "platform", "linux"),
|
||||
patch.object(updates_module.settings, "app_dir", tmp_path),
|
||||
):
|
||||
assert updates_module._is_windows_installer_install() is False
|
||||
|
||||
def test_find_windows_installer_asset_prefers_versioned(self):
|
||||
from backend.app.api.routes.updates import _find_windows_installer_asset
|
||||
|
||||
release = {
|
||||
"assets": [
|
||||
{"name": "bambuddy-0.2.5b1-windows-x64-setup.exe", "browser_download_url": "https://x/v.exe"},
|
||||
{"name": "bambuddy-windows-x64-setup.exe", "browser_download_url": "https://x/alias.exe"},
|
||||
{"name": "checksums.txt", "browser_download_url": "https://x/c.txt"},
|
||||
],
|
||||
}
|
||||
assert _find_windows_installer_asset(release) == "https://x/v.exe"
|
||||
|
||||
def test_find_windows_installer_asset_falls_back_to_alias(self):
|
||||
from backend.app.api.routes.updates import _find_windows_installer_asset
|
||||
|
||||
release = {
|
||||
"assets": [
|
||||
{"name": "bambuddy-windows-x64-setup.exe", "browser_download_url": "https://x/alias.exe"},
|
||||
],
|
||||
}
|
||||
assert _find_windows_installer_asset(release) == "https://x/alias.exe"
|
||||
|
||||
def test_find_windows_installer_asset_none_when_missing(self):
|
||||
from backend.app.api.routes.updates import _find_windows_installer_asset
|
||||
|
||||
assert _find_windows_installer_asset({"assets": []}) is None
|
||||
assert _find_windows_installer_asset({}) is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_apply_update_windows_installer_rejection(self, async_client: AsyncClient):
|
||||
"""Direct POST /apply on a Windows-installer install must be rejected
|
||||
with a friendly message — the git path would error out with "git not
|
||||
found" (or worse, "not a git repository") if it ran."""
|
||||
with (
|
||||
patch("backend.app.api.routes.updates._is_ha_addon", return_value=False),
|
||||
patch("backend.app.api.routes.updates._is_docker_environment", return_value=False),
|
||||
patch(
|
||||
"backend.app.api.routes.updates._is_windows_installer_install",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
response = await async_client.post("/api/v1/updates/apply")
|
||||
result = response.json()
|
||||
assert result["success"] is False
|
||||
assert result["is_windows_installer"] is True
|
||||
assert "installer" in result["message"].lower()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_windows_installer_returns_method_and_url(self, async_client: AsyncClient):
|
||||
"""/updates/check must surface update_method=windows_installer plus
|
||||
the installer .exe URL so the frontend can render a Download button
|
||||
instead of the in-app Install button."""
|
||||
import httpx as _httpx
|
||||
|
||||
fake_release = {
|
||||
# Non-prerelease tag — beta-channel filter defaults to off, so a
|
||||
# `b1` suffix would be skipped and the route would return
|
||||
# "No releases found" before reaching update_method.
|
||||
"tag_name": "v999.9.9",
|
||||
"name": "v999.9.9",
|
||||
"body": "",
|
||||
"html_url": "https://github.com/maziggy/bambuddy/releases/tag/v999.9.9",
|
||||
"published_at": "2099-01-01T00:00:00Z",
|
||||
"assets": [
|
||||
{
|
||||
"name": "bambuddy-999.9.9-windows-x64-setup.exe",
|
||||
"browser_download_url": "https://github.com/maziggy/bambuddy/releases/download/v999.9.9/bambuddy-999.9.9-windows-x64-setup.exe",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
class _Resp:
|
||||
status_code = 200
|
||||
|
||||
def raise_for_status(self):
|
||||
return None
|
||||
|
||||
def json(self):
|
||||
return [fake_release]
|
||||
|
||||
class _FakeClient:
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *_):
|
||||
return None
|
||||
|
||||
async def get(self, *_, **__):
|
||||
return _Resp()
|
||||
|
||||
with (
|
||||
patch.object(_httpx, "AsyncClient", _FakeClient),
|
||||
patch("backend.app.api.routes.updates._is_ha_addon", return_value=False),
|
||||
patch("backend.app.api.routes.updates._is_docker_environment", return_value=False),
|
||||
patch(
|
||||
"backend.app.api.routes.updates._is_windows_installer_install",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
response = await async_client.get("/api/v1/updates/check")
|
||||
body = response.json()
|
||||
assert "update_method" in body, f"unexpected response shape: {body}"
|
||||
assert body["update_method"] == "windows_installer"
|
||||
assert body["is_windows_installer"] is True
|
||||
assert body["installer_download_url"].endswith("bambuddy-999.9.9-windows-x64-setup.exe")
|
||||
|
||||
Generated
-1
@@ -6401,7 +6401,6 @@
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz",
|
||||
"integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
|
||||
@@ -488,6 +488,35 @@ describe('SettingsPage', () => {
|
||||
expect(screen.queryByText(/Home Assistant Supervisor/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: /install update/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the installer-download link for Windows installer installs', async () => {
|
||||
const downloadUrl =
|
||||
'https://github.com/maziggy/bambuddy/releases/download/v0.2.5/bambuddy-0.2.5-windows-x64-setup.exe';
|
||||
await renderWithUpdateCheck({
|
||||
update_available: true,
|
||||
current_version: '0.2.4',
|
||||
latest_version: '0.2.5',
|
||||
release_name: '0.2.5',
|
||||
release_notes: '',
|
||||
release_url: 'https://github.com/maziggy/bambuddy/releases/tag/v0.2.5',
|
||||
published_at: '2099-01-01T00:00:00Z',
|
||||
is_docker: false,
|
||||
is_ha_addon: false,
|
||||
is_windows_installer: true,
|
||||
update_method: 'windows_installer',
|
||||
installer_download_url: downloadUrl,
|
||||
});
|
||||
|
||||
const link = await screen.findByRole('link', { name: /download installer for v0\.2\.5/i });
|
||||
expect(link).toHaveAttribute('href', downloadUrl);
|
||||
expect(link).toHaveAttribute('target', '_blank');
|
||||
expect(link).toHaveAttribute('rel', expect.stringContaining('noopener'));
|
||||
// The in-app update button must NOT render — the git-fetch path can't
|
||||
// work from an installer payload.
|
||||
expect(screen.queryByRole('button', { name: /install update/i })).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Home Assistant Supervisor/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('docker compose pull && docker compose up -d')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('tabs navigation', () => {
|
||||
|
||||
@@ -2825,7 +2825,9 @@ export interface UpdateCheckResult {
|
||||
message?: string;
|
||||
is_docker?: boolean;
|
||||
is_ha_addon?: boolean;
|
||||
update_method?: 'docker' | 'git' | 'ha_addon';
|
||||
is_windows_installer?: boolean;
|
||||
update_method?: 'docker' | 'git' | 'ha_addon' | 'windows_installer';
|
||||
installer_download_url?: string | null;
|
||||
}
|
||||
|
||||
export interface UpdateStatus {
|
||||
@@ -5334,7 +5336,7 @@ export const api = {
|
||||
getVersion: () => request<VersionInfo>('/updates/version'),
|
||||
checkForUpdates: () => request<UpdateCheckResult>('/updates/check'),
|
||||
applyUpdate: () =>
|
||||
request<{ success: boolean; message: string; status?: UpdateStatus; is_docker?: boolean; is_ha_addon?: boolean }>('/updates/apply', {
|
||||
request<{ success: boolean; message: string; status?: UpdateStatus; is_docker?: boolean; is_ha_addon?: boolean; is_windows_installer?: boolean }>('/updates/apply', {
|
||||
method: 'POST',
|
||||
}),
|
||||
getUpdateStatus: () => request<UpdateStatus>('/updates/status'),
|
||||
|
||||
@@ -2294,6 +2294,8 @@ export default {
|
||||
releaseNotes: 'Versionshinweise',
|
||||
updateViaDocker: 'Update über Docker Compose:',
|
||||
updateViaHomeAssistant: 'Updates werden vom Home Assistant Supervisor verwaltet. Öffne Einstellungen → Add-ons → Bambuddy in Home Assistant, um die neue Version zu installieren.',
|
||||
updateViaWindowsInstaller: 'Windows-Installationen werden durch erneutes Ausführen des Installers aktualisiert. Lade die neue Version unten herunter — deine Daten, Einstellungen und Drucker bleiben erhalten.',
|
||||
downloadWindowsInstaller: 'Installer für v{{version}} herunterladen',
|
||||
installUpdate: 'Update installieren',
|
||||
latestVersionRunning: 'Sie verwenden die neueste Version',
|
||||
failedToCheckUpdates: 'Update-Prüfung fehlgeschlagen: {{error}}',
|
||||
|
||||
@@ -2305,6 +2305,8 @@ export default {
|
||||
releaseNotes: 'Release Notes',
|
||||
updateViaDocker: 'Update via Docker Compose:',
|
||||
updateViaHomeAssistant: 'Updates are managed by the Home Assistant Supervisor. Open Settings → Add-ons → Bambuddy in Home Assistant to install the new version.',
|
||||
updateViaWindowsInstaller: 'Windows installations are updated by re-running the installer. Download the new version below — your data, settings and printers are preserved.',
|
||||
downloadWindowsInstaller: 'Download installer for v{{version}}',
|
||||
installUpdate: 'Install Update',
|
||||
latestVersionRunning: "You're running the latest version",
|
||||
failedToCheckUpdates: 'Failed to check for updates: {{error}}',
|
||||
|
||||
@@ -2297,6 +2297,8 @@ export default {
|
||||
releaseNotes: 'Notas de la versión',
|
||||
updateViaDocker: 'Actualizar mediante Docker Compose:',
|
||||
updateViaHomeAssistant: 'Las actualizaciones las gestiona el Supervisor de Home Assistant. Abra Ajustes → Complementos → Bambuddy en Home Assistant para instalar la nueva versión.',
|
||||
updateViaWindowsInstaller: 'Las instalaciones en Windows se actualizan volviendo a ejecutar el instalador. Descarga la nueva versión abajo — tus datos, ajustes e impresoras se conservan.',
|
||||
downloadWindowsInstaller: 'Descargar instalador para v{{version}}',
|
||||
installUpdate: 'Instalar actualización',
|
||||
latestVersionRunning: 'Está ejecutando la versión más reciente',
|
||||
failedToCheckUpdates: 'Error al buscar actualizaciones: {{error}}',
|
||||
|
||||
@@ -2244,6 +2244,8 @@ export default {
|
||||
releaseNotes: 'Notes de version',
|
||||
updateViaDocker: 'Mettre à jour via Docker Compose :',
|
||||
updateViaHomeAssistant: 'Les mises à jour sont gérées par le superviseur Home Assistant. Ouvrez Paramètres → Modules complémentaires → Bambuddy dans Home Assistant pour installer la nouvelle version.',
|
||||
updateViaWindowsInstaller: "Les installations Windows se mettent à jour en relançant l'installateur. Téléchargez la nouvelle version ci-dessous — vos données, paramètres et imprimantes sont préservés.",
|
||||
downloadWindowsInstaller: "Télécharger l'installateur pour la v{{version}}",
|
||||
installUpdate: 'Installer la mise à jour',
|
||||
latestVersionRunning: 'Vous utilisez la dernière version',
|
||||
failedToCheckUpdates: 'Échec de la vérification des mises à jour : {{error}}',
|
||||
|
||||
@@ -2243,6 +2243,8 @@ export default {
|
||||
releaseNotes: 'Note di rilascio',
|
||||
updateViaDocker: 'Aggiorna tramite Docker Compose:',
|
||||
updateViaHomeAssistant: 'Gli aggiornamenti sono gestiti dal Supervisor di Home Assistant. Apri Impostazioni → Add-on → Bambuddy in Home Assistant per installare la nuova versione.',
|
||||
updateViaWindowsInstaller: 'Le installazioni Windows si aggiornano rieseguendo l\'installer. Scarica la nuova versione qui sotto — i tuoi dati, le impostazioni e le stampanti vengono mantenuti.',
|
||||
downloadWindowsInstaller: 'Scarica installer per v{{version}}',
|
||||
installUpdate: 'Installa aggiornamento',
|
||||
latestVersionRunning: 'Stai usando l\'ultima versione',
|
||||
failedToCheckUpdates: 'Controllo aggiornamenti fallito: {{error}}',
|
||||
|
||||
@@ -2293,6 +2293,8 @@ export default {
|
||||
releaseNotes: 'リリースノート',
|
||||
updateViaDocker: 'Docker Composeでアップデート:',
|
||||
updateViaHomeAssistant: 'アップデートはHome Assistant Supervisorによって管理されます。Home Assistantの設定→アドオン→Bambuddyを開いて新しいバージョンをインストールしてください。',
|
||||
updateViaWindowsInstaller: 'Windowsインストールはインストーラーを再実行して更新します。下のリンクから新しいバージョンをダウンロードしてください — データ、設定、プリンターは保持されます。',
|
||||
downloadWindowsInstaller: 'v{{version}} のインストーラーをダウンロード',
|
||||
installUpdate: 'アップデートをインストール',
|
||||
latestVersionRunning: '最新バージョンを使用しています',
|
||||
failedToCheckUpdates: 'アップデートの確認に失敗しました: {{error}}',
|
||||
|
||||
@@ -2160,6 +2160,8 @@ export default {
|
||||
releaseNotes: '릴리스 노트',
|
||||
updateViaDocker: 'Docker Compose로 업데이트:',
|
||||
updateViaHomeAssistant: '업데이트는 Home Assistant 수퍼바이저에서 관리됩니다. Home Assistant에서 설정 → 애드온 → Bambuddy를 열어 새 버전을 설치하세요.',
|
||||
updateViaWindowsInstaller: 'Windows 설치본은 설치 프로그램을 다시 실행하여 업데이트합니다. 아래에서 새 버전을 다운로드하세요 — 데이터, 설정 및 프린터는 유지됩니다.',
|
||||
downloadWindowsInstaller: 'v{{version}} 설치 프로그램 다운로드',
|
||||
installUpdate: '업데이트 설치',
|
||||
latestVersionRunning: '최신 버전을 실행 중입니다',
|
||||
failedToCheckUpdates: '업데이트 확인 실패: {{error}}',
|
||||
|
||||
@@ -2243,6 +2243,8 @@ export default {
|
||||
releaseNotes: 'Notas da versão',
|
||||
updateViaDocker: 'Atualizar via Docker Compose:',
|
||||
updateViaHomeAssistant: 'As atualizações são gerenciadas pelo Supervisor do Home Assistant. Abra Configurações → Complementos → Bambuddy no Home Assistant para instalar a nova versão.',
|
||||
updateViaWindowsInstaller: 'Instalações no Windows são atualizadas executando o instalador novamente. Baixe a nova versão abaixo — seus dados, configurações e impressoras são preservados.',
|
||||
downloadWindowsInstaller: 'Baixar instalador da v{{version}}',
|
||||
installUpdate: 'Instalar atualização',
|
||||
latestVersionRunning: 'Você está usando a versão mais recente',
|
||||
failedToCheckUpdates: 'Falha ao verificar atualizações: {{error}}',
|
||||
|
||||
@@ -2297,6 +2297,8 @@ export default {
|
||||
releaseNotes: 'Sürüm Notları',
|
||||
updateViaDocker: 'Docker Compose ile güncelle:',
|
||||
updateViaHomeAssistant: "Güncellemeler Home Assistant Supervisor tarafından yönetilir. Yeni sürümü yüklemek için Home Assistant'ta Ayarlar → Eklentiler → Bambuddy'ye gidin.",
|
||||
updateViaWindowsInstaller: 'Windows kurulumları, kurucu yeniden çalıştırılarak güncellenir. Yeni sürümü aşağıdan indirin — verileriniz, ayarlarınız ve yazıcılarınız korunur.',
|
||||
downloadWindowsInstaller: 'v{{version}} için kurucuyu indir',
|
||||
installUpdate: 'Güncellemeyi Yükle',
|
||||
latestVersionRunning: 'En son sürümü çalıştırıyorsunuz',
|
||||
failedToCheckUpdates: 'Güncellemeler kontrol edilemedi: {{error}}',
|
||||
|
||||
@@ -2288,6 +2288,8 @@ export default {
|
||||
releaseNotes: '发布说明',
|
||||
updateViaDocker: '通过 Docker Compose 更新:',
|
||||
updateViaHomeAssistant: '更新由 Home Assistant Supervisor 管理。请在 Home Assistant 中打开 设置 → 加载项 → Bambuddy 以安装新版本。',
|
||||
updateViaWindowsInstaller: 'Windows 安装通过重新运行安装程序来更新。请在下方下载新版本 — 您的数据、设置和打印机都会保留。',
|
||||
downloadWindowsInstaller: '下载 v{{version}} 安装程序',
|
||||
installUpdate: '安装更新',
|
||||
latestVersionRunning: '您正在运行最新版本',
|
||||
failedToCheckUpdates: '检查更新失败:{{error}}',
|
||||
|
||||
@@ -2288,6 +2288,8 @@ export default {
|
||||
releaseNotes: '發布說明',
|
||||
updateViaDocker: '透過 Docker Compose 更新:',
|
||||
updateViaHomeAssistant: '更新由 Home Assistant Supervisor 管理。請在 Home Assistant 中開啟 設定 → 附加元件 → Bambuddy 以安裝新版本。',
|
||||
updateViaWindowsInstaller: 'Windows 安裝可透過重新執行安裝程式來更新。請在下方下載新版本 — 您的資料、設定和印表機都會保留。',
|
||||
downloadWindowsInstaller: '下載 v{{version}} 安裝程式',
|
||||
installUpdate: '安裝更新',
|
||||
latestVersionRunning: '您正在執行最新版本',
|
||||
failedToCheckUpdates: '檢查更新失敗:{{error}}',
|
||||
|
||||
@@ -768,7 +768,7 @@ export function SettingsPage() {
|
||||
const applyUpdateMutation = useMutation({
|
||||
mutationFn: api.applyUpdate,
|
||||
onSuccess: (data) => {
|
||||
if (data.is_ha_addon || data.is_docker) {
|
||||
if (data.is_ha_addon || data.is_docker || data.is_windows_installer) {
|
||||
showToast(data.message, 'error');
|
||||
} else {
|
||||
refetchUpdateStatus();
|
||||
@@ -2549,6 +2549,21 @@ export function SettingsPage() {
|
||||
docker compose pull && docker compose up -d
|
||||
</code>
|
||||
</div>
|
||||
) : updateCheck?.update_method === 'windows_installer' ? (
|
||||
<div className="mt-3 p-3 bg-bambu-dark-tertiary rounded-lg">
|
||||
<p className="text-sm text-bambu-gray mb-3">
|
||||
{t('settings.updateViaWindowsInstaller')}
|
||||
</p>
|
||||
<a
|
||||
href={updateCheck.installer_download_url || updateCheck.release_url || `https://github.com/maziggy/bambuddy/releases/tag/v${updateCheck.latest_version}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-bambu-dark disabled:opacity-50 bg-bambu-green hover:bg-bambu-green-light text-white focus:ring-bambu-green px-4 py-2 text-sm gap-2 min-h-[44px] md:min-h-0"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
{t('settings.downloadWindowsInstaller', { version: updateCheck.latest_version })}
|
||||
</a>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
className="mt-3"
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -26,7 +26,7 @@
|
||||
|
||||
<!-- Splash screens for iOS -->
|
||||
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
|
||||
<script type="module" crossorigin src="/assets/index-BdzWqWAS.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-Ckvy6dRm.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-7mua31Zk.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user