3 Commits
Author SHA1 Message Date
Lucas RayanandClaude Sonnet 5 5e508bbd14 chore(release): bump version to 1.3.3
CI / Tests (Linux) (push) Failing after 10m8s
CI / Format and clippy (push) Failing after 15m22s
CI / Dependency audit (push) Canceled after 7m7s
CI / Check desktop app (Linux) (push) Canceled after 0s
Release / Create release (push) Successful in 5s
Release / CLI (linux-x86_64) (push) Successful in 3m15s
Release / CLI (linux-aarch64) (push) Successful in 1m39s
Release / Desktop app (linux-aarch64) (push) Successful in 6m23s
Release / Desktop app (linux-x86_64) (push) Successful in 13m45s
Release / Desktop app (windows) (push) Successful in 17m27s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
2026-10-30 22:05:25 -03:00
Lucas RayanandClaude Sonnet 5 40ebf705ed ci(release): drop the updater job, build linux-x86_64 on the native amd64 runner
Two changes to release.yml, kept together since they touch the same
jobs:

1. Drops the 'updater' job ('Build latest.json'): the desktop app no
   longer reads a Tauri-updater manifest (see the previous commit), so
   nothing consumes it any more. Also drops the now-unused
   GUARA_TAURI_SIGNING_PRIVATE_KEY(_PASSWORD) secrets from the 'gui'
   job's env and the header comment documenting them.

2. Routes the linux-x86_64 leg of the cli and gui matrices to the new
   native x86_64 Gitea Actions runner (hostinger-runner, label
   linux-amd64) instead of cross-compiling it from an aarch64 host
   (oracle-runner/-b, label linux-arm64):
   - matrix.include gets an explicit runner field per platform;
     runs-on: ${{ matrix.runner }} picks it per job. windows also
     pinned to linux-amd64 explicitly rather than relying on the
     ubuntu-latest alias, since that alias now points at whichever
     runner is tagged for it and is no longer a safe stand-in for
     "the aarch64 host" (it briefly meant exactly that, then got
     relabeled to the x86_64 runner — pinning explicit labels avoids
     depending on that alias at all).
   - The "Platform cross-linker" step (gcc-x86-64-linux-gnu) only
     runs when NOT on the native amd64 runner, since a native build
     needs no cross-linker.
   - scripts/build-gui.sh and scripts/package-cli.sh are intentionally
     left untouched: both already branch on `uname -m` and take the
     simple native path when run on x86_64. The existing cross-compile
     path (qemu wrappers, the ELF launcher that survives
     tauri-bundler's dd patch) stays in place as a dormant fallback in
     case the single amd64 runner is ever unavailable — deliberately
     kept, not deleted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
2026-10-30 22:04:15 -03:00
Lucas RayanandClaude Sonnet 5 76e4466652 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
2026-10-30 22:03:28 -03:00
32 changed files with 392 additions and 955 deletions

No files matched your search

-2
View File
@@ -39,8 +39,6 @@ jobs:
node-version: 22 node-version: 22
- name: UI translation coverage - name: UI translation coverage
run: node scripts/check-ui-i18n.mjs run: node scripts/check-ui-i18n.mjs
- name: Updater latest.json unit tests
run: python3 scripts/test_make_latest_json.py
- run: cargo fmt --all -- --check - run: cargo fmt --all -- --check
- run: cargo clippy --workspace --all-targets - run: cargo clippy --workspace --all-targets
+12 -35
View File
@@ -1,16 +1,12 @@
name: Release name: Release
# Triggered when a v* tag is pushed: creates the Gitea Release entry with notes # Triggered when a v* tag is pushed: creates the Gitea Release entry with notes
# from CHANGELOG.md, builds the CLI (Linux x86_64 and aarch64) and the desktop # from CHANGELOG.md and uploads the compiled CLI (scripts/package-cli.sh) with
# app (Linux aarch64, Linux x86_64, and Windows via cargo-xwin cross-compilation), # a .sha256 checksum alongside each archive, plus the desktop bundles (Linux
# and generates the latest.json manifest read by the desktop updater. # aarch64, Linux x86_64, and Windows via cargo-xwin).
# #
# No macOS: Tauri requires Apple's SDK and code-signing hardware/account, which # No macOS: Tauri requires Apple's SDK and code-signing hardware/account, which
# this Linux ARM64 runner does not have. # this Linux ARM64 runner does not have.
#
# Required repository/user secrets: GUARA_TAURI_SIGNING_PRIVATE_KEY and
# GUARA_TAURI_SIGNING_PRIVATE_KEY_PASSWORD (sign updater bundles). Unprefixed
# secret names remain supported as fallback.
on: on:
push: push:
tags: ['v*'] tags: ['v*']
@@ -50,22 +46,22 @@ jobs:
cli: cli:
name: CLI (${{ matrix.platform }}) name: CLI (${{ matrix.platform }})
needs: release needs: release
runs-on: ubuntu-latest runs-on: ${{ matrix.runner }}
env: env:
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/guara-release-cli-${{ matrix.platform }} CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/guara-release-cli-${{ matrix.platform }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- { platform: linux-aarch64, target: aarch64-unknown-linux-gnu, apt: "" } - { platform: linux-aarch64, target: aarch64-unknown-linux-gnu, apt: "", runner: linux-arm64 }
- { platform: linux-x86_64, target: x86_64-unknown-linux-gnu, apt: "gcc-x86-64-linux-gnu" } - { platform: linux-x86_64, target: x86_64-unknown-linux-gnu, apt: "gcc-x86-64-linux-gnu", runner: linux-amd64 }
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
targets: ${{ matrix.target }} targets: ${{ matrix.target }}
- name: Platform cross-linker - name: Platform cross-linker
if: ${{ matrix.apt != '' }} if: ${{ matrix.apt != '' && matrix.runner != 'linux-amd64' }}
run: scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/cli-x86_64 ${{ matrix.apt }} run: scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/cli-x86_64 ${{ matrix.apt }}
- name: Build and package - name: Build and package
run: scripts/package-cli.sh "${{ gitea.ref_name }}" "${{ matrix.platform }}" "${{ matrix.target }}" | tee packages.txt run: scripts/package-cli.sh "${{ gitea.ref_name }}" "${{ matrix.platform }}" "${{ matrix.target }}" | tee packages.txt
@@ -78,15 +74,16 @@ jobs:
gui: gui:
name: Desktop app (${{ matrix.platform }}) name: Desktop app (${{ matrix.platform }})
needs: release needs: release
runs-on: ubuntu-latest runs-on: ${{ matrix.runner }}
env: env:
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/guara-release-gui-${{ matrix.platform }} CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/guara-release-gui-${{ matrix.platform }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.GUARA_TAURI_SIGNING_PRIVATE_KEY || secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.GUARA_TAURI_SIGNING_PRIVATE_KEY_PASSWORD || secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
platform: [linux-aarch64, linux-x86_64, windows] include:
- { platform: linux-aarch64, runner: linux-arm64 }
- { platform: linux-x86_64, runner: linux-amd64 }
- { platform: windows, runner: linux-amd64 }
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
@@ -129,23 +126,3 @@ jobs:
TOKEN: ${{ gitea.token }} TOKEN: ${{ gitea.token }}
API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }} API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}
run: scripts/release-upload.sh "${{ gitea.ref_name }}" dist-gui/* run: scripts/release-upload.sh "${{ gitea.ref_name }}" dist-gui/*
updater:
name: Build latest.json
needs: gui
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: latest.json
env:
TAG: ${{ gitea.ref_name }}
TOKEN: ${{ gitea.token }}
API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}
run: |
scripts/release-notes.sh "$TAG" > notes.md
python3 scripts/make-latest-json.py "$TAG" notes.md latest.json
- name: Upload to release
env:
TOKEN: ${{ gitea.token }}
API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}
run: scripts/release-upload.sh "${{ gitea.ref_name }}" latest.json
+11
View File
@@ -4,6 +4,17 @@
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow SemVer. Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow SemVer.
## [1.3.3] - 2026-10-30
Unified updater architecture with Sabiá: migrated from the signed `tauri-plugin-updater` to direct Gitea Releases API checking and self-updating with SHA-256 verification.
### Changed
- **New update mechanism:** The updater was rewritten to match Sabiá's model. Updates now query the Gitea Releases API directly, verify package checksums via `.sha256`, and no longer rely on `latest.json` or minisign updater keys.
- **CLI self-update (`guara update`):** The CLI now supports `guara update` (checks for new versions) and `guara update --apply` (downloads, verifies SHA-256 checksum, and atomically replaces the binary).
- **Desktop app notice:** The desktop app checks Gitea on startup and links directly to the new release packages.
- **Important notice for existing installations:** Versions up to 1.3.2 used the previous signed `tauri-plugin-updater` mechanism looking for `latest.json`. Because that mechanism has been retired, existing installs will not automatically find version 1.3.3: download version 1.3.3 once manually from Gitea Releases, and future update checks will work through the new unified mechanism.
- Removed `tauri-plugin-updater` dependency, `createUpdaterArtifacts` configuration, and the `updater` (`Build latest.json`) CI job.
## [1.3.2] - 2026-10-30 ## [1.3.2] - 2026-10-30
The project moved from GitHub to Gitea, and the update check followed it. The project moved from GitHub to Gitea, and the update check followed it.
+11
View File
@@ -4,6 +4,17 @@
Formato baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/); versões seguem SemVer. Formato baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/); versões seguem SemVer.
## [1.3.3] - 2026-10-30
Arquitetura de atualizador unificada com o Sabiá: migração do `tauri-plugin-updater` assinado para consulta direta à API de Releases do Gitea e autoatualização com verificação de SHA-256.
### Alterado
- **Novo mecanismo de atualização:** O atualizador foi reescrito para seguir o modelo do Sabiá. As atualizações agora consultam diretamente a API de Releases do Gitea, conferem a integridade via `.sha256` e não dependem mais de `latest.json` nem de chaves minisign.
- **Autoatualização do CLI (`guara update`):** O CLI agora conta com `guara update` (para verificar novas versões) e `guara update --apply` (que baixa, confere o SHA-256 e substitui o binário atomicamente).
- **Aviso no app desktop:** O app desktop consulta o Gitea ao abrir e direciona para os novos pacotes da release.
- **Aviso importante para instalações existentes:** Versões até a 1.3.2 usavam o mecanismo assinado do `tauri-plugin-updater` baseado em `latest.json`. Como esse mecanismo foi descontinuado, instalações anteriores não encontrarão a versão 1.3.3 sozinhas: baixe a versão 1.3.3 manualmente uma vez nas Releases do Gitea, e as próximas verificações funcionarão pelo novo mecanismo unificado.
- Removidos a dependência `tauri-plugin-updater`, a configuração `createUpdaterArtifacts` e o job de CI `updater` (`Build latest.json`).
## [1.3.2] - 2026-10-30 ## [1.3.2] - 2026-10-30
O projeto saiu do GitHub para o Gitea, e a verificação de atualização foi junto. O projeto saiu do GitHub para o Gitea, e a verificação de atualização foi junto.
+1 -3
View File
@@ -23,13 +23,11 @@ On Linux the tests build images with `mtools`, `ntfs-3g`, `e2fsprogs`, `exfatpro
For the external reference images (BitLocker, FileVault 2, dfvfs, AFF4), run `scripts/fetch-testdata.sh`; without them those tests are skipped. The BitLocker test takes about 75 s. For the external reference images (BitLocker, FileVault 2, dfvfs, AFF4), run `scripts/fetch-testdata.sh`; without them those tests are skipped. The BitLocker test takes about 75 s.
The desktop app signs its update packages: `tauri.conf.json` has `createUpdaterArtifacts` on, so a local `tauri build` needs `TAURI_SIGNING_PRIVATE_KEY` (any key made with `tauri signer generate`; the release key lives only in the repository secrets). For a quick local bundle, pass `--config '{"bundle":{"createUpdaterArtifacts":false}}'`.
## Releases and Versioning ## Releases and Versioning
Versions strictly follow **Semantic Versioning 2.0.0** (`MAJOR.MINOR.PATCH`), synchronized across `Cargo.toml`, `apps/gui/src-tauri/Cargo.toml`, and `apps/gui/src-tauri/tauri.conf.json` (enforced by `scripts/check.sh`). Versions strictly follow **Semantic Versioning 2.0.0** (`MAJOR.MINOR.PATCH`), synchronized across `Cargo.toml`, `apps/gui/src-tauri/Cargo.toml`, and `apps/gui/src-tauri/tauri.conf.json` (enforced by `scripts/check.sh`).
Pushing a `v*` tag makes the Gitea CI create the release (notes come from `CHANGELOG.md`, so update it first), attach the Linux CLI (x86_64 and aarch64), the desktop app for Linux (x86_64 and aarch64) and Windows, and build the updater's `latest.json`. Pushing a `v*` tag makes the Gitea CI create the release (notes come from `CHANGELOG.md`, so update it first) and attach the Linux CLI (x86_64 and aarch64) and the desktop app for Linux (x86_64 and aarch64) and Windows.
There is no macOS build: it needs Apple's SDK and code signing, which a Linux runner cannot provide. There is no macOS build: it needs Apple's SDK and code signing, which a Linux runner cannot provide.
+1 -3
View File
@@ -23,13 +23,11 @@ No Linux os testes geram imagens com `mtools`, `ntfs-3g`, `e2fsprogs`, `exfatpro
Para as imagens de referência externas (BitLocker, FileVault 2, dfvfs, AFF4), rode `scripts/fetch-testdata.sh`; sem elas esses testes são pulados. O teste de BitLocker leva cerca de 75 s. Para as imagens de referência externas (BitLocker, FileVault 2, dfvfs, AFF4), rode `scripts/fetch-testdata.sh`; sem elas esses testes são pulados. O teste de BitLocker leva cerca de 75 s.
O app desktop assina os pacotes de atualização: o `tauri.conf.json` tem `createUpdaterArtifacts` ligado, então um `tauri build` local precisa de `TAURI_SIGNING_PRIVATE_KEY` (qualquer chave feita com `tauri signer generate`; a chave da release fica só nos segredos do repositório). Para um pacote local rápido, passe `--config '{"bundle":{"createUpdaterArtifacts":false}}'`.
## Releases e Versionamento ## Releases e Versionamento
As versões seguem estritamente o **Versionamento Semântico 2.0.0** (`MAJOR.MINOR.PATCH`), sincronizadas entre `Cargo.toml`, `apps/gui/src-tauri/Cargo.toml` e `apps/gui/src-tauri/tauri.conf.json` (validado por `scripts/check.sh`). As versões seguem estritamente o **Versionamento Semântico 2.0.0** (`MAJOR.MINOR.PATCH`), sincronizadas entre `Cargo.toml`, `apps/gui/src-tauri/Cargo.toml` e `apps/gui/src-tauri/tauri.conf.json` (validado por `scripts/check.sh`).
Enviar uma tag `v*` faz o CI do Gitea criar a release (as notas vêm do `CHANGELOG.md`, então atualize-o antes), anexar o CLI para Linux (x86_64 e aarch64), o app desktop para Linux (x86_64 e aarch64) e Windows, e montar o `latest.json` do atualizador. Enviar uma tag `v*` faz o CI do Gitea criar a release (as notas vêm do `CHANGELOG.md`, então atualize-o antes) e anexar o CLI para Linux (x86_64 e aarch64) e o app desktop para Linux (x86_64 e aarch64) e Windows.
Não há build para macOS: ele exige o SDK e a assinatura da Apple, que um runner Linux não tem. Não há build para macOS: ele exige o SDK e a assinatura da Apple, que um runner Linux não tem.
Generated
+2 -2
View File
@@ -416,7 +416,7 @@ dependencies = [
[[package]] [[package]]
name = "guara-cli" name = "guara-cli"
version = "1.3.2" version = "1.3.3"
dependencies = [ dependencies = [
"clap", "clap",
"ctrlc", "ctrlc",
@@ -429,7 +429,7 @@ dependencies = [
[[package]] [[package]]
name = "guara-core" name = "guara-core"
version = "1.3.2" version = "1.3.3"
dependencies = [ dependencies = [
"aes", "aes",
"argon2", "argon2",
+1 -1
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/guara-core", "crates/guara-cli"] members = ["crates/guara-core", "crates/guara-cli"]
[workspace.package] [workspace.package]
version = "1.3.2" version = "1.3.3"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://git.lucasrguerra.dev.br/lucasrguerra/guara" repository = "https://git.lucasrguerra.dev.br/lucasrguerra/guara"
+1 -1
View File
@@ -45,7 +45,7 @@ The CLI is Linux-only; the desktop app ships for Linux and Windows. Every CLI
package has a `.sha256` next to it. To build from source: `cargo build package has a `.sha256` next to it. To build from source: `cargo build
--release -p guara-cli` (Rust 1.80+, Linux). --release -p guara-cli` (Rust 1.80+, Linux).
**Updates:** the desktop app (AppImage and Windows) checks Gitea for a newer version when it opens; if there is one, a card offers **Update now**: it downloads, verifies the package signature, installs and reopens, so you no longer swap the AppImage by hand. The check is a single request for the release's `latest.json` and nothing of yours is sent; turn it off in **Settings**. Installs from `.deb`/`.rpm` only show the notice, because the package manager owns those files. **Updates:** the desktop app checks Gitea on startup; when a new version exists, a notice offers the release link. The CLI updates the same way (`guara update`); it downloads the right binary for your platform, verifies the **SHA-256** and swaps the running executable — no manual download. The check is a single request to the releases API and sends nothing of yours.
> The binaries are **not signed yet**: Windows SmartScreen will warn you. Check the SHA-256. > The binaries are **not signed yet**: Windows SmartScreen will warn you. Check the SHA-256.
+1 -1
View File
@@ -45,7 +45,7 @@ O CLI é só para Linux; o app desktop sai para Linux e Windows. Cada pacote do
CLI tem um `.sha256` ao lado. Compilar do código: `cargo build --release -p CLI tem um `.sha256` ao lado. Compilar do código: `cargo build --release -p
guara-cli` (Rust 1.80+, Linux). guara-cli` (Rust 1.80+, Linux).
**Atualizações:** o app desktop (AppImage e Windows) consulta o Gitea ao abrir; havendo versão nova, um cartão oferece **Atualizar agora**: ele baixa, confere a assinatura do pacote, instala e reabre, e você deixa de trocar o AppImage na mão. A consulta é uma única requisição ao `latest.json` da release e nada seu é enviado; desligue em **Configurações**. Instalações por `.deb`/`.rpm` só mostram o aviso, porque quem manda nesses arquivos é o gerenciador de pacotes. **Atualizações:** o app desktop consulta o Gitea ao abrir; havendo versão nova, um aviso oferece o link da release. O CLI atualiza do mesmo jeito (`guara update`); ele baixa o binário certo para a plataforma, confere o **SHA-256** e troca o executável em uso — você não precisa baixar nada na mão. A consulta é uma única requisição à API de releases e nada seu é enviado.
> Os binários ainda **não são assinados**: o Windows SmartScreen vai avisar. Confira o SHA-256. > Os binários ainda **não são assinados**: o Windows SmartScreen vai avisar. Confira o SHA-256.
+18 -514
View File
@@ -450,16 +450,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.10.1" version = "0.10.1"
@@ -483,7 +473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
dependencies = [ dependencies = [
"bitflags 2.13.2", "bitflags 2.13.2",
"core-foundation 0.10.1", "core-foundation",
"core-graphics-types", "core-graphics-types",
"foreign-types", "foreign-types",
"libc", "libc",
@@ -496,7 +486,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [ dependencies = [
"bitflags 2.13.2", "bitflags 2.13.2",
"core-foundation 0.10.1", "core-foundation",
"libc", "libc",
] ]
@@ -897,16 +887,6 @@ dependencies = [
"typeid", "typeid",
] ]
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.5.0" version = "2.5.0"
@@ -932,16 +912,6 @@ dependencies = [
"rustc_version", "rustc_version",
] ]
[[package]]
name = "filetime"
version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
dependencies = [
"cfg-if",
"libc",
]
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.15" version = "0.1.15"
@@ -1187,17 +1157,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.3.4" version = "0.3.4"
@@ -1383,7 +1342,7 @@ dependencies = [
[[package]] [[package]]
name = "guara-core" name = "guara-core"
version = "1.3.2" version = "1.3.3"
dependencies = [ dependencies = [
"aes", "aes",
"argon2", "argon2",
@@ -1414,7 +1373,7 @@ dependencies = [
[[package]] [[package]]
name = "guara-gui" name = "guara-gui"
version = "1.3.2" version = "1.3.3"
dependencies = [ dependencies = [
"guara-core", "guara-core",
"libc", "libc",
@@ -1423,7 +1382,6 @@ dependencies = [
"tauri", "tauri",
"tauri-build", "tauri-build",
"tauri-plugin-dialog", "tauri-plugin-dialog",
"tauri-plugin-updater",
] ]
[[package]] [[package]]
@@ -1548,21 +1506,6 @@ dependencies = [
"want", "want",
] ]
[[package]]
name = "hyper-rustls"
version = "0.27.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53"
dependencies = [
"http",
"hyper",
"hyper-util",
"rustls",
"tokio",
"tokio-rustls",
"tower-service",
]
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.21" version = "0.1.21"
@@ -1582,11 +1525,9 @@ dependencies = [
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"system-configuration",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
"windows-registry",
] ]
[[package]] [[package]]
@@ -1905,36 +1846,6 @@ dependencies = [
"windows-sys 0.45.0", "windows-sys 0.45.0",
] ]
[[package]]
name = "jni"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
dependencies = [
"cfg-if",
"combine",
"jni-macros",
"jni-sys 0.4.1",
"log",
"simd_cesu8",
"thiserror 2.0.21",
"walkdir",
"windows-link",
]
[[package]]
name = "jni-macros"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"simd_cesu8",
"syn 2.0.119",
]
[[package]] [[package]]
name = "jni-sys" name = "jni-sys"
version = "0.3.1" version = "0.3.1"
@@ -2082,12 +1993,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]] [[package]]
name = "litemap" name = "litemap"
version = "0.8.3" version = "0.8.3"
@@ -2163,12 +2068,6 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minisign-verify"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "871285dc19d8d0ebe0eef3d0e99a205f2a71363b122632cbbfa4a6c370a960ce"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.8.9" version = "0.8.9"
@@ -2456,18 +2355,6 @@ dependencies = [
"objc2-core-foundation", "objc2-core-foundation",
] ]
[[package]]
name = "objc2-osa-kit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
dependencies = [
"bitflags 2.13.2",
"objc2",
"objc2-app-kit",
"objc2-foundation",
]
[[package]] [[package]]
name = "objc2-quartz-core" name = "objc2-quartz-core"
version = "0.3.2" version = "0.3.2"
@@ -2531,32 +2418,12 @@ version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "openssl-probe"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e036b411754132fd6b74e94b74e9a13c5cf606fa93d1885c35860d3060820b1"
[[package]] [[package]]
name = "option-ext" name = "option-ext"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "osakit"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
dependencies = [
"objc2",
"objc2-foundation",
"objc2-osa-kit",
"serde",
"serde_json",
"thiserror 2.0.21",
]
[[package]] [[package]]
name = "pango" name = "pango"
version = "0.18.3" version = "0.18.3"
@@ -2961,20 +2828,15 @@ dependencies = [
"http-body", "http-body",
"http-body-util", "http-body-util",
"hyper", "hyper",
"hyper-rustls",
"hyper-util", "hyper-util",
"js-sys", "js-sys",
"log", "log",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls",
"rustls-pki-types",
"rustls-platform-verifier",
"serde", "serde",
"serde_json", "serde_json",
"sync_wrapper", "sync_wrapper",
"tokio", "tokio",
"tokio-rustls",
"tokio-util", "tokio-util",
"tower", "tower",
"tower-http", "tower-http",
@@ -3012,20 +2874,6 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.17",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "ripemd" name = "ripemd"
version = "0.2.0" version = "0.2.0"
@@ -3050,92 +2898,6 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "rustix"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5e76742e5294153d4dde1207dbec5bdd81eef6fabdc0d2118babadabfd858aa"
dependencies = [
"bitflags 2.13.2",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls"
version = "0.23.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf"
dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a073f5dc7469f984c52ad2752b63b0807745133b6de880b7b64c1ac4c48aec4"
dependencies = [
"openssl-probe",
"rustls-pki-types",
"schannel",
"security-framework",
]
[[package]]
name = "rustls-pki-types"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-platform-verifier"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98"
dependencies = [
"core-foundation 0.10.1",
"core-foundation-sys",
"jni 0.22.4",
"log",
"once_cell",
"rustls",
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki",
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f"
[[package]]
name = "rustls-webpki"
version = "0.103.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.23" version = "1.0.23"
@@ -3160,15 +2922,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "schannel"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cb23c424d8ee841bb4d813677ca237e2a610067d594fec130f87d6093495ab5"
dependencies = [
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "schemars" name = "schemars"
version = "0.8.22" version = "0.8.22"
@@ -3226,29 +2979,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [
"bitflags 2.13.2",
"core-foundation 0.10.1",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "selectors" name = "selectors"
version = "0.38.0" version = "0.38.0"
@@ -3482,22 +3212,6 @@ version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
[[package]]
name = "simd_cesu8"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520"
dependencies = [
"rustc_version",
"simdutf8",
]
[[package]]
name = "simdutf8"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "1.0.4" version = "1.0.4"
@@ -3622,12 +3336,6 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "swift-rs" name = "swift-rs"
version = "1.0.9" version = "1.0.9"
@@ -3691,27 +3399,6 @@ dependencies = [
"syn 3.0.9", "syn 3.0.9",
] ]
[[package]]
name = "system-configuration"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
dependencies = [
"bitflags 2.13.2",
"core-foundation 0.9.4",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "system-deps" name = "system-deps"
version = "6.2.2" version = "6.2.2"
@@ -3733,7 +3420,7 @@ checksum = "d5777b06fe6435126ae8d816eba269e050e1e10d302cf5461e9c49eccabc3548"
dependencies = [ dependencies = [
"bitflags 2.13.2", "bitflags 2.13.2",
"block2", "block2",
"core-foundation 0.10.1", "core-foundation",
"core-graphics", "core-graphics",
"crossbeam-channel", "crossbeam-channel",
"dbus", "dbus",
@@ -3743,7 +3430,7 @@ dependencies = [
"gdkwayland-sys", "gdkwayland-sys",
"gdkx11-sys", "gdkx11-sys",
"gtk", "gtk",
"jni 0.21.1", "jni",
"libc", "libc",
"log", "log",
"ndk", "ndk",
@@ -3776,17 +3463,6 @@ dependencies = [
"syn 2.0.119", "syn 2.0.119",
] ]
[[package]]
name = "tar"
version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
dependencies = [
"filetime",
"libc",
"xattr",
]
[[package]] [[package]]
name = "target-lexicon" name = "target-lexicon"
version = "0.12.16" version = "0.12.16"
@@ -3810,7 +3486,7 @@ dependencies = [
"gtk", "gtk",
"heck 0.5.0", "heck 0.5.0",
"http", "http",
"jni 0.21.1", "jni",
"libc", "libc",
"log", "log",
"mime", "mime",
@@ -3966,40 +3642,6 @@ dependencies = [
"url", "url",
] ]
[[package]]
name = "tauri-plugin-updater"
version = "2.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d957abbcb3f9f6c76d22a3a0f012f30880640d0bccb5634d2b12a9937e4c872b"
dependencies = [
"base64 0.23.1",
"dirs",
"flate2",
"futures-util",
"http",
"infer",
"libc",
"log",
"minisign-verify",
"osakit",
"percent-encoding",
"reqwest",
"rustls",
"semver",
"serde",
"serde_json",
"tar",
"tauri",
"tauri-plugin",
"tempfile",
"thiserror 2.0.21",
"time",
"tokio",
"url",
"windows-sys 0.61.2",
"zip",
]
[[package]] [[package]]
name = "tauri-runtime" name = "tauri-runtime"
version = "2.12.2" version = "2.12.2"
@@ -4010,7 +3652,7 @@ dependencies = [
"dpi", "dpi",
"gtk", "gtk",
"http", "http",
"jni 0.21.1", "jni",
"objc2", "objc2",
"objc2-ui-kit", "objc2-ui-kit",
"objc2-web-kit", "objc2-web-kit",
@@ -4033,7 +3675,7 @@ checksum = "e1aa9d8b7a120f879f44fdc1053f7b7dbf416bf0bbc6a72b714dec46b782b52f"
dependencies = [ dependencies = [
"gtk", "gtk",
"http", "http",
"jni 0.21.1", "jni",
"log", "log",
"objc2", "objc2",
"objc2-app-kit", "objc2-app-kit",
@@ -4099,19 +3741,6 @@ dependencies = [
"toml 1.1.8+spec-1.1.0", "toml 1.1.8+spec-1.1.0",
] ]
[[package]]
name = "tempfile"
version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom 0.4.3",
"once_cell",
"rustix",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "tendril" name = "tendril"
version = "0.5.1" version = "0.5.1"
@@ -4233,16 +3862,6 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "tokio-rustls"
version = "0.26.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db"
dependencies = [
"rustls",
"tokio",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.20" version = "0.7.20"
@@ -4480,12 +4099,6 @@ version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.8" version = "2.5.8"
@@ -4748,15 +4361,6 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "webpki-root-certs"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e49d0e65abc00c142e8f9428da991da9f1a250c39be97459901eb12b9b49f62a"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "webview2-com" name = "webview2-com"
version = "0.39.1" version = "0.39.1"
@@ -4927,17 +4531,6 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "windows-registry"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
dependencies = [
"windows-link",
"windows-result",
"windows-strings",
]
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.4.1" version = "0.4.1"
@@ -4962,16 +4555,7 @@ version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [ dependencies = [
"windows-targets 0.42.2", "windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
] ]
[[package]] [[package]]
@@ -4989,29 +4573,13 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.42.2", "windows_aarch64_gnullvm",
"windows_aarch64_msvc 0.42.2", "windows_aarch64_msvc",
"windows_i686_gnu 0.42.2", "windows_i686_gnu",
"windows_i686_msvc 0.42.2", "windows_i686_msvc",
"windows_x86_64_gnu 0.42.2", "windows_x86_64_gnu",
"windows_x86_64_gnullvm 0.42.2", "windows_x86_64_gnullvm",
"windows_x86_64_msvc 0.42.2", "windows_x86_64_msvc",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
] ]
[[package]] [[package]]
@@ -5038,90 +4606,42 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.42.2" version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.42.2" version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.42.2" version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.42.2" version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.42.2" version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.42.2" version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.5.40" version = "0.5.40"
@@ -5180,7 +4700,7 @@ dependencies = [
"gtk", "gtk",
"http", "http",
"javascriptcore-rs", "javascriptcore-rs",
"jni 0.21.1", "jni",
"libc", "libc",
"ndk", "ndk",
"objc2", "objc2",
@@ -5226,16 +4746,6 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "xattr"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963d6fab92d5504b971dc8bd90ca743c2575e590bc8071ea5bc571598667ae08"
dependencies = [
"libc",
"rustix",
]
[[package]] [[package]]
name = "yoke" name = "yoke"
version = "0.8.4" version = "0.8.4"
@@ -5280,12 +4790,6 @@ dependencies = [
"synstructure", "synstructure",
] ]
[[package]]
name = "zeroize"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879"
[[package]] [[package]]
name = "zerotrie" name = "zerotrie"
version = "0.2.5" version = "0.2.5"
+1 -2
View File
@@ -3,7 +3,7 @@
[package] [package]
name = "guara-gui" name = "guara-gui"
description = "Guará: graphical file recovery interface" description = "Guará: graphical file recovery interface"
version = "1.3.2" version = "1.3.3"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
authors = ["Guará contributors"] authors = ["Guará contributors"]
@@ -18,7 +18,6 @@ tauri-build = { version = "2", features = [] }
[dependencies] [dependencies]
tauri = { version = "2", features = [] } tauri = { version = "2", features = [] }
tauri-plugin-dialog = "2" tauri-plugin-dialog = "2"
tauri-plugin-updater = "2"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
guara-core = { path = "../../../crates/guara-core" } guara-core = { path = "../../../crates/guara-core" }
@@ -1 +1 @@
{"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener","allow-supports-multiple-windows"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-exit":{"identifier":"allow-exit","description":"Enables the exit command without any pre-configured scope.","commands":{"allow":["exit"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-supports-multiple-windows":{"identifier":"allow-supports-multiple-windows","description":"Enables the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":["supports_multiple_windows"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-exit":{"identifier":"deny-exit","description":"Denies the exit command without any pre-configured scope.","commands":{"allow":[],"deny":["exit"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"DenLine truncated {"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener","allow-supports-multiple-windows"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-exit":{"identifier":"allow-exit","description":"Enables the exit command without any pre-configured scope.","commands":{"allow":["exit"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-supports-multiple-windows":{"identifier":"allow-supports-multiple-windows","description":"Enables the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":["supports_multiple_windows"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-exit":{"identifier":"deny-exit","description":"Denies the exit command without any pre-configured scope.","commands":{"allow":[],"deny":["exit"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"DenLine truncated
@@ -2281,60 +2281,6 @@
"type": "string", "type": "string",
"const": "dialog:deny-save", "const": "dialog:deny-save",
"markdownDescription": "Denies the save command without any pre-configured scope." "markdownDescription": "Denies the save command without any pre-configured scope."
},
{
"description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`",
"type": "string",
"const": "updater:default",
"markdownDescription": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`"
},
{
"description": "Enables the check command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-check",
"markdownDescription": "Enables the check command without any pre-configured scope."
},
{
"description": "Enables the download command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-download",
"markdownDescription": "Enables the download command without any pre-configured scope."
},
{
"description": "Enables the download_and_install command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-download-and-install",
"markdownDescription": "Enables the download_and_install command without any pre-configured scope."
},
{
"description": "Enables the install command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-install",
"markdownDescription": "Enables the install command without any pre-configured scope."
},
{
"description": "Denies the check command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-check",
"markdownDescription": "Denies the check command without any pre-configured scope."
},
{
"description": "Denies the download command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-download",
"markdownDescription": "Denies the download command without any pre-configured scope."
},
{
"description": "Denies the download_and_install command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-download-and-install",
"markdownDescription": "Denies the download_and_install command without any pre-configured scope."
},
{
"description": "Denies the install command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-install",
"markdownDescription": "Denies the install command without any pre-configured scope."
} }
] ]
}, },
@@ -2281,60 +2281,6 @@
"type": "string", "type": "string",
"const": "dialog:deny-save", "const": "dialog:deny-save",
"markdownDescription": "Denies the save command without any pre-configured scope." "markdownDescription": "Denies the save command without any pre-configured scope."
},
{
"description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`",
"type": "string",
"const": "updater:default",
"markdownDescription": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`"
},
{
"description": "Enables the check command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-check",
"markdownDescription": "Enables the check command without any pre-configured scope."
},
{
"description": "Enables the download command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-download",
"markdownDescription": "Enables the download command without any pre-configured scope."
},
{
"description": "Enables the download_and_install command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-download-and-install",
"markdownDescription": "Enables the download_and_install command without any pre-configured scope."
},
{
"description": "Enables the install command without any pre-configured scope.",
"type": "string",
"const": "updater:allow-install",
"markdownDescription": "Enables the install command without any pre-configured scope."
},
{
"description": "Denies the check command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-check",
"markdownDescription": "Denies the check command without any pre-configured scope."
},
{
"description": "Denies the download command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-download",
"markdownDescription": "Denies the download command without any pre-configured scope."
},
{
"description": "Denies the download_and_install command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-download-and-install",
"markdownDescription": "Denies the download_and_install command without any pre-configured scope."
},
{
"description": "Denies the install command without any pre-configured scope.",
"type": "string",
"const": "updater:deny-install",
"markdownDescription": "Denies the install command without any pre-configured scope."
} }
] ]
}, },
+6 -74
View File
@@ -21,82 +21,15 @@ async fn rpc(
.map_err(|e| e.to_string())? .map_err(|e| e.to_string())?
} }
/// New version found on Gitea releases.
#[derive(serde::Serialize)]
struct UpdateInfo {
current: String,
version: String,
notes: String,
/// Whether the app can self-update (AppImage, Windows, and macOS). Package installs
/// (.deb/.rpm) only notify the user and open the release page.
auto: bool,
url: String,
}
fn self_update_possible() -> bool {
!cfg!(target_os = "linux") || std::env::var_os("APPIMAGE").is_some()
}
/// Checks for a newer version. `None` = already up to date.
#[tauri::command] #[tauri::command]
async fn update_check(app: tauri::AppHandle) -> Result<Option<UpdateInfo>, String> { fn update_check(state: tauri::State<'_, State>) -> Result<serde_json::Value, String> {
use tauri_plugin_updater::UpdaterExt; let status = state.0.update_check().map_err(|e| e.to_string())?;
let found = app serde_json::to_value(status).map_err(|e| e.to_string())
.updater()
.map_err(|e| e.to_string())?
.check()
.await
.map_err(|e| e.to_string())?;
Ok(found.map(|u| UpdateInfo {
current: u.current_version.clone(),
notes: u.body.clone().unwrap_or_default(),
url: format!(
"https://git.lucasrguerra.dev.br/lucasrguerra/guara/releases/tag/v{}",
u.version
),
version: u.version,
auto: self_update_possible(),
}))
} }
/// Downloads, verifies the signature (embedded public key), installs, and restarts the app.
/// Progress is emitted via the `update-progress` event.
#[tauri::command] #[tauri::command]
async fn update_install(app: tauri::AppHandle) -> Result<(), String> { fn update_apply(state: tauri::State<'_, State>) -> Result<String, String> {
use tauri::Emitter; state.0.update_apply().map_err(|e| e.to_string())
use tauri_plugin_updater::UpdaterExt;
if !self_update_possible() {
return Err("Esta instalação não se atualiza sozinha (pacote do sistema). Baixe a versão nova na página da release.".into());
}
let update = app
.updater()
.map_err(|e| e.to_string())?
.check()
.await
.map_err(|e| e.to_string())?
.ok_or("Já está na versão mais recente.")?;
let (a, b) = (app.clone(), app.clone());
let mut got = 0u64;
update
.download_and_install(
move |chunk, total| {
got += chunk as u64;
let _ = a.emit(
"update-progress",
serde_json::json!({ "downloaded": got, "total": total }),
);
},
move || {
let _ = b.emit("update-progress", serde_json::json!({ "installing": true }));
},
)
.await
.map_err(|e| format!("Não foi possível atualizar: {e}"))?;
// When running as administrator, the new file is owned by root: hand it back to the invoking user.
if let Some(p) = std::env::var_os("APPIMAGE") {
guara_core::owner::give_to_user(std::path::Path::new(&p));
}
app.restart()
} }
/// `true` when the process already has sufficient privileges to read raw disks. /// `true` when the process already has sufficient privileges to read raw disks.
@@ -308,7 +241,6 @@ fn main() {
let for_protocol = engine.clone(); let for_protocol = engine.clone();
tauri::Builder::default() tauri::Builder::default()
.plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_updater::Builder::new().build())
.manage(State(engine)) .manage(State(engine))
.register_asynchronous_uri_scheme_protocol("guara", move |_ctx, request, responder| { .register_asynchronous_uri_scheme_protocol("guara", move |_ctx, request, responder| {
let eng = for_protocol.clone(); let eng = for_protocol.clone();
@@ -349,7 +281,7 @@ fn main() {
is_elevated, is_elevated,
relaunch_elevated, relaunch_elevated,
update_check, update_check,
update_install update_apply
]) ])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("erro ao iniciar o Guará"); .expect("erro ao iniciar o Guará");
+2 -14
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "Guará", "productName": "Guará",
"version": "1.3.2", "version": "1.3.3",
"identifier": "dev.guara.app", "identifier": "dev.guara.app",
"build": { "build": {
"frontendDist": "../ui" "frontendDist": "../ui"
@@ -34,18 +34,6 @@
"icons/128x128@2x.png", "icons/128x128@2x.png",
"icons/icon.icns", "icons/icon.icns",
"icons/icon.ico" "icons/icon.ico"
], ]
"createUpdaterArtifacts": true
},
"plugins": {
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDhENzRCRUU5NEI3QTAxQzgKUldUSUFYcEw2YjUwamFzMnpXM3ZKTEZPN1JIeXdLL3Q5SEF1M1FxQ284WFM0UWNsdlAyNUloWTEK",
"endpoints": [
"https://git.lucasrguerra.dev.br/lucasrguerra/guara/releases/latest/download/latest.json"
],
"windows": {
"installMode": "passive"
}
}
} }
} }
+23 -38
View File
@@ -765,61 +765,47 @@ $('#settingsBtn')?.addEventListener('click', () => {
}); });
// ------------------------------------------------------------------ app updater // ------------------------------------------------------------------ app updater
// The desktop shell (Tauri) queries `latest.json` from the Gitea release, verifies the // The desktop shell (Tauri) queries Gitea Releases API and notifies the user.
// package signature against the embedded public key, installs, and relaunches. No-op in browser mode (`guara ui`). // No-op in browser mode (`guara ui`).
let UPD = null; let UPD = null;
async function checkUpdate(manual) { async function checkUpdate(manual) {
if (!tauri?.core?.invoke) return; if (!tauri?.core?.invoke) return;
const updNow = $('#updNow');
if (updNow) updNow.textContent = 'checando…';
try { try {
UPD = await tauri.core.invoke('update_check'); const status = await tauri.core.invoke('update_check');
renderUpdateCard(); if (status?.update_available) {
if (manual) toast(UPD ? `Nova versão ${UPD.version} disponível` : 'Você já está na versão mais recente'); 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) { } catch (e) {
if (updNow) updNow.textContent = 'Verificar agora';
if (manual) toast(`Não foi possível verificar atualizações: ${e}`); if (manual) toast(`Não foi possível verificar atualizações: ${e}`);
} }
} }
function renderUpdateCard(state) { function renderUpdateCard() {
let el = $('#updateCard'); let el = $('#updateCard');
if (!UPD) { el?.remove(); return; } 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 (!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> 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"> <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> <button class="btn sm" id="updLater">Depois</button>
</div>`; </div>`;
$('#updGo')?.addEventListener('click', installUpdate);
$('#updLater')?.addEventListener('click', () => { UPD = null; el.remove(); }); $('#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 // ------------------------------------------------------------------ close while scan is running
@@ -870,7 +856,6 @@ function watchClose() {
setLang(SETTINGS.lang); setLang(SETTINGS.lang);
if (SETTINGS.ui_lang !== currentLang()) saveSettings({ ui_lang: currentLang() }); if (SETTINGS.ui_lang !== currentLang()) saveSettings({ ui_lang: currentLang() });
watchClose(); watchClose();
tauri?.event?.listen?.('update-progress', (e) => updateProgress(e.payload));
if (SETTINGS.updates !== false) checkUpdate(false); if (SETTINGS.updates !== false) checkUpdate(false);
if (tauri) S.elevated = await tauri.core.invoke('is_elevated').catch(() => true); 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')); }); tauri?.event?.listen?.('elevate-failed', (e) => { toast(String(e.payload || 'Não foi possível reiniciar como administrador')); });
+3
View File
@@ -587,6 +587,9 @@ export const EN = {
'Atualizações automáticas': 'Automatic updates', 'Atualizações automáticas': 'Automatic updates',
'Ao abrir, consulta o Gitea para ver se há versão nova (uma consulta, sem enviar dados seus)': 'On startup, checks Gitea for a new version (one request, nothing of yours is sent)', 'Ao abrir, consulta o Gitea para ver se há versão nova (uma consulta, sem enviar dados seus)': 'On startup, checks Gitea for a new version (one request, nothing of yours is sent)',
'Verificar agora': 'Check now', 'Verificar agora': 'Check now',
'checando…': 'checking…',
'atualizar para {n}': 'update to {n}',
'já atualizado': 'already up to date',
'Nova versão {n} disponível': 'New version {n} available', 'Nova versão {n} disponível': 'New version {n} available',
'Você já está na versão mais recente': 'You are already on the latest version', 'Você já está na versão mais recente': 'You are already on the latest version',
'Não foi possível verificar atualizações: {n}': 'Could not check for updates: {n}', 'Não foi possível verificar atualizações: {n}': 'Could not check for updates: {n}',
+25
View File
@@ -190,6 +190,11 @@ enum Cmd {
Hash { path: String }, Hash { path: String },
/// List autosaved sessions /// List autosaved sessions
Sessions, Sessions,
/// Check for and apply `guara` CLI updates
Update {
#[arg(long)]
apply: bool,
},
/// Open the web UI in the browser (local mode) /// Open the web UI in the browser (local mode)
Ui { Ui {
#[arg(long, default_value_t = 0)] #[arg(long, default_value_t = 0)]
@@ -666,6 +671,26 @@ fn run(cli: Cli) -> Result<(), String> {
} }
Ok(()) Ok(())
} }
Cmd::Update { apply } => {
let current = env!("CARGO_PKG_VERSION");
if apply {
let exe = std::env::current_exe().map_err(|e| e.to_string())?;
let version = guara_core::update::apply(&exe).map_err(|e| e.to_string())?;
println!("atualizado para {version}");
} else {
let status = guara_core::update::check(current).map_err(|e| e.to_string())?;
if status.update_available {
println!(
"atualização disponível: {} -> {} (rode `guara update --apply`)",
status.current_version,
status.latest_version.unwrap_or_default()
);
} else {
println!("já na última versão ({})", status.current_version);
}
}
Ok(())
}
Cmd::Ui { port, no_open } => serve::run(port, !no_open), Cmd::Ui { port, no_open } => serve::run(port, !no_open),
} }
} }
+1
View File
@@ -50,6 +50,7 @@ pub mod source;
pub mod sqlite; pub mod sqlite;
pub mod thumb; pub mod thumb;
pub mod types; pub mod types;
pub mod update;
pub mod vdisk; pub mod vdisk;
pub mod volume; pub mod volume;
+17
View File
@@ -1303,9 +1303,26 @@ impl Engine {
"version" => { "version" => {
json!({ "version": env!("CARGO_PKG_VERSION"), "os": std::env::consts::OS }) json!({ "version": env!("CARGO_PKG_VERSION"), "os": std::env::consts::OS })
} }
"update_check" => {
let st = self.update_check()?;
json!(st)
}
"update_apply" => {
let version = self.update_apply()?;
json!({ "version": version })
}
other => return Err(Error::msg(format!("método desconhecido: {other}"))), other => return Err(Error::msg(format!("método desconhecido: {other}"))),
}) })
} }
pub fn update_check(&self) -> Result<crate::update::UpdateStatus> {
crate::update::check(crate::VERSION)
}
pub fn update_apply(&self) -> Result<String> {
let exe = std::env::current_exe()?;
crate::update::apply(&exe)
}
} }
pub use crate::explorer::Query; pub use crate::explorer::Query;
+1 -1
View File
@@ -23,7 +23,7 @@ pub struct Settings {
/// Show OpenStreetMap tiles for geotagged photos (fetches tiles over the network). /// Show OpenStreetMap tiles for geotagged photos (fetches tiles over the network).
#[serde(default)] #[serde(default)]
pub maps: bool, pub maps: bool,
/// Check Gitea Releases for updates on startup (fetches `latest.json` once /// Check Gitea Releases for updates on startup (one query to the releases API,
/// without sending any telemetry). /// without sending any telemetry).
#[serde(default = "yes")] #[serde(default = "yes")]
pub updates: bool, pub updates: bool,
+237
View File
@@ -0,0 +1,237 @@
//! CLI self-updater: queries the latest Gitea Release, downloads the matching
//! platform archive, verifies its SHA-256 checksum, and atomically replaces the
//! running `guara` binary.
//!
//! Uses the system `curl` and `tar` binaries to avoid pulling a full TLS stack
//! into `guara-core`.
use crate::error::{Error, Result};
use serde::Deserialize;
use sha2::{Digest, Sha256};
use std::fs;
use std::path::PathBuf;
use std::process::Command;
/// Gitea `releases/latest` API endpoint (`tag_name`, `assets[].name`, `assets[].browser_download_url`).
const LATEST_RELEASE_URL: &str =
"https://git.lucasrguerra.dev.br/api/v1/repos/lucasrguerra/guara/releases/latest";
/// Human-facing Gitea Releases page for desktop GUI users.
const RELEASES_PAGE_URL: &str = "https://git.lucasrguerra.dev.br/lucasrguerra/guara/releases";
#[derive(Debug, Deserialize)]
struct Release {
tag_name: String,
assets: Vec<Asset>,
}
#[derive(Debug, Deserialize)]
struct Asset {
name: String,
browser_download_url: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct UpdateStatus {
pub current_version: String,
pub latest_version: Option<String>,
pub update_available: bool,
}
fn curl_text(url: &str) -> Result<String> {
let out = Command::new("curl")
.args(["-fsSL", "-H", "User-Agent: guara-updater", url])
.output()
.map_err(|e| Error::msg(format!("curl {url}: {e}")))?;
if !out.status.success() {
return Err(Error::msg(format!(
"curl {url}: {}",
String::from_utf8_lossy(&out.stderr)
)));
}
Ok(String::from_utf8_lossy(&out.stdout).into_owned())
}
fn curl_download(url: &str, dest: &PathBuf) -> Result<()> {
let status = Command::new("curl")
.args(["-fsSL", "-H", "User-Agent: guara-updater", "-o"])
.arg(dest)
.arg(url)
.status()
.map_err(|e| Error::msg(format!("curl {url}: {e}")))?;
if !status.success() {
return Err(Error::msg(format!("curl {url}: download falhou")));
}
Ok(())
}
/// Returns the expected release asset suffix for the current OS/architecture
/// (e.g. `"-linux-x86_64.tar.gz"`).
fn asset_suffix_for_platform() -> String {
let os = if cfg!(target_os = "linux") {
"linux"
} else if cfg!(target_os = "macos") {
"macos"
} else {
"windows"
};
let arch = if cfg!(target_arch = "x86_64") {
"x86_64"
} else {
"aarch64"
};
let ext = if cfg!(windows) { "zip" } else { "tar.gz" };
format!("-{os}-{arch}.{ext}")
}
/// Queries the latest published release and compares it against the running
/// `CARGO_PKG_VERSION`.
pub fn check(current_version: &str) -> Result<UpdateStatus> {
let url = LATEST_RELEASE_URL;
let body = match curl_text(url) {
Ok(b) => b,
Err(_) => {
// Offline or no published releases yet: treat as no update available.
return Ok(UpdateStatus {
current_version: current_version.to_string(),
latest_version: None,
update_available: false,
});
}
};
let release: Release = serde_json::from_str(&body)?;
let latest = release.tag_name.trim_start_matches('v').to_string();
let update_available = latest != current_version;
Ok(UpdateStatus {
current_version: current_version.to_string(),
latest_version: Some(latest),
update_available,
})
}
/// Downloads, verifies, and installs the latest CLI release in place.
/// Returns the newly installed version string.
pub fn apply(current_exe: &PathBuf) -> Result<String> {
// Guard against replacing `guara-gui` with the CLI binary (`guara`);
// desktop GUI updates use the installer/package from the release page instead.
let is_cli = current_exe
.file_stem()
.map(|stem| stem == "guara")
.unwrap_or(false);
if !is_cli {
return Err(Error::msg(format!(
"a atualização automática vale só para o CLI `guara`; 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();
let asset = release
.assets
.iter()
.find(|a| a.name.ends_with(&suffix))
.ok_or_else(|| {
Error::msg(format!(
"nenhum asset terminado em `{suffix}` na release {}",
release.tag_name
))
})?;
let checksum_asset = release
.assets
.iter()
.find(|a| a.name == format!("{}.sha256", asset.name));
let tmp_dir = std::env::temp_dir().join("guara-update");
fs::create_dir_all(&tmp_dir)?;
let archive_path = tmp_dir.join(&asset.name);
curl_download(&asset.browser_download_url, &archive_path)?;
if let Some(checksum_asset) = checksum_asset {
let expected = curl_text(&checksum_asset.browser_download_url)?;
let expected_hash = expected
.split_whitespace()
.next()
.unwrap_or("")
.to_lowercase();
let data = fs::read(&archive_path)?;
let mut hasher = Sha256::new();
hasher.update(&data);
let actual_hash: String = hasher
.finalize()
.iter()
.map(|b| format!("{b:02x}"))
.collect();
if actual_hash != expected_hash {
return Err(Error::msg(
"checksum SHA-256 não confere — atualização descartada",
));
}
}
// Extract the archive (`tar xf` auto-detects `.tar.gz` and `.zip` on modern
// libarchive/bsdtar) and atomically replace the current executable.
let extract_dir = tmp_dir.join("extracted");
let _ = fs::remove_dir_all(&extract_dir);
fs::create_dir_all(&extract_dir)?;
let status = Command::new("tar")
.arg("xf")
.arg(&archive_path)
.arg("-C")
.arg(&extract_dir)
.status()?;
if !status.success() {
return Err(Error::msg("tar xf: extração falhou"));
}
let new_binary = find_binary(&extract_dir)?;
let backup = current_exe.with_extension("bak");
let _ = fs::remove_file(&backup);
fs::rename(current_exe, &backup)?;
fs::copy(&new_binary, current_exe)?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(current_exe)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(current_exe, perms)?;
}
Ok(release.tag_name)
}
/// Locates `name` at the archive root or one directory level below
/// (`guara-vX.Y.Z-<platform>/guara`).
fn find_binary(dir: &PathBuf) -> Result<PathBuf> {
fn is_binary(name: &std::ffi::OsStr) -> bool {
name == "guara" || name == "guara.exe"
}
for entry in fs::read_dir(dir)? {
let entry = entry?;
if is_binary(&entry.file_name()) {
return Ok(entry.path());
}
if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) {
for inner in fs::read_dir(entry.path())? {
let inner = inner?;
if is_binary(&inner.file_name()) {
return Ok(inner.path());
}
}
}
}
Err(Error::msg("binário não encontrado no pacote baixado"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apply_refuses_to_overwrite_the_desktop_app() {
let err = apply(&PathBuf::from("/opt/guara/guara-gui")).unwrap_err();
assert!(err.to_string().contains("só para o CLI"), "{err}");
let err = apply(&PathBuf::from("C:/Program Files/Guara/guara-gui.exe")).unwrap_err();
assert!(err.to_string().contains(RELEASES_PAGE_URL), "{err}");
}
}
+7
View File
@@ -149,3 +149,10 @@ guara report s.json report.html --case 2026-001 --examiner "Name" --hash-source
``` ```
Includes case identification, source, partitions, summary, hashes, the audit trail status and the list of files. `--hash-source` adds the hash of the whole source. The format follows the extension: `report.pdf` writes an A4 PDF (standard fonts, nothing external), anything else writes HTML. Includes case identification, source, partitions, summary, hashes, the audit trail status and the list of files. `--hash-source` adds the hash of the whole source. The format follows the extension: `report.pdf` writes an A4 PDF (standard fonts, nothing external), anything else writes HTML.
## `update` — updating
```bash
guara update # check for a new version
guara update --apply # download, verify the SHA-256 and swap the executable
```
+1 -1
View File
@@ -29,7 +29,7 @@ The switch at the bottom of the sidebar offers **Auto** (follows the system), **
## Updates ## Updates
When the desktop app opens it asks Gitea for the release's `latest.json`. If a newer version exists, a card appears at the bottom right with the versions, **What's new** (the CHANGELOG notes) and **Update now**. The app downloads the package, verifies its signature against the public key built into the app, installs and reopens by itself. On Linux this works for the AppImage (the file is replaced in place); installs from `.deb` or `.rpm` only show the notice with the release address. **Settings > Automatic updates** turns the startup check off and has a **Check now** button. The check sends nothing besides what any browser sends to Gitea. `guara ui` in the browser does not update itself. When the desktop app opens it asks Gitea for the latest release. If a newer version exists, a card appears at the bottom right with the versions and the release address. For the CLI, `guara update` checks for and installs updates with SHA-256 verification. **Settings > Automatic updates** turns the startup check off and has a **Check now** button. The check sends nothing besides what any browser sends to Gitea. `guara ui` in the browser does not update itself.
<p align="center"><img src="img/settings-language-en.png" width="420" alt="The Settings dialog with the Language selector set to English"></p> <p align="center"><img src="img/settings-language-en.png" width="420" alt="The Settings dialog with the Language selector set to English"></p>
+7
View File
@@ -149,3 +149,10 @@ guara report s.json relatorio.html --case 2026-001 --examiner "Nome" --hash-sour
``` ```
Traz identificação do caso, origem, partições, resumo, hashes, a situação da trilha de auditoria e a lista dos arquivos. `--hash-source` inclui o hash da origem inteira. O formato segue a extensão: `relatorio.pdf` gera um PDF A4 (fontes padrão, nada externo); qualquer outra gera HTML. Traz identificação do caso, origem, partições, resumo, hashes, a situação da trilha de auditoria e a lista dos arquivos. `--hash-source` inclui o hash da origem inteira. O formato segue a extensão: `relatorio.pdf` gera um PDF A4 (fontes padrão, nada externo); qualquer outra gera HTML.
## `update` — atualizações
```bash
guara update # verifica se há versão nova
guara update --apply # baixa, confere o SHA-256 e troca o executável
```
+1 -1
View File
@@ -29,7 +29,7 @@ Em **Configurações** (a engrenagem no fim da barra lateral) há o seletor **Id
## Atualizações ## Atualizações
Ao abrir, o app desktop pede ao Gitea o `latest.json` da release. Havendo versão mais nova, aparece um cartão no canto inferior direito com as versões, **Novidades** (as notas do CHANGELOG) e **Atualizar agora**. O app baixa o pacote, confere a assinatura com a chave pública embutida nele, instala e reabre sozinho. No Linux isso vale para o AppImage (o arquivo é substituído no lugar); instalações por `.deb` ou `.rpm` só mostram o aviso com o endereço da release. **Configurações > Atualizações automáticas** desliga a verificação ao abrir e tem o botão **Verificar agora**. A consulta não envia nada além do que qualquer navegador envia ao Gitea. O `guara ui` no navegador não se atualiza sozinho. Ao abrir, o app desktop consulta o Gitea para verificar se há uma versão mais nova. Havendo versão nova, aparece um cartão no canto inferior direito com as versões e o endereço da release. Para o CLI, `guara update` verifica e instala atualizações com conferência de SHA-256. **Configurações > Atualizações automáticas** desliga a verificação ao abrir e tem o botão **Verificar agora**. A consulta não envia nada além do que qualquer navegador envia ao Gitea. O `guara ui` no navegador não se atualiza sozinho.
<p align="center"><img src="../img/settings-language-pt.png" width="420" alt="A janela de Configurações com o seletor de Idioma"></p> <p align="center"><img src="../img/settings-language-pt.png" width="420" alt="A janela de Configurações com o seletor de Idioma"></p>
-15
View File
@@ -7,11 +7,6 @@
# scripts/build-gui.sh windows # NSIS installer, cross-built from Linux via cargo-xwin # scripts/build-gui.sh windows # NSIS installer, cross-built from Linux via cargo-xwin
# scripts/build-gui.sh windows-docker # Same as above inside an isolated container # scripts/build-gui.sh windows-docker # Same as above inside an isolated container
# #
# The Tauri updater signs each bundle (createUpdaterArtifacts: true), so
# TAURI_SIGNING_PRIVATE_KEY and TAURI_SIGNING_PRIVATE_KEY_PASSWORD must be set
# in the environment. If unset locally, this script loads ~/.tauri/guara-updater.key
# when available.
#
# Windows builds use cargo-xwin and NSIS (requires: nsis, lld, llvm, clang, and # Windows builds use cargo-xwin and NSIS (requires: nsis, lld, llvm, clang, and
# `cargo install cargo-xwin`). # `cargo install cargo-xwin`).
set -euo pipefail set -euo pipefail
@@ -20,19 +15,12 @@ cd "$(dirname "$0")/.."
platform=${1:?usage: build-gui.sh <linux|linux-x86_64|linux-aarch64|windows|windows-docker>} platform=${1:?usage: build-gui.sh <linux|linux-x86_64|linux-aarch64|windows|windows-docker>}
tauri() { npx --yes @tauri-apps/cli@2 "$@"; } tauri() { npx --yes @tauri-apps/cli@2 "$@"; }
if [ -z "${TAURI_SIGNING_PRIVATE_KEY:-}" ] && [ -f "$HOME/.tauri/guara-updater.key" ]; then
export TAURI_SIGNING_PRIVATE_KEY="$(cat "$HOME/.tauri/guara-updater.key")"
[ -f "$HOME/.tauri/guara-updater.pass" ] && export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="$(cat "$HOME/.tauri/guara-updater.pass")"
fi
: "${TAURI_SIGNING_PRIVATE_KEY:?set TAURI_SIGNING_PRIVATE_KEY (or generate a local key with 'tauri signer generate')}"
if [ "$platform" = windows-docker ]; then if [ "$platform" = windows-docker ]; then
docker volume create guara-cargo >/dev/null docker volume create guara-cargo >/dev/null
docker volume create guara-rustup >/dev/null docker volume create guara-rustup >/dev/null
mkdir -p dist-gui mkdir -p dist-gui
exec docker run --rm -v "$PWD":/src -v guara-cargo:/root/.cargo -v guara-rustup:/root/.rustup \ exec docker run --rm -v "$PWD":/src -v guara-cargo:/root/.cargo -v guara-rustup:/root/.rustup \
-w /src -e HOST_UID="$(id -u)" -e HOST_GID="$(id -g)" \ -w /src -e HOST_UID="$(id -u)" -e HOST_GID="$(id -g)" \
-e TAURI_SIGNING_PRIVATE_KEY -e TAURI_SIGNING_PRIVATE_KEY_PASSWORD \
ubuntu:24.04 bash -c ' ubuntu:24.04 bash -c '
set -e set -e
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
@@ -81,7 +69,6 @@ case "$platform" in
rm -rf "$bundle/appimage" "$bundle/deb" rm -rf "$bundle/appimage" "$bundle/deb"
tauri build --bundles appimage,deb tauri build --bundles appimage,deb
cp "$bundle"/appimage/*.AppImage "$bundle"/deb/*.deb ../../../dist-gui/ cp "$bundle"/appimage/*.AppImage "$bundle"/deb/*.deb ../../../dist-gui/
cp "$bundle"/appimage/*.AppImage.sig ../../../dist-gui/ 2>/dev/null || true
;; ;;
linux-x86_64) linux-x86_64)
if [ "$(uname -m)" = x86_64 ]; then if [ "$(uname -m)" = x86_64 ]; then
@@ -169,7 +156,6 @@ EOF
tauri build --target x86_64-unknown-linux-gnu --bundles appimage,deb tauri build --target x86_64-unknown-linux-gnu --bundles appimage,deb
fi fi
cp "$bundle"/appimage/*.AppImage "$bundle"/deb/*.deb ../../../dist-gui/ cp "$bundle"/appimage/*.AppImage "$bundle"/deb/*.deb ../../../dist-gui/
cp "$bundle"/appimage/*.AppImage.sig ../../../dist-gui/ 2>/dev/null || true
;; ;;
windows) windows)
rustup target add x86_64-pc-windows-msvc >/dev/null rustup target add x86_64-pc-windows-msvc >/dev/null
@@ -177,7 +163,6 @@ EOF
rm -rf "$bundle" rm -rf "$bundle"
tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc --bundles nsis tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc --bundles nsis
cp "$bundle"/*-setup.exe ../../../dist-gui/ cp "$bundle"/*-setup.exe ../../../dist-gui/
cp "$bundle"/*-setup.exe.sig ../../../dist-gui/ 2>/dev/null || true
;; ;;
*) echo "unknown platform: $platform" >&2; exit 2 ;; *) echo "unknown platform: $platform" >&2; exit 2 ;;
esac esac
-1
View File
@@ -21,7 +21,6 @@ step "formatting (cargo fmt)" cargo fmt --all -- --check
# Same flags as CI: warnings are treated as errors across all targets, including tests # Same flags as CI: warnings are treated as errors across all targets, including tests
step "clippy (workspace, all targets)" env RUSTFLAGS="-D warnings" cargo clippy --workspace --all-targets step "clippy (workspace, all targets)" env RUSTFLAGS="-D warnings" cargo clippy --workspace --all-targets
step "UI translation coverage" node scripts/check-ui-i18n.mjs step "UI translation coverage" node scripts/check-ui-i18n.mjs
step "latest.json unit tests" python3 -B scripts/test_make_latest_json.py
step "bash script syntax" bash -c 'for f in scripts/*.sh .githooks/*; do bash -n "$f" || exit 1; done' step "bash script syntax" bash -c 'for f in scripts/*.sh .githooks/*; do bash -n "$f" || exit 1; done'
step "workflows and templates (valid YAML)" python3 -c " step "workflows and templates (valid YAML)" python3 -c "
import glob, yaml import glob, yaml
-82
View File
@@ -1,82 +0,0 @@
#!/usr/bin/env python3
"""Builds the latest.json manifest read by the Tauri updater from the release
bundles and `.sig` signatures already uploaded to the Gitea release.
Runs in the final publish job after all matrix packaging jobs complete. Building
the manifest in a single step avoids race conditions from parallel matrix jobs.
Usage: make-latest-json.py <tag> <notes.md> <out.json>
Requires API (Gitea repository API URL) and TOKEN in the environment.
"""
import json
import os
import sys
import urllib.parse
import urllib.request
from datetime import datetime, timezone
# (platform keys, package filename patterns). {v} = version without leading "v".
# No macOS: no Apple hardware/signing runner is available.
PLATFORMS = [
(["linux-x86_64", "linux-x86_64-appimage"], ("Guará_{v}_amd64.AppImage", "Guara_{v}_amd64.AppImage")),
(["linux-aarch64", "linux-aarch64-appimage"], ("Guará_{v}_aarch64.AppImage", "Guara_{v}_aarch64.AppImage")),
(["windows-x86_64", "windows-x86_64-nsis"], ("Guará_{v}_x64-setup.exe", "Guara_{v}_x64-setup.exe")),
]
def api_get(path):
req = urllib.request.Request(f"{os.environ['API']}{path}", headers={"Authorization": f"token {os.environ['TOKEN']}"})
return json.load(urllib.request.urlopen(req))
def api_get_text(url):
safe_url = urllib.parse.quote(url, safe=":/?&=%")
req = urllib.request.Request(safe_url, headers={"Authorization": f"token {os.environ['TOKEN']}"})
return urllib.request.urlopen(req).read().decode()
def build(version, notes, assets, pub_date):
"""`assets` = {package_name: (url, signature)}. Returns the latest.json dict.
Platforms missing a package or signature are omitted; the caller decides whether that is fatal."""
platforms = {}
for keys, patterns in PLATFORMS:
name = next((n for p in patterns if (n := p.format(v=version)) in assets), None)
if name is None:
continue
url, sig = assets[name]
for k in keys:
platforms.setdefault(k, {"signature": sig.strip(), "url": url})
return {"version": version, "notes": notes.strip(), "pub_date": pub_date, "platforms": platforms}
def main():
tag, notes_path, out = sys.argv[1:4]
version = tag.lstrip("v")
rel = api_get(f"/releases/tags/{tag}")
by_name = {a["name"]: a for a in rel["assets"]}
assets = {}
for _, patterns in PLATFORMS:
name = next((n for p in patterns if (n := p.format(v=version)) in by_name), None)
if name is None:
continue
sig_name = name + ".sig"
if sig_name in by_name:
sig = api_get_text(by_name[sig_name]["browser_download_url"])
assets[name] = (by_name[name]["browser_download_url"], sig)
data = build(version, open(notes_path, encoding="utf-8").read(), assets,
datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"))
# Require signed packages for Linux (x86_64 and aarch64) and Windows.
platforms = data["platforms"]
for must in ("linux-x86_64", "linux-aarch64", "windows-x86_64"):
if must not in platforms:
sys.exit(f"missing package or signature for {must} (found {sorted(platforms)}); "
"verify that TAURI_SIGNING_PRIVATE_KEY is set in repository secrets")
with open(out, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
f.write("\n")
print(f"{out}: {sorted(data['platforms'])}")
if __name__ == "__main__":
main()
-55
View File
@@ -1,55 +0,0 @@
#!/usr/bin/env python3
"""Unit tests for scripts/make-latest-json.py: python3 scripts/test_make_latest_json.py"""
import importlib.util
import os
import sys
import unittest
sys.dont_write_bytecode = True
spec = importlib.util.spec_from_file_location("m", os.path.join(os.path.dirname(__file__), "make-latest-json.py"))
m = importlib.util.module_from_spec(spec)
spec.loader.exec_module(m)
BASE = "https://git.lucasrguerra.dev.br/lucasrguerra/guara/releases/download/v1.3.2/"
def assets(version="1.3.2"):
names = [patterns[0].format(v=version) for _, patterns in m.PLATFORMS]
return {n: (BASE + n, f"sig-of-{n}\n") for n in names}
class LatestJson(unittest.TestCase):
def test_all_platforms_and_variants(self):
d = m.build("1.3.2", "### What's New\n", assets(), "2026-10-30T12:00:00Z")
self.assertEqual(d["version"], "1.3.2")
self.assertEqual(d["notes"], "### What's New")
p = d["platforms"]
for k in ("linux-x86_64", "linux-x86_64-appimage", "linux-aarch64", "windows-x86_64", "windows-x86_64-nsis"):
self.assertIn(k, p)
self.assertTrue(p["windows-x86_64"]["url"].endswith("x64-setup.exe"))
self.assertEqual(p["linux-x86_64"]["signature"], "sig-of-Guará_1.3.2_amd64.AppImage")
def test_missing_package_is_left_out(self):
a = assets()
del a["Guará_1.3.2_x64-setup.exe"]
d = m.build("1.3.2", "", a, "2026-10-30T12:00:00Z")
self.assertNotIn("windows-x86_64", d["platforms"])
self.assertIn("linux-x86_64", d["platforms"])
def test_other_versions_do_not_leak(self):
d = m.build("1.3.2", "", assets("1.2.0"), "2026-10-30T12:00:00Z")
self.assertEqual(d["platforms"], {})
def test_accepts_legacy_unaccented_name(self):
a = assets()
old = "Guara_1.3.2_amd64.AppImage"
_, sig = a.pop("Guará_1.3.2_amd64.AppImage")
a[old] = (BASE + old, sig)
d = m.build("1.3.2", "", a, "2026-10-30T12:00:00Z")
self.assertIn("linux-x86_64", d["platforms"])
self.assertTrue(d["platforms"]["linux-x86_64"]["url"].endswith(old))
if __name__ == "__main__":
unittest.main()