release 1.0.1: update from Gitea, Linux-only CLI, desktop app for Linux and Windows
CI / Formatação e clippy (push) Successful in 49s
CI / Testes (Linux) (push) Successful in 49s
CI / Auditoria de dependências (push) Successful in 12s
CI / Compila o app desktop (Linux) (push) Successful in 1m12s

The updater reads the latest release from Gitea. update::apply refuses to
overwrite anything but the CLI. Release workflow builds the Linux CLI;
scripts/build-gui.sh builds the Linux and Windows desktop app. Docs say Linux
and Windows only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas RayanandClaude Sonnet 5.5 committed 2026-10-30 13:17:32 -03:00
1 parent 0e31d28342
commit d885d92643
17 files changed
+289 -42

No files matched your search

+40 -6
View File
@@ -1,4 +1,4 @@
//! Auto-atualização simples: verifica a última release no GitHub, baixa o
//! Auto-atualização simples: verifica a última release no Gitea, baixa o
//! binário certo para a plataforma atual, confere o checksum e troca o
//! executável em uso — sem o usuário precisar baixar nada manualmente.
//!
@@ -12,7 +12,13 @@ use std::fs;
use std::path::PathBuf;
use std::process::Command;
const REPO: &str = "lucasrguerra/sabia";
/// A API do Gitea devolve o mesmo formato de `releases/latest` que o GitHub devolvia
/// (`tag_name`, `assets[].name`, `assets[].browser_download_url`).
const LATEST_RELEASE_URL: &str =
"https://git.lucasrguerra.dev.br/api/v1/repos/lucasrguerra/sabia/releases/latest";
/// Página de downloads, para quem usa o app desktop.
const RELEASES_PAGE_URL: &str = "https://git.lucasrguerra.dev.br/lucasrguerra/sabia/releases";
#[derive(Debug, Deserialize)]
struct Release {
@@ -92,8 +98,8 @@ fn asset_suffix_for_platform() -> String {
/// Consulta a última release publicada e compara com a versão atual
/// (`CARGO_PKG_VERSION` do binário chamador).
pub fn check(current_version: &str) -> Result<UpdateStatus> {
let url = format!("https://api.github.com/repos/{REPO}/releases/latest");
let body = match curl_text(&url) {
let url = LATEST_RELEASE_URL;
let body = match curl_text(url) {
Ok(b) => b,
Err(_) => {
// Sem rede, ou repositório ainda sem releases publicadas: não é
@@ -118,8 +124,22 @@ pub fn check(current_version: &str) -> Result<UpdateStatus> {
/// Baixa e aplica a atualização mais recente, substituindo o binário atual.
/// Retorna a versão instalada.
pub fn apply(current_exe: &PathBuf) -> Result<String> {
let url = format!("https://api.github.com/repos/{REPO}/releases/latest");
let body = curl_text(&url)?;
// O pacote da release contém o CLI (`sabia`). Trocar por ele um executável que
// não seja o CLI, como o `sabia-gui`, o deixaria como um CLI e quebraria o app
// na próxima abertura. O app desktop se atualiza baixando o instalador.
let is_cli = current_exe
.file_stem()
.map(|stem| stem == "sabia")
.unwrap_or(false);
if !is_cli {
return Err(SabiaError::CommandFailed {
cmd: "update".into(),
detail: format!(
"a atualização automática vale só para o CLI `sabia`; para o app desktop, baixe o instalador em {RELEASES_PAGE_URL}"
),
});
}
let body = curl_text(LATEST_RELEASE_URL)?;
let release: Release = serde_json::from_str(&body)?;
let suffix = asset_suffix_for_platform();
@@ -225,3 +245,17 @@ fn find_binary(dir: &PathBuf) -> Result<PathBuf> {
detail: "binário não encontrado no pacote baixado".into(),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apply_refuses_to_overwrite_the_desktop_app() {
// Falha antes de qualquer acesso à rede: o nome do executável já não é o do CLI.
let err = apply(&PathBuf::from("/opt/sabia/sabia-gui")).unwrap_err();
assert!(err.to_string().contains("só para o CLI"), "{err}");
let err = apply(&PathBuf::from("C:/Program Files/Sabia/sabia-gui.exe")).unwrap_err();
assert!(err.to_string().contains(RELEASES_PAGE_URL), "{err}");
}
}