From 39fc36f35b12585af549932e4e154a645584c257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> Date: Mon, 17 Aug 2026 14:01:01 +0800 Subject: [PATCH] build(hololake): add verified Windows x64 distribution path --- .../scripts/release-pipeline.test.mjs | 9 ++ .../src-tauri/icons/icon.ico | Bin 0 -> 27002 bytes .../src-tauri/src/code_channel.rs | 36 ++++- .../src-tauri/src/code_repo_login.rs | 1 + .../src/direct_local_broker_windows.rs | 57 ++++++++ .../src-tauri/src/knowledge_base.rs | 3 +- .../src-tauri/src/lib.rs | 6 +- .../src-tauri/src/pncc_remote_git.rs | 19 ++- .../src-tauri/src/pncc_repository_binding.rs | 32 ++++- .../src-tauri/src/pncc_server_projection.rs | 3 +- .../src-tauri/src/release_update.rs | 136 ++++++++++++++++-- .../src-tauri/src/user_pncc_channel.rs | 3 +- .../src-tauri/tauri.conf.json | 3 +- 13 files changed, 275 insertions(+), 33 deletions(-) create mode 100644 product-source/hololake-native-desktop/src-tauri/icons/icon.ico create mode 100644 product-source/hololake-native-desktop/src-tauri/src/direct_local_broker_windows.rs diff --git a/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs b/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs index 6aa18ef43..2a6e61298 100644 --- a/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs +++ b/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs @@ -1,4 +1,5 @@ import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' import test from 'node:test' import { @@ -74,3 +75,11 @@ test('release pipeline requires updater signing, Developer ID and Apple notariza APPLE_TEAM_ID: 'TEAM', })) }) + +test('Windows updater keeps signed installation but never claims the macOS rollback boundary', () => { + const source = readFileSync(new URL('../src-tauri/src/release_update.rs', import.meta.url), 'utf8') + assert.match(source, /SIGNED_PACKAGE_VERIFIED_INSTALLING_NO_LOCAL_ROLLBACK/) + assert.match(source, /HOLOLAKE_RELEASE_WINDOWS_LOCAL_ROLLBACK_NOT_AVAILABLE/) + assert.match(source, /snapshot\.envelope\.hololake\.rollback\.supported\s*&&\s*platform_recovery\.local_rollback_available/) + assert.match(source, /update\.install\(&bytes\)/) +}) diff --git a/product-source/hololake-native-desktop/src-tauri/icons/icon.ico b/product-source/hololake-native-desktop/src-tauri/icons/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..98c5418161c8676b6487ce064092a61b637deaee GIT binary patch literal 27002 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 literal 0 HcmV?d00001 diff --git a/product-source/hololake-native-desktop/src-tauri/src/code_channel.rs b/product-source/hololake-native-desktop/src-tauri/src/code_channel.rs index ec656b3c9..a6dd47cd7 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/code_channel.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/code_channel.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use std::cmp::Reverse; use std::fs::{self, OpenOptions}; use std::io::Write; +#[cfg(unix)] use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; use std::path::{Component, Path, PathBuf}; use std::process::Command; @@ -13,6 +14,18 @@ use tauri_plugin_dialog::DialogExt; use url::Url; use uuid::Uuid; +#[cfg(windows)] +trait OpenOptionsModeExt { + fn mode(&mut self, mode: u32) -> &mut Self; +} + +#[cfg(windows)] +impl OpenOptionsModeExt for OpenOptions { + fn mode(&mut self, _mode: u32) -> &mut Self { + self + } +} + const SNAPSHOT_SCHEMA: &str = "hololake.code-channel/v1"; const REGISTRY_SCHEMA: &str = "hololake.code-channel-registry/v1"; const ALLOWED_HOSTS: &[&str] = &["guanghulab.com", "guanghubingshuo.com", "guanghu.chat"]; @@ -207,6 +220,7 @@ pub(crate) fn register_managed_repository( fn ensure_root(root: &Path) -> Result<(), String> { fs::create_dir_all(root.join("repositories")) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_STORAGE_UNAVAILABLE: {error}"))?; + #[cfg(unix)] fs::set_permissions(root, fs::Permissions::from_mode(0o700)) .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_PERMISSION_FAILED: {error}"))?; Ok(()) @@ -526,11 +540,15 @@ fn clone_at(root: &Path, input: CloneCodeChannelInput) -> Result Result { } fn git_optional(root: &Path, args: &[&str]) -> Option { - let output = Command::new("/usr/bin/git") + let output = Command::new(git_executable()) .current_dir(root) .env("GIT_TERMINAL_PROMPT", "0") .args(args) @@ -790,6 +808,14 @@ fn git_optional(root: &Path, args: &[&str]) -> Option { .then(|| String::from_utf8_lossy(&output.stdout).into_owned()) } +fn git_executable() -> &'static str { + if cfg!(windows) { + "git" + } else { + "/usr/bin/git" + } +} + fn now_unix_ms() -> Result { SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs b/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs index d868d59a0..bee6e8bea 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs @@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize}; use std::fs; +#[cfg(target_os = "macos")] use std::process::Command; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use tauri::{AppHandle, Manager, State}; diff --git a/product-source/hololake-native-desktop/src-tauri/src/direct_local_broker_windows.rs b/product-source/hololake-native-desktop/src-tauri/src/direct_local_broker_windows.rs new file mode 100644 index 000000000..cd9350d4f --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/src/direct_local_broker_windows.rs @@ -0,0 +1,57 @@ +use serde::Serialize; +use tauri::AppHandle; + +const DISCOVERY_SCHEMA: &str = "hololake.nearby-ai-discovery/v1"; + +/// Windows currently has no Unix-domain socket transport. Keep the public app +/// launchable while reporting the connector boundary as closed, rather than +/// silently opening a TCP listener with weaker local-user isolation. +#[derive(Default)] +pub struct DirectLocalBrokerState; + +impl DirectLocalBrokerState { + pub fn ensure_started(&self, _app: &AppHandle) -> Result { + Ok(false) + } + + pub fn active_connection_count(&self) -> usize { + 0 + } +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NearbyAiDiscoverySnapshot { + pub schema: &'static str, + pub state: &'static str, + pub service_name: &'static str, + pub transport: &'static str, + pub language_protocol: &'static str, + pub automatic_same_device_discovery: bool, + pub large_invitation_copy_required: bool, + pub generic_ai_visitor: &'static str, + pub guanghu_persona: &'static str, + pub local_network_discovery: &'static str, + pub authority: &'static str, +} + +#[tauri::command] +pub fn get_nearby_ai_discovery() -> NearbyAiDiscoverySnapshot { + NearbyAiDiscoverySnapshot { + schema: DISCOVERY_SCHEMA, + state: "WINDOWS_TRANSPORT_NOT_YET_IMPLEMENTED", + service_name: "HoloLake", + transport: "CLOSED_NO_TCP_FALLBACK", + language_protocol: "GLP/1.0", + automatic_same_device_discovery: false, + large_invitation_copy_required: false, + generic_ai_visitor: "DEFERRED", + guanghu_persona: "BINDING_EVIDENCE_REQUIRED", + local_network_discovery: "DEFERRED_UNTIL_ENCRYPTED_TRANSPORT_AND_APPROVAL", + authority: "UNAVAILABLE_IS_NOT_AUTHORIZATION", + } +} + +pub fn run_connector() -> Result<(), String> { + Err("HOLOLAKE_WINDOWS_DIRECT_LOCAL_CONNECTOR_NOT_IMPLEMENTED".into()) +} diff --git a/product-source/hololake-native-desktop/src-tauri/src/knowledge_base.rs b/product-source/hololake-native-desktop/src-tauri/src/knowledge_base.rs index 81763f401..9fec95dea 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/knowledge_base.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/knowledge_base.rs @@ -1195,7 +1195,8 @@ fn modified_unix_ms(metadata: &fs::Metadata) -> u128 { } fn git(root: &Path, args: &[&str], operation: &str) -> Result { - let output = Command::new("/usr/bin/git") + let executable = if cfg!(windows) { "git" } else { "/usr/bin/git" }; + let output = Command::new(executable) .current_dir(root) .env("GIT_TERMINAL_PROMPT", "0") .args(args) diff --git a/product-source/hololake-native-desktop/src-tauri/src/lib.rs b/product-source/hololake-native-desktop/src-tauri/src/lib.rs index b55f97fe8..498ff8b3b 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/lib.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/lib.rs @@ -1,7 +1,11 @@ -mod circular_lake_membrane; mod authenticated_storage; +mod circular_lake_membrane; mod code_channel; mod code_repo_login; +#[cfg(unix)] +mod direct_local_broker; +#[cfg(windows)] +#[path = "direct_local_broker_windows.rs"] mod direct_local_broker; mod direct_local_session; mod dynamic_capability_routing; diff --git a/product-source/hololake-native-desktop/src-tauri/src/pncc_remote_git.rs b/product-source/hololake-native-desktop/src-tauri/src/pncc_remote_git.rs index 1368f321d..04a960c16 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/pncc_remote_git.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/pncc_remote_git.rs @@ -688,12 +688,23 @@ fn remove_exact_cache(root: &Path, cache: &Path) -> Result<(), String> { } fn trusted_git() -> Command { - let mut command = Command::new("/usr/bin/git"); + let executable = if cfg!(windows) { "git" } else { "/usr/bin/git" }; + let mut command = Command::new(executable); + command.env_clear(); + #[cfg(unix)] + command.env("PATH", "/usr/bin:/bin"); + #[cfg(windows)] + for key in ["PATH", "SystemRoot", "WINDIR", "TEMP", "TMP"] { + if let Some(value) = std::env::var_os(key) { + command.env(key, value); + } + } command - .env_clear() - .env("PATH", "/usr/bin:/bin") .env("GIT_CONFIG_NOSYSTEM", "1") - .env("GIT_CONFIG_GLOBAL", "/dev/null") + .env( + "GIT_CONFIG_GLOBAL", + if cfg!(windows) { "NUL" } else { "/dev/null" }, + ) .env("GIT_TERMINAL_PROMPT", "0") .env("GCM_INTERACTIVE", "Never") .args(["-c", "protocol.file.allow=never"]); diff --git a/product-source/hololake-native-desktop/src-tauri/src/pncc_repository_binding.rs b/product-source/hololake-native-desktop/src-tauri/src/pncc_repository_binding.rs index 919e91771..6162d71fc 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/pncc_repository_binding.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/pncc_repository_binding.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; use std::fs::{self, OpenOptions}; use std::io::Write; +#[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; use std::path::{Component, Path, PathBuf}; use std::process::{Command, Output}; @@ -16,6 +17,18 @@ use tauri::AppHandle; use tauri_plugin_dialog::DialogExt; use uuid::Uuid; +#[cfg(windows)] +trait OpenOptionsModeExt { + fn mode(&mut self, mode: u32) -> &mut Self; +} + +#[cfg(windows)] +impl OpenOptionsModeExt for OpenOptions { + fn mode(&mut self, _mode: u32) -> &mut Self { + self + } +} + const MANIFEST_PATH: &str = ".hololake/persona/manifest.json"; const MANIFEST_SCHEMA: &str = "hololake.persona/v1"; const MAX_MANIFEST_BYTES: usize = 512 * 1024; @@ -765,12 +778,23 @@ fn now_unix_ms() -> Result { } fn trusted_git() -> Command { - let mut command = Command::new("/usr/bin/git"); + let executable = if cfg!(windows) { "git" } else { "/usr/bin/git" }; + let mut command = Command::new(executable); + command.env_clear(); + #[cfg(unix)] + command.env("PATH", "/usr/bin:/bin"); + #[cfg(windows)] + for key in ["PATH", "SystemRoot", "WINDIR", "TEMP", "TMP"] { + if let Some(value) = std::env::var_os(key) { + command.env(key, value); + } + } command - .env_clear() - .env("PATH", "/usr/bin:/bin") .env("GIT_CONFIG_NOSYSTEM", "1") - .env("GIT_CONFIG_GLOBAL", "/dev/null") + .env( + "GIT_CONFIG_GLOBAL", + if cfg!(windows) { "NUL" } else { "/dev/null" }, + ) .env("GIT_TERMINAL_PROMPT", "0"); command } diff --git a/product-source/hololake-native-desktop/src-tauri/src/pncc_server_projection.rs b/product-source/hololake-native-desktop/src-tauri/src/pncc_server_projection.rs index fc0ae4d24..fd6ec1fc3 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/pncc_server_projection.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/pncc_server_projection.rs @@ -120,7 +120,8 @@ fn parse_projection(bytes: &[u8]) -> Result { #[tauri::command] pub async fn query_jd_pncc_server_projection() -> Result { tauri::async_runtime::spawn_blocking(|| { - let output = Command::new("/usr/bin/ssh") + let executable = if cfg!(windows) { "ssh" } else { "/usr/bin/ssh" }; + let output = Command::new(executable) .args([ "-o", "BatchMode=yes", diff --git a/product-source/hololake-native-desktop/src-tauri/src/release_update.rs b/product-source/hololake-native-desktop/src-tauri/src/release_update.rs index f349415cd..5d0bec7ce 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/release_update.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/release_update.rs @@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use std::fs::{self, OpenOptions}; use std::io::Write; +#[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; @@ -15,6 +16,18 @@ use tauri::{AppHandle, Manager}; use tauri_plugin_updater::{Update, UpdaterExt}; use uuid::Uuid; +#[cfg(windows)] +trait OpenOptionsModeExt { + fn mode(&mut self, mode: u32) -> &mut Self; +} + +#[cfg(windows)] +impl OpenOptionsModeExt for OpenOptions { + fn mode(&mut self, _mode: u32) -> &mut Self { + self + } +} + const CANDIDATE_SCHEMA: &str = "hololake.release-candidate/v1"; const RECEIPT_SCHEMA: &str = "hololake.release-install-receipt/v1"; const RECOVERY_SCHEMA: &str = "hololake.release-recovery/v1"; @@ -257,18 +270,17 @@ pub async fn confirm_hololake_update_install( { return Err("HOLOLAKE_RELEASE_PACKAGE_DIGEST_OR_SIZE_MISMATCH".into()); } - let current_bundle = current_app_bundle_path()?; - let backup = prepare_last_known_good_at(&root, ¤t_bundle)?; + let platform_recovery = prepare_platform_recovery_at(&root)?; let mut recovery = ReleaseRecoveryRecord { schema: RECOVERY_SCHEMA.into(), - state: "LAST_KNOWN_GOOD_VERIFIED_INSTALLING".into(), + state: platform_recovery.installing_state.into(), release_id: snapshot.envelope.release_id.clone(), previous_version: snapshot.current_version.clone(), installed_version: snapshot.envelope.version.clone(), - backup_bundle_path: backup.0.to_string_lossy().into_owned(), - backup_identifier: backup.1.identifier, - backup_team_identifier: backup.1.team_identifier, - backup_cdhash: backup.1.cdhash, + backup_bundle_path: platform_recovery.backup_bundle_path, + backup_identifier: platform_recovery.backup_identifier, + backup_team_identifier: platform_recovery.backup_team_identifier, + backup_cdhash: platform_recovery.backup_cdhash, failed_bundle_path: None, observed_at_unix_ms: now_unix_ms()?, receipt_id: sha256_hex( @@ -293,7 +305,8 @@ pub async fn confirm_hololake_update_install( package_size: snapshot.package_size, automatic_restart: false, health_receipt_required: snapshot.envelope.hololake.rollback.health_receipt_required, - rollback_declared_by_broadcast: snapshot.envelope.hololake.rollback.supported, + rollback_declared_by_broadcast: snapshot.envelope.hololake.rollback.supported + && platform_recovery.local_rollback_available, receipt_id: sha256_hex( format!( "{}\n{}\n{}\n{}", @@ -311,13 +324,13 @@ pub async fn confirm_hololake_update_install( "RECEIPT", )?; if let Err(error) = mark_candidate_installing_at(&root, &record.candidate_id) { - recovery.state = "INSTALL_FAILED_BACKUP_RETAINED".into(); + recovery.state = platform_install_failed_state().into(); recovery.observed_at_unix_ms = now_unix_ms()?; write_json_atomic(&recovery_path(&root), &recovery, "RECOVERY")?; return Err(error); } if let Err(error) = update.install(&bytes) { - recovery.state = "INSTALL_FAILED_BACKUP_RETAINED".into(); + recovery.state = platform_install_failed_state().into(); recovery.observed_at_unix_ms = now_unix_ms()?; write_json_atomic(&recovery_path(&root), &recovery, "RECOVERY")?; return Err(format!("HOLOLAKE_RELEASE_INSTALL_FAILED: {error}")); @@ -347,6 +360,8 @@ pub fn confirm_hololake_release_health(app: AppHandle) -> Result Result { + "HEALTH_CONFIRMED_NO_LOCAL_BACKUP" + } "AWAITING_HUMAN_HEALTH_CONFIRMATION" => "HEALTH_CONFIRMED_BACKUP_REMOVED", "ROLLED_BACK_AWAITING_HUMAN_ACKNOWLEDGEMENT" => "ROLLBACK_CONFIRMED_BACKUP_REMOVED", + _ if record.backup_bundle_path.is_empty() => "INSTALL_FAILURE_ACKNOWLEDGED_NO_LOCAL_BACKUP", _ => "INSTALL_FAILURE_ACKNOWLEDGED_BACKUP_REMOVED", } .into(); @@ -374,6 +390,7 @@ pub fn confirm_hololake_release_health(app: AppHandle) -> Result Result { let root = release_update_root(&app)?; @@ -396,6 +413,12 @@ pub fn rollback_hololake_update(app: AppHandle) -> Result Result { + Err("HOLOLAKE_RELEASE_WINDOWS_LOCAL_ROLLBACK_NOT_AVAILABLE".into()) +} + pub(crate) fn observe_release_startup(app: &AppHandle) -> Result<(), String> { let root = release_update_root(app)?; let path = recovery_path(&root); @@ -422,6 +445,16 @@ pub(crate) fn observe_release_startup(app: &AppHandle) -> Result<(), String> { "LAST_KNOWN_GOOD_VERIFIED_INSTALLING" if version == record.installed_version => { Some("AWAITING_HUMAN_HEALTH_CONFIRMATION") } + "SIGNED_PACKAGE_VERIFIED_INSTALLING_NO_LOCAL_ROLLBACK" + if version == record.previous_version => + { + Some("INSTALL_INTERRUPTED_NO_LOCAL_BACKUP") + } + "SIGNED_PACKAGE_VERIFIED_INSTALLING_NO_LOCAL_ROLLBACK" + if version == record.installed_version => + { + Some("AWAITING_HUMAN_HEALTH_CONFIRMATION") + } "AWAITING_HUMAN_HEALTH_CONFIRMATION" if version == record.previous_version => { Some("ROLLED_BACK_AWAITING_HUMAN_ACKNOWLEDGEMENT") } @@ -615,8 +648,10 @@ fn ensure_no_unresolved_recovery_at(root: &Path) -> Result<(), String> { if matches!( record.state.as_str(), "HEALTH_CONFIRMED_BACKUP_REMOVED" + | "HEALTH_CONFIRMED_NO_LOCAL_BACKUP" | "ROLLBACK_CONFIRMED_BACKUP_REMOVED" | "INSTALL_FAILURE_ACKNOWLEDGED_BACKUP_REMOVED" + | "INSTALL_FAILURE_ACKNOWLEDGED_NO_LOCAL_BACKUP" ) { Ok(()) } else { @@ -643,8 +678,11 @@ fn release_recovery_status_at(root: &Path) -> Result ReleaseRecoveryReceipt { } } +struct PlatformRecoveryPreparation { + installing_state: &'static str, + backup_bundle_path: String, + backup_identifier: String, + backup_team_identifier: String, + backup_cdhash: String, + local_rollback_available: bool, +} + +#[cfg(not(windows))] +fn prepare_platform_recovery_at(root: &Path) -> Result { + let current_bundle = current_app_bundle_path()?; + let (backup, evidence) = prepare_last_known_good_at(root, ¤t_bundle)?; + Ok(PlatformRecoveryPreparation { + installing_state: "LAST_KNOWN_GOOD_VERIFIED_INSTALLING", + backup_bundle_path: backup.to_string_lossy().into_owned(), + backup_identifier: evidence.identifier, + backup_team_identifier: evidence.team_identifier, + backup_cdhash: evidence.cdhash, + local_rollback_available: true, + }) +} + +#[cfg(windows)] +fn prepare_platform_recovery_at(_root: &Path) -> Result { + Ok(PlatformRecoveryPreparation { + installing_state: "SIGNED_PACKAGE_VERIFIED_INSTALLING_NO_LOCAL_ROLLBACK", + backup_bundle_path: String::new(), + backup_identifier: String::new(), + backup_team_identifier: String::new(), + backup_cdhash: String::new(), + local_rollback_available: false, + }) +} + +#[cfg(not(windows))] +fn platform_install_failed_state() -> &'static str { + "INSTALL_FAILED_BACKUP_RETAINED" +} + +#[cfg(windows)] +fn platform_install_failed_state() -> &'static str { + "INSTALL_FAILED_NO_LOCAL_BACKUP" +} + +#[cfg(not(windows))] +fn finalize_platform_recovery_artifacts( + root: &Path, + record: &ReleaseRecoveryRecord, +) -> Result<(), String> { + remove_owned_bundle_path(root, Path::new(&record.backup_bundle_path))?; + if let Some(failed) = record.failed_bundle_path.as_deref() { + remove_owned_failed_bundle(Path::new(failed), ¤t_app_parent()?)?; + } + Ok(()) +} + +#[cfg(windows)] +fn finalize_platform_recovery_artifacts( + _root: &Path, + record: &ReleaseRecoveryRecord, +) -> Result<(), String> { + if !record.backup_bundle_path.is_empty() || record.failed_bundle_path.is_some() { + return Err("HOLOLAKE_RELEASE_WINDOWS_RECOVERY_BOUNDARY_INVALID".into()); + } + Ok(()) +} + fn prepare_last_known_good_at( root: &Path, current_bundle: &Path, diff --git a/product-source/hololake-native-desktop/src-tauri/src/user_pncc_channel.rs b/product-source/hololake-native-desktop/src-tauri/src/user_pncc_channel.rs index 550deaf72..9966f0b60 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/user_pncc_channel.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/user_pncc_channel.rs @@ -402,7 +402,8 @@ fn write_json_atomic(path: &Path, value: &UserPnccBindingRecord) -> Result<(), S } fn run_git(root: &Path, args: &[&str], operation: &str) -> Result { - let output = Command::new("/usr/bin/git") + let executable = if cfg!(windows) { "git" } else { "/usr/bin/git" }; + let output = Command::new(executable) .current_dir(root) .env("GIT_TERMINAL_PROMPT", "0") .env("GIT_CONFIG_NOSYSTEM", "1") diff --git a/product-source/hololake-native-desktop/src-tauri/tauri.conf.json b/product-source/hololake-native-desktop/src-tauri/tauri.conf.json index 836ebf2db..247a4e08e 100644 --- a/product-source/hololake-native-desktop/src-tauri/tauri.conf.json +++ b/product-source/hololake-native-desktop/src-tauri/tauri.conf.json @@ -37,7 +37,8 @@ "targets": "all", "icon": [ "icons/icon.icns", - "icons/icon.png" + "icons/icon.png", + "icons/icon.ico" ], "createUpdaterArtifacts": true, "category": "Productivity",