From 7a5bbb0bda84b3c47318cd85fd819e21dbf2d0f4 Mon Sep 17 00:00:00 2001 From: egdev6 Date: Fri, 6 Nov 2026 21:07:49 +0100 Subject: [PATCH 1/2] =?UTF-8?q?chore(rust):=20acotar=20por=20plataforma=20?= =?UTF-8?q?los=20s=C3=ADmbolos=20que=20avisaban=20en=20Windows=20(#246)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/i18n-inventory.md | 2 +- src-tauri/src/cli_process.rs | 2 +- src-tauri/src/detail_placement.rs | 8 ++++++-- src-tauri/src/login.rs | 14 ++++++++++---- src-tauri/src/providers/claude.rs | 9 +++++++-- src-tauri/src/providers/gemini.rs | 1 - src-tauri/src/uninstall.rs | 14 ++++++++++++++ src-tauri/src/wm_compat.rs | 4 +++- src-tauri/src/wsl/process.rs | 5 +++++ 9 files changed, 47 insertions(+), 12 deletions(-) diff --git a/docs/i18n-inventory.md b/docs/i18n-inventory.md index ae08846..b9577ef 100644 --- a/docs/i18n-inventory.md +++ b/docs/i18n-inventory.md @@ -1,6 +1,6 @@ # i18n inventory (egdev6/quorb#66) -Generated by `node scripts/i18n-inventory.mjs` from the source trees `src/` = `129601dcd1c39752bf3a0d5e2a484ed622db7138` and `src-tauri/src/` = `df433ca38183d57bc26b6840a43190bba6083dd1`. +Generated by `node scripts/i18n-inventory.mjs` from the source trees `src/` = `129601dcd1c39752bf3a0d5e2a484ed622db7138` and `src-tauri/src/` = `9ee0286cdbdd9c645c483158e9e9c576fa47e589`. Any commit whose `git rev-parse :src` and `:src-tauri/src` match has these exact texts. Do not edit by hand: regenerate, or verify with `node scripts/i18n-inventory.mjs --check`. diff --git a/src-tauri/src/cli_process.rs b/src-tauri/src/cli_process.rs index a77fd51..842406e 100644 --- a/src-tauri/src/cli_process.rs +++ b/src-tauri/src/cli_process.rs @@ -67,7 +67,7 @@ impl CommandRunner for ProcessRunner { cwd: &Path, timeout: Duration, ) -> Result { - let mut child = spawn_child(program, args, env, cwd)?; + let child = spawn_child(program, args, env, cwd)?; run_pipelined(child, timeout) } } diff --git a/src-tauri/src/detail_placement.rs b/src-tauri/src/detail_placement.rs index e67ad4c..2fd4391 100644 --- a/src-tauri/src/detail_placement.rs +++ b/src-tauri/src/detail_placement.rs @@ -38,9 +38,13 @@ use std::sync::atomic::{AtomicU64, Ordering}; use crate::detail_geometry::Bounds; use crate::widget_placement::{ - position_matches, rooted_origin, size_matches, to_logical, to_physical, Geometry, Reason, - Transport, READS, SETTLE, + position_matches, size_matches, to_logical, Geometry, Reason, Transport, READS, }; +// Piezas del ciclo nativo X11: solo las usa `UiSurface`, que existe únicamente +// en Linux. En Windows/macOS el transporte aplica la petición e informa sin +// read-back, así que estos símbolos no se importan ahí. +#[cfg(target_os = "linux")] +use crate::widget_placement::{rooted_origin, to_physical, SETTLE}; // El informe es el espejo de `set_widget_bounds`: viajan los mismos tipos, // así el frontend trata con un solo contrato. pub use crate::widget_placement::{Report, Status}; diff --git a/src-tauri/src/login.rs b/src-tauri/src/login.rs index 02a83c4..ce6de9c 100644 --- a/src-tauri/src/login.rs +++ b/src-tauri/src/login.rs @@ -254,9 +254,11 @@ type SessionEnv = Option<(&'static str, std::path::PathBuf)>; /// Antepone al PATH la carpeta donde se encontró el CLI (si no estaba en el PATH), /// incluyendo el PATH fresco del registro para que la terminal nueva lo vea. -/// Solo la consumen las ramas Windows/Linux: en macOS el PATH viaja dentro del -/// texto del script de Terminal, no por herencia del entorno. -#[cfg(any(test, not(target_os = "macos")))] +/// Solo la consume la rama Windows (`spawn_terminal`: inyecta el PATH en la +/// consola nueva): en macOS el PATH viaja dentro del texto del script de +/// Terminal y en Linux la rama usa `path_with_dirs` con los directorios +/// explícitos. +#[cfg(windows)] fn path_with(extra: Option) -> Option { let mut dirs: Vec = std::env::var_os("PATH") .map(|p| std::env::split_paths(&p).collect()) @@ -266,7 +268,7 @@ fn path_with(extra: Option) -> Option { } /// Antepone `extra` a `current` (el PATH actual) sin tocar el entorno real. -#[cfg(any(test, not(target_os = "macos")))] +#[cfg(any(test, windows))] fn path_with_current( extra: Option, current: Option, @@ -396,6 +398,10 @@ const SHELL_WRAP: &str = "\"$@\"; exec \"${SHELL:-/bin/sh}\""; /// literal): si el CLI necesita variables, deben definirse fuera del comando. /// Comillas sin cerrar devuelven `Err` y el login se aborta /// (fail-closed): el texto del usuario nunca se interpola como código. +/// Solo lo consumen `spawn_terminal` de Linux (la terminal recibe el login +/// como argv) y los tests: en Windows el comando va dentro de `cmd /K` y en +/// macOS dentro del script de Terminal. +#[cfg(any(test, all(not(windows), not(target_os = "macos"))))] fn split_login_command(command: &str) -> Result<(String, Vec), String> { let mut tokens: Vec = Vec::new(); let mut current = String::new(); diff --git a/src-tauri/src/providers/claude.rs b/src-tauri/src/providers/claude.rs index 26142f4..4210da6 100644 --- a/src-tauri/src/providers/claude.rs +++ b/src-tauri/src/providers/claude.rs @@ -13,7 +13,6 @@ use crate::config::SubscriptionConfig; use crate::model::{Kind, Meter, Status, UsageReport}; use crate::wsl::{SessionError, SessionResolver}; use serde_json::Value; -use std::path::Path; const USAGE_URL: &str = "https://api.anthropic.com/api/oauth/usage"; @@ -25,11 +24,15 @@ const KEYCHAIN_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5); /// Resultado de consultar el Keychain de macOS por la sesión de Claude Code. #[derive(Debug, PartialEq)] enum KeychainOutcome { - /// Sesión válida encontrada y parseada. + /// Sesión válida encontrada y parseada. Solo el Keychain de macOS la + /// produce (`keychain_sync`); fuera de macOS no se construye. + #[cfg(target_os = "macos")] Session(Credentials), /// No hay entrada, el usuario negó el acceso o el secreto no parsea. NoSession, /// La autorización no se completó dentro del presupuesto: estado accionable. + /// Igual que `Session`, solo la rama macOS la produce. + #[cfg(target_os = "macos")] TimedOut, } @@ -244,7 +247,9 @@ pub async fn fetch( ); } match keychain_fallback().await { + #[cfg(target_os = "macos")] KeychainOutcome::Session(c) => c, + #[cfg(target_os = "macos")] KeychainOutcome::TimedOut => { return report.fail( Status::Warning, diff --git a/src-tauri/src/providers/gemini.rs b/src-tauri/src/providers/gemini.rs index dc93027..afbe4ba 100644 --- a/src-tauri/src/providers/gemini.rs +++ b/src-tauri/src/providers/gemini.rs @@ -16,7 +16,6 @@ use crate::model::{Kind, Meter, Status, UsageReport}; use crate::wsl::{SessionError, SessionResolver}; use serde_json::{json, Value}; use std::collections::BTreeMap; -use std::path::Path; use std::sync::Mutex; const API: &str = "https://cloudcode-pa.googleapis.com/v1internal"; diff --git a/src-tauri/src/uninstall.rs b/src-tauri/src/uninstall.rs index 8396c08..4f33343 100644 --- a/src-tauri/src/uninstall.rs +++ b/src-tauri/src/uninstall.rs @@ -1,6 +1,8 @@ //! Explicit NSIS delete-data helper, dispatched before Tauri/plugins start. //! No config/session contents are loaded and no credential values are read. +#[cfg(any(windows, test))] use crate::channel::Profile; +#[cfg(any(windows, test))] use std::{ fs, path::{Path, PathBuf}, @@ -9,24 +11,33 @@ use std::{ #[derive(Debug, PartialEq)] enum Failure { Arguments, + #[cfg(any(windows, test))] Paths, + #[cfg(any(windows, test))] Credentials, + #[cfg(any(windows, test))] Files, } impl Failure { fn exit_code(&self) -> i32 { match self { Self::Arguments => 2, + #[cfg(any(windows, test))] Self::Paths => 3, + #[cfg(any(windows, test))] Self::Credentials => 4, + #[cfg(any(windows, test))] Self::Files => 5, } } } +// Only the Windows NSIS helper deletes data; tests exercise it everywhere. +#[cfg(any(windows, test))] struct Plan { roots: [PathBuf; 3], } +#[cfg(any(windows, test))] impl Plan { fn new(profile: &Profile, home: &Path, roaming: &Path, local: &Path) -> Result { if !matches!( @@ -81,6 +92,7 @@ impl Plan { } } +#[cfg(any(windows, test))] fn is_link(meta: &fs::Metadata) -> bool { #[cfg(windows)] { @@ -93,6 +105,7 @@ fn is_link(meta: &fs::Metadata) -> bool { } } +#[cfg(any(windows, test))] fn purge(plan: &Plan, credentials: impl FnOnce() -> Result<(), Failure>) -> Result<(), Failure> { plan.preflight()?; // If vault access/deletion fails, preserve files and stop the uninstaller. @@ -107,6 +120,7 @@ fn purge(plan: &Plan, credentials: impl FnOnce() -> Result<(), Failure>) -> Resu Ok(()) } +#[cfg(any(windows, test))] fn owned_target(target: &str, identifier: &str) -> bool { let Some(key) = target.strip_suffix(&format!(".{identifier}")) else { return false; diff --git a/src-tauri/src/wm_compat.rs b/src-tauri/src/wm_compat.rs index 850926f..b946125 100644 --- a/src-tauri/src/wm_compat.rs +++ b/src-tauri/src/wm_compat.rs @@ -299,7 +299,9 @@ pub fn display_backend_from_env(env: &Env) -> DisplayBackend { /// live GDK probe, when it resolves, MUST win over the env fallback. Pure /// helper, applied at the command level: rewrites `display_backend` and /// recomputes ONLY `surfaces.window_positioning`; every other surface stays -/// untouched. +/// untouched. Only `get_wm_info` on Linux (the sole platform with the runtime +/// GDK backend probe) and the tests call it. +#[cfg(any(test, target_os = "linux"))] pub fn with_display_backend( mut info: WindowManagerInfo, backend: DisplayBackend, diff --git a/src-tauri/src/wsl/process.rs b/src-tauri/src/wsl/process.rs index 885a1ad..8f826ba 100644 --- a/src-tauri/src/wsl/process.rs +++ b/src-tauri/src/wsl/process.rs @@ -95,6 +95,11 @@ pub fn pidfile_base() -> PathBuf { /// El nombre del subdirectorio lo decide `mktemp -d` en la distro, así que /// el host lo descubre con un glob dentro del base. `$1` entra como /// parámetro posicional: nada se interpola en el script. +/// +/// El código de producción lee PID y token en una sola llamada +/// (`SCRIPT_READ_IDENTITY`); este script queda como fixture de los tests de +/// argv. +#[cfg(test)] pub const SCRIPT_READ_PID: &str = "cat \"$1\"/*/pid"; /// Script constante para leer PID **y token de instancia** en la misma From cffba13a12e7b20549c1a5f377b8774971bea3d4 Mon Sep 17 00:00:00 2001 From: egdev6 Date: Fri, 6 Nov 2026 21:18:06 +0100 Subject: [PATCH 2/2] chore(rust): acotar a unix los helpers de tests que avisaban en Windows (#246) --- docs/i18n-inventory.md | 2 +- src-tauri/src/codex_setup.rs | 1 + src-tauri/src/providers/claude.rs | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/i18n-inventory.md b/docs/i18n-inventory.md index b9577ef..c5cbf3f 100644 --- a/docs/i18n-inventory.md +++ b/docs/i18n-inventory.md @@ -1,6 +1,6 @@ # i18n inventory (egdev6/quorb#66) -Generated by `node scripts/i18n-inventory.mjs` from the source trees `src/` = `129601dcd1c39752bf3a0d5e2a484ed622db7138` and `src-tauri/src/` = `9ee0286cdbdd9c645c483158e9e9c576fa47e589`. +Generated by `node scripts/i18n-inventory.mjs` from the source trees `src/` = `129601dcd1c39752bf3a0d5e2a484ed622db7138` and `src-tauri/src/` = `fa2fd75f1124453a881a835169cde8f4ffb08839`. Any commit whose `git rev-parse :src` and `:src-tauri/src` match has these exact texts. Do not edit by hand: regenerate, or verify with `node scripts/i18n-inventory.mjs --check`. diff --git a/src-tauri/src/codex_setup.rs b/src-tauri/src/codex_setup.rs index ee79b4b..c654e28 100644 --- a/src-tauri/src/codex_setup.rs +++ b/src-tauri/src/codex_setup.rs @@ -1627,6 +1627,7 @@ mod tests { /// Node real del proyecto (por `QUORB_TEST_NODE_EXECUTABLE` o PATH); nunca se /// lee su versión ni se ejecutan probes de npm/Codex. + #[cfg(unix)] fn test_node_executable() -> Option { if let Some(explicit) = std::env::var_os("QUORB_TEST_NODE_EXECUTABLE") { let explicit = PathBuf::from(explicit); diff --git a/src-tauri/src/providers/claude.rs b/src-tauri/src/providers/claude.rs index 4210da6..86d7b2e 100644 --- a/src-tauri/src/providers/claude.rs +++ b/src-tauri/src/providers/claude.rs @@ -108,7 +108,7 @@ fn keychain_sync(timeout: std::time::Duration) -> KeychainOutcome { /// Resultado diferenciado de la espera acotada: terminar bien, terminar mal /// o agotar el presupuesto. Distintos a propósito — `keychain_sync` reporta /// `NoSession` y `TimedOut` de forma distinta (revisión #112). -#[cfg(any(test, target_os = "macos"))] +#[cfg(any(target_os = "macos", all(test, unix)))] enum WaitOutcome { /// Terminó con éxito dentro del presupuesto. Success(BoundedOutput), @@ -122,7 +122,7 @@ enum WaitOutcome { /// terminó bien dentro del presupuesto; en timeout mata el árbol y devuelve /// `TimedOut`; en salida fallida, `Failed`. Puro respecto del Keychain para /// poder probarlo con procesos reales (`sleep`, `true`, `false`). -#[cfg(any(test, target_os = "macos"))] +#[cfg(any(target_os = "macos", all(test, unix)))] fn wait_bounded(mut child: std::process::Child, timeout: std::time::Duration) -> WaitOutcome { use std::io::Read; let mut pipe = match child.stdout.take() { @@ -156,7 +156,7 @@ fn wait_bounded(mut child: std::process::Child, timeout: std::time::Duration) -> } /// Salida capturada de un proceso que terminó con éxito dentro del presupuesto. -#[cfg(any(test, target_os = "macos"))] +#[cfg(any(target_os = "macos", all(test, unix)))] struct BoundedOutput { stdout: String, }