feat(update): unify the updater with Sabiá's Gitea-based mechanism
Replaces the signed tauri-plugin-updater (latest.json manifest, minisign key pair) with the same simple mechanism Sabiá already uses: query the Gitea Releases API directly, download the matching platform archive, verify its SHA-256 checksum, and swap the binary in place. The CI job that built the old latest.json manifest is removed in the next commit, alongside an unrelated runner-routing change to the same workflow file. - New guara-core::update module (check/apply), ported from sabia-core::update and adapted to Guará's own release asset naming (guara-<tag>-<platform>.tar.gz, matching scripts/package-cli.sh). - New Engine::update_check / Engine::update_apply, wired into the RPC service for the browser UI. - New CLI subcommand: "guara update" (check) and "guara update --apply" (download, verify, replace), mirroring "sabia update". - GUI: replaced the Tauri plugin-updater flow (download_and_install, update-progress events, in-app restart) with a call to the new update_check/update_apply commands. The desktop app can no longer self-install in one click — it now points the user to the Releases page, same as Sabiá's desktop app already does. - Removed tauri-plugin-updater, createUpdaterArtifacts and the updater.pubkey config, the signing-key loading in build-gui.sh, the CI step testing the old latest.json generator, and the now-unused make-latest-json.py/test_make_latest_json.py scripts. - Documented the break for existing installs in CHANGELOG.md / CHANGELOG.pt-BR.md: versions up to 1.3.2 won't find this release automatically and need one manual download. Trade-off accepted knowingly: SHA-256 verifies integrity against Gitea's own API but, unlike the retired signed mechanism, does not protect against a compromised Gitea server independently of it. This is Lucas's own self-hosted instance, not a third-party mirror. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
This commit is contained in:
1 parent
6328220c60
commit
76e4466652
29 files changed
+373
-913
No files matched your search
+23
-38
@@ -765,61 +765,47 @@ $('#settingsBtn')?.addEventListener('click', () => {
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------ app updater
|
||||
// The desktop shell (Tauri) queries `latest.json` from the Gitea release, verifies the
|
||||
// package signature against the embedded public key, installs, and relaunches. No-op in browser mode (`guara ui`).
|
||||
// The desktop shell (Tauri) queries Gitea Releases API and notifies the user.
|
||||
// No-op in browser mode (`guara ui`).
|
||||
let UPD = null;
|
||||
|
||||
async function checkUpdate(manual) {
|
||||
if (!tauri?.core?.invoke) return;
|
||||
const updNow = $('#updNow');
|
||||
if (updNow) updNow.textContent = 'checando…';
|
||||
try {
|
||||
UPD = await tauri.core.invoke('update_check');
|
||||
renderUpdateCard();
|
||||
if (manual) toast(UPD ? `Nova versão ${UPD.version} disponível` : 'Você já está na versão mais recente');
|
||||
const status = await tauri.core.invoke('update_check');
|
||||
if (status?.update_available) {
|
||||
UPD = {
|
||||
current: status.current_version,
|
||||
version: status.latest_version,
|
||||
url: 'https://git.lucasrguerra.dev.br/lucasrguerra/guara/releases',
|
||||
};
|
||||
renderUpdateCard();
|
||||
if (updNow) updNow.textContent = `atualizar para ${status.latest_version}`;
|
||||
if (manual) toast(`Nova versão ${status.latest_version} disponível`);
|
||||
} else {
|
||||
UPD = null;
|
||||
renderUpdateCard();
|
||||
if (updNow) updNow.textContent = 'já atualizado';
|
||||
if (manual) toast('Você já está na versão mais recente');
|
||||
}
|
||||
} catch (e) {
|
||||
if (updNow) updNow.textContent = 'Verificar agora';
|
||||
if (manual) toast(`Não foi possível verificar atualizações: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
function renderUpdateCard(state) {
|
||||
function renderUpdateCard() {
|
||||
let el = $('#updateCard');
|
||||
if (!UPD) { el?.remove(); return; }
|
||||
if (!el) { el = document.createElement('div'); el.id = 'updateCard'; el.className = 'update-card'; el.setAttribute('role', 'status'); document.body.appendChild(el); }
|
||||
if (state === 'busy') return;
|
||||
el.innerHTML = `<div class="upd-head"><b>Nova versão do Guará disponível</b><span class="mono upd-ver">v${esc(UPD.current)} → v${esc(UPD.version)}</span></div>
|
||||
${UPD.auto ? '' : '<p class="sub">Esta instalação vem de um pacote do sistema e não se atualiza sozinha. Baixe a versão nova na página da release:</p><p class="mono upd-url">' + esc(UPD.url) + '</p>'}
|
||||
<p class="sub">Esta instalação vem de um pacote do sistema e não se atualiza sozinha. Baixe a versão nova na página da release:</p><p class="mono upd-url">${esc(UPD.url)}</p>
|
||||
<div class="upd-actions">
|
||||
${UPD.auto ? '<button class="btn primary sm" id="updGo">Atualizar agora</button>' : ''}
|
||||
${UPD.notes ? '<button class="btn sm" id="updNotes">Novidades</button>' : ''}
|
||||
<button class="btn sm" id="updLater">Depois</button>
|
||||
</div>`;
|
||||
$('#updGo')?.addEventListener('click', installUpdate);
|
||||
$('#updLater')?.addEventListener('click', () => { UPD = null; el.remove(); });
|
||||
$('#updNotes')?.addEventListener('click', () => info({ title: `Guará ${UPD.version}`, html: `<pre class="upd-notes">${esc(UPD.notes)}</pre>` }));
|
||||
}
|
||||
|
||||
async function installUpdate() {
|
||||
const el = $('#updateCard');
|
||||
el.innerHTML = `<div class="upd-head"><b>Atualizando o Guará…</b></div>
|
||||
<div class="bar indet" id="updBar"><i style="width:0"></i></div><p class="sub" id="updMsg">Baixando a atualização…</p>`;
|
||||
renderUpdateCard('busy');
|
||||
try {
|
||||
await tauri.core.invoke('update_install');
|
||||
} catch (e) {
|
||||
toast(String(e));
|
||||
renderUpdateCard();
|
||||
}
|
||||
}
|
||||
|
||||
function updateProgress(p) {
|
||||
const bar = $('#updBar'); const msg = $('#updMsg');
|
||||
if (!bar || !msg) return;
|
||||
if (p.installing) { bar.classList.add('indet'); msg.textContent = 'Instalando… o Guará vai reabrir sozinho.'; return; }
|
||||
if (p.total) {
|
||||
const pct = Math.min(100, Math.round((p.downloaded / p.total) * 100));
|
||||
bar.classList.remove('indet');
|
||||
bar.firstElementChild.style.width = `${pct}%`;
|
||||
msg.textContent = `Baixando… ${pct}%`;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ close while scan is running
|
||||
@@ -870,7 +856,6 @@ function watchClose() {
|
||||
setLang(SETTINGS.lang);
|
||||
if (SETTINGS.ui_lang !== currentLang()) saveSettings({ ui_lang: currentLang() });
|
||||
watchClose();
|
||||
tauri?.event?.listen?.('update-progress', (e) => updateProgress(e.payload));
|
||||
if (SETTINGS.updates !== false) checkUpdate(false);
|
||||
if (tauri) S.elevated = await tauri.core.invoke('is_elevated').catch(() => true);
|
||||
tauri?.event?.listen?.('elevate-failed', (e) => { toast(String(e.payload || 'Não foi possível reiniciar como administrador')); });
|
||||
|
||||
Reference in new issue
Block a user