From d33f8692f848d7fb9cd96f86a4bd989bac96e45f Mon Sep 17 00:00:00 2001 From: nartsisss Date: Thu, 26 Mar 2026 10:01:52 +0300 Subject: [PATCH] tabularis: init at 0.9.12 --- .../ta/tabularis/disable-updater.patch | 543 ++++++++++++++++++ pkgs/by-name/ta/tabularis/package.nix | 76 +++ 2 files changed, 619 insertions(+) create mode 100644 pkgs/by-name/ta/tabularis/disable-updater.patch create mode 100644 pkgs/by-name/ta/tabularis/package.nix diff --git a/pkgs/by-name/ta/tabularis/disable-updater.patch b/pkgs/by-name/ta/tabularis/disable-updater.patch new file mode 100644 index 000000000000..e6aef0136f7d --- /dev/null +++ b/pkgs/by-name/ta/tabularis/disable-updater.patch @@ -0,0 +1,543 @@ +diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml +index e74f8c5..5333b4a 100644 +--- a/src-tauri/Cargo.toml ++++ b/src-tauri/Cargo.toml +@@ -59,8 +59,5 @@ tauri-plugin-clipboard-manager = "2" + [target.'cfg(target_os = "linux")'.dependencies] + gtk = "0.18" + +-[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] +-tauri-plugin-updater = "2" +- + [dev-dependencies] + tempfile = "3.24.0" +diff --git a/src-tauri/capabilities/desktop.json b/src-tauri/capabilities/desktop.json +index ba6d080..3069583 100644 +--- a/src-tauri/capabilities/desktop.json ++++ b/src-tauri/capabilities/desktop.json +@@ -9,6 +9,5 @@ + "main" + ], + "permissions": [ +- "updater:default" + ] +-} +\ No newline at end of file ++} +diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs +index a7a62b7..6010aca 100644 +--- a/src-tauri/src/lib.rs ++++ b/src-tauri/src/lib.rs +@@ -21,7 +21,6 @@ pub mod ssh_tunnel; + pub mod theme_commands; + pub mod theme_models; + pub mod task_manager; +-pub mod updater; + pub mod plugins; + pub mod drivers { + pub mod common; +@@ -137,7 +136,6 @@ pub fn run() { + .plugin(tauri_plugin_opener::init()) + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_fs::init()) +- .plugin(tauri_plugin_updater::Builder::new().build()) + .manage(commands::QueryCancellationState::default()) + .manage(export::ExportCancellationState::default()) + .manage(dump_commands::DumpCancellationState::default()) +@@ -284,10 +282,6 @@ pub fn run() { + dump_commands::import_database, + dump_commands::cancel_import, + dump_commands::cancel_dump, +- // Updater +- updater::check_for_updates, +- updater::download_and_install_update, +- updater::get_installation_source, + // Logs + log_commands::get_logs, + log_commands::clear_logs, +diff --git a/src-tauri/src/updater.rs b/src-tauri/src/updater.rs +deleted file mode 100644 +index 6351f27..0000000 +--- a/src-tauri/src/updater.rs ++++ /dev/null +@@ -1,435 +0,0 @@ +-use reqwest::Client; +-use serde::{Deserialize, Serialize}; +-use std::fs; +-use std::path::PathBuf; +-use std::time::{SystemTime, UNIX_EPOCH}; +-use tauri::Emitter; +-use tauri::{AppHandle, Manager}; +- +-// Strutture dati +-#[derive(Serialize, Deserialize, Debug, Clone)] +-#[serde(rename_all = "camelCase")] +-pub struct UpdateCheckResult { +- pub has_update: bool, +- pub current_version: String, +- pub latest_version: String, +- pub release_notes: String, +- pub release_url: String, +- pub published_at: String, +- pub download_urls: Vec, +-} +- +-#[derive(Serialize, Deserialize, Debug, Clone)] +-#[serde(rename_all = "camelCase")] +-pub struct DownloadAsset { +- pub name: String, +- pub url: String, +- pub size: u64, +- pub platform: String, +-} +- +-// Cache structure +-#[derive(Serialize, Deserialize, Debug, Clone)] +-struct UpdateCheckCache { +- last_checked: u64, +- last_result: Option, +-} +- +-// GitHub API response +-#[derive(Deserialize, Debug)] +-struct GitHubRelease { +- tag_name: String, +- body: String, +- html_url: String, +- published_at: String, +- assets: Vec, +-} +- +-#[derive(Deserialize, Debug)] +-struct GitHubAsset { +- name: String, +- browser_download_url: String, +- size: u64, +-} +- +-// Constants +-const GITHUB_REPO: &str = "debba/tabularis"; +-const CACHE_DURATION_SECS: u64 = 43200; // 12 hours +-/// Returns the installation source: "snap", "aur", or None for direct installs. +-/// Only meaningful on Linux; always returns None on other platforms. +-fn detect_installation_source() -> Option { +- #[cfg(target_os = "linux")] +- { +- // Snap sets the SNAP env var when running inside a snap sandbox +- if std::env::var("SNAP").is_ok() { +- return Some("snap".to_string()); +- } +- +- // Flatpak sets FLATPAK_ID when running inside a Flatpak sandbox +- if std::env::var("FLATPAK_ID").is_ok() { +- return Some("flatpak".to_string()); +- } +- +- // AUR: check if pacman's local database has a tabularis-bin entry +- if let Ok(entries) = std::fs::read_dir("/var/lib/pacman/local") { +- let is_aur = entries +- .filter_map(|e| e.ok()) +- .any(|e| e.file_name().to_string_lossy().starts_with("tabularis-bin-")); +- if is_aur { +- return Some("aur".to_string()); +- } +- } +- } +- +- None +-} +- +-/// Returns true when updates should not be managed by the app itself. +-fn is_managed_package() -> bool { +- detect_installation_source().is_some() +-} +- +-#[tauri::command] +-pub fn get_installation_source() -> Option { +- detect_installation_source() +-} +- +-// Helper functions +-fn get_cache_path(app: &AppHandle) -> Option { +- app.path() +- .app_config_dir() +- .ok() +- .map(|p| p.join("update_check_cache.json")) +-} +- +-fn parse_version(version: &str) -> Option<(u32, u32, u32)> { +- let clean = version.trim_start_matches('v'); +- let parts: Vec<&str> = clean.split('.').collect(); +- if parts.len() != 3 { +- return None; +- } +- +- let major = parts[0].parse().ok()?; +- let minor = parts[1].parse().ok()?; +- let patch = parts[2].parse().ok()?; +- +- Some((major, minor, patch)) +-} +- +-fn is_newer_version(current: &str, latest: &str) -> bool { +- match (parse_version(current), parse_version(latest)) { +- (Some(c), Some(l)) => l > c, +- _ => false, +- } +-} +- +-async fn fetch_latest_release() -> Result { +- let client = Client::new(); +- let url = format!( +- "https://api.github.com/repos/{}/releases/latest", +- GITHUB_REPO +- ); +- +- let res = client +- .get(&url) +- .header("User-Agent", "Tabularis") +- .header("Accept", "application/vnd.github.v3+json") +- .send() +- .await +- .map_err(|e| format!("Network error: {}", e))?; +- +- if !res.status().is_success() { +- return Err(format!("GitHub API error: {}", res.status())); +- } +- +- res.json::() +- .await +- .map_err(|e| format!("Failed to parse response: {}", e)) +-} +- +-fn categorize_asset(name: &str) -> String { +- if name.ends_with(".dmg") || name.contains("darwin") || name.contains("macos") { +- "macos".to_string() +- } else if name.ends_with(".exe") || name.ends_with(".msi") || name.contains("windows") { +- "windows".to_string() +- } else if name.ends_with(".AppImage") || name.ends_with(".deb") || name.ends_with(".rpm") { +- "linux".to_string() +- } else { +- "other".to_string() +- } +-} +- +-// Tauri commands +-#[tauri::command] +-pub async fn check_for_updates(app: AppHandle, force: bool) -> Result { +- // Managed packages (AUR, Snap) should not use the built-in updater +- if is_managed_package() { +- return Err("Updates are managed by the package manager".to_string()); +- } +- +- let config = crate::config::load_config_internal(&app); +- +- // Check if updates are disabled +- if !force && config.check_for_updates == Some(false) { +- return Err("Update checks disabled".to_string()); +- } +- +- // Check cache if not forced +- if !force { +- if let Some(cache_path) = get_cache_path(&app) { +- if cache_path.exists() { +- if let Ok(content) = fs::read_to_string(&cache_path) { +- if let Ok(cache) = serde_json::from_str::(&content) { +- let now = SystemTime::now() +- .duration_since(UNIX_EPOCH) +- .unwrap_or_default() +- .as_secs(); +- +- if now - cache.last_checked < CACHE_DURATION_SECS { +- if let Some(result) = cache.last_result { +- // Invalidate cache if the app was updated since it was written +- if result.current_version == env!("CARGO_PKG_VERSION") { +- return Ok(result); +- } +- } +- } +- } +- } +- } +- } +- } +- +- // Fetch latest release from GitHub +- let release = fetch_latest_release().await?; +- +- let current_version = env!("CARGO_PKG_VERSION"); +- let latest_version = release.tag_name.trim_start_matches('v'); +- +- let download_urls = release +- .assets +- .into_iter() +- .map(|asset| DownloadAsset { +- name: asset.name.clone(), +- url: asset.browser_download_url, +- size: asset.size, +- platform: categorize_asset(&asset.name), +- }) +- .collect(); +- +- let result = UpdateCheckResult { +- has_update: is_newer_version(current_version, &release.tag_name), +- current_version: current_version.to_string(), +- latest_version: latest_version.to_string(), +- release_notes: release.body, +- release_url: release.html_url, +- published_at: release.published_at, +- download_urls, +- }; +- +- // Save to cache +- if let Some(cache_path) = get_cache_path(&app) { +- let timestamp = SystemTime::now() +- .duration_since(UNIX_EPOCH) +- .unwrap_or_default() +- .as_secs(); +- +- let cache = UpdateCheckCache { +- last_checked: timestamp, +- last_result: Some(result.clone()), +- }; +- +- if let Ok(content) = serde_json::to_string(&cache) { +- let _ = fs::write(cache_path, content); +- } +- } +- +- Ok(result) +-} +- +-#[tauri::command] +-pub async fn download_and_install_update(app: AppHandle) -> Result<(), String> { +- // Usa tauri-plugin-updater per gestire il download e installazione +- use tauri_plugin_updater::UpdaterExt; +- +- let updater = app.updater_builder().build().map_err(|e| e.to_string())?; +- +- if let Some(update) = updater.check().await.map_err(|e| e.to_string())? { +- // Emetti eventi per aggiornare la UI sul progresso +- let mut downloaded = 0; +- +- update +- .download_and_install( +- |chunk_length, content_length| { +- downloaded += chunk_length; +- let progress = if let Some(total) = content_length { +- (downloaded as f64 / total as f64 * 100.0) as u32 +- } else { +- 0 +- }; +- +- let _ = app.emit("update-progress", progress); +- }, +- || { +- // Pre-installazione: salva stato, chiudi connessioni, etc. +- let _ = app.emit("update-installing", ()); +- }, +- ) +- .await +- .map_err(|e| e.to_string())?; +- +- app.restart(); +- } else { +- Err("No update available".to_string()) +- } +-} +- +-#[cfg(test)] +-mod tests { +- use super::*; +- +- // Version parsing tests +- #[test] +- fn test_version_parsing_standard() { +- assert_eq!(parse_version("0.8.8"), Some((0, 8, 8))); +- assert_eq!(parse_version("1.2.3"), Some((1, 2, 3))); +- assert_eq!(parse_version("10.20.30"), Some((10, 20, 30))); +- } +- +- #[test] +- fn test_version_parsing_with_v_prefix() { +- assert_eq!(parse_version("v0.8.8"), Some((0, 8, 8))); +- assert_eq!(parse_version("v1.0.0"), Some((1, 0, 0))); +- } +- +- #[test] +- fn test_version_parsing_invalid() { +- assert_eq!(parse_version("invalid"), None); +- assert_eq!(parse_version("1.2"), None); +- assert_eq!(parse_version("1.2.3.4"), None); +- assert_eq!(parse_version("a.b.c"), None); +- assert_eq!(parse_version(""), None); +- } +- +- #[test] +- fn test_version_parsing_edge_cases() { +- assert_eq!(parse_version("0.0.0"), Some((0, 0, 0))); +- assert_eq!(parse_version("999.999.999"), Some((999, 999, 999))); +- } +- +- // Version comparison tests +- #[test] +- fn test_version_comparison_newer() { +- assert!(is_newer_version("0.8.8", "0.9.0")); +- assert!(is_newer_version("0.8.8", "0.8.9")); +- assert!(is_newer_version("0.8.8", "1.0.0")); +- assert!(is_newer_version("1.0.0", "2.0.0")); +- } +- +- #[test] +- fn test_version_comparison_not_newer() { +- assert!(!is_newer_version("0.8.8", "0.8.8")); +- assert!(!is_newer_version("0.8.8", "0.8.7")); +- assert!(!is_newer_version("0.8.8", "0.7.9")); +- assert!(!is_newer_version("1.0.0", "0.9.9")); +- } +- +- #[test] +- fn test_version_comparison_with_v_prefix() { +- assert!(is_newer_version("0.8.8", "v0.9.0")); +- assert!(is_newer_version("v0.8.8", "0.9.0")); +- assert!(is_newer_version("v0.8.8", "v0.9.0")); +- } +- +- #[test] +- fn test_version_comparison_invalid() { +- assert!(!is_newer_version("invalid", "0.9.0")); +- assert!(!is_newer_version("0.8.8", "invalid")); +- assert!(!is_newer_version("invalid", "invalid")); +- } +- +- // Asset categorization tests +- #[test] +- fn test_categorize_asset_macos() { +- assert_eq!(categorize_asset("Tabularis_0.8.8_x64.dmg"), "macos"); +- assert_eq!(categorize_asset("Tabularis_0.8.8_aarch64.dmg"), "macos"); +- assert_eq!(categorize_asset("tabularis-darwin.zip"), "macos"); +- assert_eq!(categorize_asset("app-macos-universal.tar.gz"), "macos"); +- } +- +- #[test] +- fn test_categorize_asset_windows() { +- assert_eq!(categorize_asset("Tabularis_0.8.8_x64_setup.exe"), "windows"); +- assert_eq!(categorize_asset("tabularis.msi"), "windows"); +- assert_eq!(categorize_asset("app-windows-x86_64.zip"), "windows"); +- } +- +- #[test] +- fn test_categorize_asset_linux() { +- assert_eq!(categorize_asset("tabularis_0.8.8_amd64.AppImage"), "linux"); +- assert_eq!(categorize_asset("tabularis_0.8.8_amd64.deb"), "linux"); +- assert_eq!(categorize_asset("tabularis-0.8.8-1.x86_64.rpm"), "linux"); +- } +- +- #[test] +- fn test_categorize_asset_other() { +- assert_eq!(categorize_asset("README.txt"), "other"); +- assert_eq!(categorize_asset("checksums.sha256"), "other"); +- assert_eq!(categorize_asset("unknown-file"), "other"); +- } +- +- // Cache path tests +- #[test] +- fn test_cache_filename() { +- let expected = "update_check_cache.json"; +- assert!(expected.ends_with(".json")); +- assert!(expected.contains("cache")); +- } +- +- // GitHub repo constant test +- #[test] +- fn test_github_repo_constant() { +- assert_eq!(GITHUB_REPO, "debba/tabularis"); +- } +- +- // Cache duration test +- #[test] +- fn test_cache_duration() { +- assert_eq!(CACHE_DURATION_SECS, 43200); // 12 hours in seconds +- assert_eq!(CACHE_DURATION_SECS / 3600, 12); // Verify it's 12 hours +- } +- +- // Mutex to serialize env var mutations across parallel tests +- static ENV_MUTEX: std::sync::LazyLock> = +- std::sync::LazyLock::new(|| std::sync::Mutex::new(())); +- +- // Installation source detection tests +- #[test] +- fn test_detect_installation_source_snap() { +- let _lock = ENV_MUTEX.lock().unwrap(); +- std::env::remove_var("FLATPAK_ID"); +- std::env::set_var("SNAP", "/snap/tabularis/current"); +- let source = detect_installation_source(); +- std::env::remove_var("SNAP"); +- assert_eq!(source.as_deref(), Some("snap")); +- } +- +- #[test] +- fn test_detect_installation_source_flatpak() { +- let _lock = ENV_MUTEX.lock().unwrap(); +- std::env::remove_var("SNAP"); +- std::env::set_var("FLATPAK_ID", "io.github.debba.tabularis"); +- let source = detect_installation_source(); +- std::env::remove_var("FLATPAK_ID"); +- assert_eq!(source.as_deref(), Some("flatpak")); +- } +- +- #[test] +- fn test_detect_installation_source_direct() { +- let _lock = ENV_MUTEX.lock().unwrap(); +- std::env::remove_var("SNAP"); +- std::env::remove_var("FLATPAK_ID"); +- let source = detect_installation_source(); +- // On a dev/CI machine without pacman or tabularis-bin installed, must be None +- assert!(source.is_none() || source.as_deref() == Some("aur")); +- } +-} +diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json +index 86395be..4fbab47 100644 +--- a/src-tauri/tauri.conf.json ++++ b/src-tauri/tauri.conf.json +@@ -23,38 +23,5 @@ + "security": { + "csp": null + } +- }, +- "bundle": { +- "createUpdaterArtifacts": true, +- "active": true, +- "targets": "all", +- "icon": [ +- "icons/32x32.png", +- "icons/64x64.png", +- "icons/128x128.png", +- "icons/128x128@2x.png", +- "icons/icon.png", +- "icons/icon.icns", +- "icons/icon.ico" +- ], +- "linux": { +- "deb": { +- "depends": [ +- "libwebkit2gtk-4.1-0", +- "libsecret-1-0" +- ] +- }, +- "appimage": { +- "bundleMediaFramework": false +- } +- } +- }, +- "plugins": { +- "updater": { +- "endpoints": [ +- "https://github.com/debba/tabularis/releases/latest/download/latest.json" +- ], +- "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDY0NzY0QjNEQjI4QjFEQjcKUldTM0hZdXlQVXQyWkRYdmRJOEJhVEpYTit2VXRYS0drTit1bmthSHVzcWlQK09Wb2l5cVpOWXAK" +- } + } +-} +\ No newline at end of file ++} diff --git a/pkgs/by-name/ta/tabularis/package.nix b/pkgs/by-name/ta/tabularis/package.nix new file mode 100644 index 000000000000..3668c683d48c --- /dev/null +++ b/pkgs/by-name/ta/tabularis/package.nix @@ -0,0 +1,76 @@ +{ + lib, + stdenv, + rustPlatform, + fetchFromGitHub, + fetchPnpmDeps, + cargo-tauri, + nodejs, + pnpm, + pnpmConfigHook, + wrapGAppsHook4, + webkitgtk_4_1, + pkg-config, + openssl, + nix-update-script, +}: +rustPlatform.buildRustPackage (finalAttrs: { + pname = "tabularis"; + version = "0.9.12"; + + src = fetchFromGitHub { + owner = "debba"; + repo = "tabularis"; + tag = "v${finalAttrs.version}"; + hash = "sha256-kObjJ+C+0d/wLNt902yUPe8Cvss8d0ILeuo98vIiYDU="; + }; + + patches = [ + ./disable-updater.patch + ]; + + strictDeps = true; + + cargoRoot = "src-tauri"; + buildAndTestSubdir = finalAttrs.cargoRoot; + + cargoHash = "sha256-XYvwgZMJXM62kC8+DR06LygtTnL+8TLWyRZAgTQWf3Q="; + + pnpmDeps = fetchPnpmDeps { + inherit (finalAttrs) pname version src; + fetcherVersion = 3; + hash = "sha256-S/XCypKyYlJtuISNiG8NtJzisAejiUwqPVltXEmVlZw="; + }; + + nativeBuildInputs = [ + cargo-tauri.hook + + nodejs + pnpmConfigHook + pnpm + + pkg-config + ] + ++ lib.optionals stdenv.hostPlatform.isLinux [ wrapGAppsHook4 ]; + + buildInputs = [ + openssl + ] + ++ lib.optionals stdenv.hostPlatform.isLinux [ + webkitgtk_4_1 + ]; + + env.OPENSSL_NO_VENDOR = 1; + + passthru.updateScript = nix-update-script { }; + + meta = { + description = "Lightweight, developer-focused database management tool, built with Tauri and React"; + homepage = "http://tabularis.dev"; + changelog = "https://github.com/debba/tabularis/blob/${finalAttrs.src.tag}/CHANGELOG.md"; + license = lib.licenses.mit; + inherit (cargo-tauri.hook.meta) platforms; + maintainers = with lib.maintainers; [ nartsiss ]; + mainProgram = "tabularis"; + }; +})