Skip to content

Commit 5b044ac

Browse files
0.4.8
1 parent b0b9fd1 commit 5b044ac

File tree

2 files changed

+137
-91
lines changed

2 files changed

+137
-91
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/update/version_manager.rs

Lines changed: 136 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,22 @@ pub struct VersionManager {
2121
impl VersionManager {
2222
/// Create a new version manager
2323
pub fn new() -> Result<Self> {
24-
let user_dirs = UserDirs::new().ok_or(DigstoreError::HomeDirectoryNotFound)?;
25-
let versions_dir = user_dirs.home_dir().join(".digstore-versions");
24+
// Try system directory first, fall back to user directory if no admin privileges
25+
let program_files = std::env::var("ProgramFiles(x86)")
26+
.or_else(|_| std::env::var("ProgramFiles"))
27+
.unwrap_or_else(|_| "C:\\Program Files".to_string());
28+
29+
let system_versions_dir = PathBuf::from(program_files).join("dig-network");
30+
31+
// Test if we can write to system directory
32+
let versions_dir = if Self::can_write_to_directory(&system_versions_dir) {
33+
// Use system directory (preferred)
34+
system_versions_dir
35+
} else {
36+
// Fall back to user directory
37+
let user_dirs = UserDirs::new().ok_or(DigstoreError::HomeDirectoryNotFound)?;
38+
user_dirs.home_dir().join(".digstore-versions")
39+
};
2640

2741
// Create versions directory if it doesn't exist
2842
if !versions_dir.exists() {
@@ -37,6 +51,19 @@ impl VersionManager {
3751
})
3852
}
3953

54+
/// Test if we can write to a directory
55+
fn can_write_to_directory(dir: &Path) -> bool {
56+
// Try to create the directory and write a test file
57+
if fs::create_dir_all(dir).is_ok() {
58+
let test_file = dir.join("access_test.tmp");
59+
if fs::write(&test_file, "test").is_ok() {
60+
let _ = fs::remove_file(&test_file);
61+
return true;
62+
}
63+
}
64+
false
65+
}
66+
4067
/// Install a new version from a binary path
4168
pub fn install_version(&mut self, version: &str, binary_path: &Path) -> Result<()> {
4269
println!(
@@ -73,7 +100,7 @@ impl VersionManager {
73100
Ok(())
74101
}
75102

76-
/// Set the active version and update PATH/symlinks
103+
/// Set the active version and update PATH
77104
pub fn set_active_version(&mut self, version: &str) -> Result<()> {
78105
let version_dir = self.get_version_dir(version);
79106
let binary_path = version_dir.join(self.get_binary_name());
@@ -84,16 +111,13 @@ impl VersionManager {
84111
});
85112
}
86113

87-
// Update the active symlink/shortcut
88-
self.update_active_link(&binary_path)?;
114+
// Update PATH to point to this version
115+
self.update_path_to_version(version)?;
89116

90117
// Save active version info
91118
self.save_active_version(version)?;
92119
self.active_version = Some(version.to_string());
93120

94-
// Refresh current environment PATH
95-
self.refresh_current_environment()?;
96-
97121
println!(
98122
" {} Active version set to: {}",
99123
"✓".green(),
@@ -269,7 +293,7 @@ impl VersionManager {
269293

270294
/// Get the directory for a specific version
271295
pub fn get_version_dir(&self, version: &str) -> PathBuf {
272-
self.versions_dir.join(version)
296+
self.versions_dir.join(format!("v{}", version))
273297
}
274298

275299
/// Get the binary name for the current platform
@@ -281,32 +305,10 @@ impl VersionManager {
281305
}
282306
}
283307

284-
/// Update the active symlink or shortcut
308+
/// Update the active symlink or shortcut (no longer needed with direct PATH approach)
285309
fn update_active_link(&self, binary_path: &Path) -> Result<()> {
286-
let link_path = self.get_active_link_path()?;
287-
288-
// Remove existing link/shortcut
289-
if link_path.exists() {
290-
fs::remove_file(&link_path)?;
291-
}
292-
293-
// Create new link/shortcut
294-
#[cfg(windows)]
295-
{
296-
// On Windows, create a batch file that calls the active version
297-
let batch_content = format!(
298-
"@echo off\n\"{}\" %*\n",
299-
binary_path.display()
300-
);
301-
fs::write(&link_path, batch_content)?;
302-
}
303-
304-
#[cfg(unix)]
305-
{
306-
// On Unix, create a symlink
307-
std::os::unix::fs::symlink(binary_path, &link_path)?;
308-
}
309-
310+
// With the new system versioned approach, we update PATH directly instead of using batch files
311+
// This method is kept for compatibility but does nothing
310312
Ok(())
311313
}
312314

@@ -349,21 +351,9 @@ impl VersionManager {
349351
}
350352
}
351353

352-
/// Get the system-wide installation directory for a version
354+
/// Get the system-wide installation directory for a version (now same as get_version_dir)
353355
pub fn get_system_install_dir(&self, version: &str) -> PathBuf {
354-
#[cfg(windows)]
355-
{
356-
let program_files = std::env::var("ProgramFiles(x86)")
357-
.or_else(|_| std::env::var("ProgramFiles"))
358-
.unwrap_or_else(|_| "C:\\Program Files".to_string());
359-
360-
PathBuf::from(program_files).join("dig-network").join(format!("v{}", version))
361-
}
362-
363-
#[cfg(not(windows))]
364-
{
365-
PathBuf::from("/usr/local/lib/digstore").join(format!("v{}", version))
366-
}
356+
self.get_version_dir(version)
367357
}
368358

369359
/// Save the active version to a config file
@@ -428,69 +418,77 @@ impl VersionManager {
428418
self.install_from_msi_user_level(version, msi_path)
429419
}
430420

431-
/// Install from MSI directly to user versioned directory
421+
/// Install from MSI to system versioned directory
432422
fn install_from_msi_user_level(&mut self, version: &str, msi_path: &Path) -> Result<()> {
433-
println!(" {} Installing MSI directly to versioned directory", "•".cyan());
423+
println!(" {} Installing MSI to system versioned directory", "•".cyan());
434424

435-
let user_install_dir = self.get_version_dir(version);
436-
fs::create_dir_all(&user_install_dir)?;
425+
let version_dir = self.get_version_dir(version);
426+
fs::create_dir_all(&version_dir)?;
437427

438-
// Install MSI directly to the versioned directory (not system location)
439-
println!(" {} Installing to: {}", "•".cyan(), user_install_dir.display());
428+
// First, install MSI to the default location (where it wants to go)
429+
println!(" {} Installing MSI to system location...", "•".cyan());
440430

441431
let install_output = Command::new("msiexec")
442432
.args(&[
443433
"/i", msi_path.to_str().unwrap(), // Install the MSI
444434
"/quiet", "/norestart", // Silent installation
445-
&format!("INSTALLDIR={}", user_install_dir.display()), // Target directory
446-
&format!("TARGETDIR={}", user_install_dir.display()), // Alternative target property
447-
"ALLUSERS=0", // User-level installation
448-
"MSIINSTALLPERUSER=1", // Per-user installation
449435
])
450436
.output()
451437
.map_err(|e| DigstoreError::ConfigurationError {
452438
reason: format!("Failed to run MSI installation: {}", e),
453439
})?;
454440

455-
let binary_path = user_install_dir.join(self.get_binary_name());
456-
let mut found = false;
441+
if !install_output.status.success() {
442+
let stderr = String::from_utf8_lossy(&install_output.stderr);
443+
let stdout = String::from_utf8_lossy(&install_output.stdout);
444+
445+
return Err(DigstoreError::ConfigurationError {
446+
reason: format!("MSI installation failed. Stderr: {}, Stdout: {}", stderr, stdout),
447+
});
448+
}
457449

458-
if install_output.status.success() {
459-
// Check if binary was installed to the target directory
460-
if binary_path.exists() {
461-
found = true;
462-
} else {
463-
// Check common subdirectories within the install dir
464-
let subdirs = ["bin", ".", "digstore"];
465-
for subdir in &subdirs {
466-
let alt_path = user_install_dir.join(subdir).join(self.get_binary_name());
467-
if alt_path.exists() {
468-
fs::copy(&alt_path, &binary_path)?;
469-
found = true;
470-
break;
450+
// Now move the installed binary to the versioned directory
451+
println!(" {} Moving installation to versioned directory...", "•".cyan());
452+
453+
let base_install_dir = self.versions_dir.clone(); // C:\Program Files (x86)\dig-network\
454+
let source_binary = base_install_dir.join(self.get_binary_name());
455+
let target_binary = version_dir.join(self.get_binary_name());
456+
457+
// Check if binary was installed to base directory
458+
if source_binary.exists() {
459+
// Move the binary to the versioned directory
460+
fs::copy(&source_binary, &target_binary)?;
461+
462+
// Remove from base directory
463+
let _ = fs::remove_file(&source_binary);
464+
465+
// Also move any other files (like DIG.ico)
466+
if let Ok(entries) = fs::read_dir(&base_install_dir) {
467+
for entry in entries.flatten() {
468+
let entry_path = entry.path();
469+
if entry_path.is_file() && entry_path != source_binary {
470+
let filename = entry_path.file_name().unwrap();
471+
let target_path = version_dir.join(filename);
472+
let _ = fs::copy(&entry_path, &target_path);
473+
let _ = fs::remove_file(&entry_path);
471474
}
472475
}
473476
}
474-
}
475-
476-
if !found {
477-
let stderr = String::from_utf8_lossy(&install_output.stderr);
478-
let stdout = String::from_utf8_lossy(&install_output.stdout);
479477

480-
// Fallback: Try extraction method if direct installation failed
481-
println!(" {} Direct installation failed, trying extraction...", "!".yellow());
482-
return self.fallback_msi_extraction(version, msi_path);
478+
println!(
479+
" {} Version {} installed to: {}",
480+
"✓".green(),
481+
version.bright_cyan(),
482+
version_dir.display().to_string().dimmed()
483+
);
484+
} else {
485+
return Err(DigstoreError::ConfigurationError {
486+
reason: format!("MSI installation succeeded but binary not found at: {}", source_binary.display()),
487+
});
483488
}
484489

485-
println!(
486-
" {} Version {} installed to: {}",
487-
"✓".green(),
488-
version.bright_cyan(),
489-
user_install_dir.display().to_string().dimmed()
490-
);
491-
492-
// Clean up any system installations and PATH entries
493-
self.cleanup_system_installations()?;
490+
// Update PATH to point to the versioned directory
491+
self.update_path_to_version(version)?;
494492

495493
// Set as active version
496494
self.set_active_version(version)?;
@@ -718,6 +716,54 @@ impl VersionManager {
718716
Ok(())
719717
}
720718

719+
/// Update PATH to point to a specific version directory
720+
fn update_path_to_version(&self, version: &str) -> Result<()> {
721+
let version_dir = self.get_version_dir(version);
722+
723+
println!(" {} Updating PATH to: {}", "•".cyan(), version_dir.display());
724+
725+
// Get current PATH
726+
let current_path = std::env::var("PATH").unwrap_or_default();
727+
let path_entries: Vec<&str> = current_path.split(';').collect();
728+
729+
// Remove any existing dig-network entries
730+
let base_dir_str = self.versions_dir.to_string_lossy();
731+
let version_dir_str = version_dir.to_string_lossy();
732+
733+
let filtered_entries: Vec<&str> = path_entries
734+
.into_iter()
735+
.filter(|entry| {
736+
let entry_trimmed = entry.trim();
737+
// Remove old dig-network entries (including versioned ones)
738+
!entry_trimmed.starts_with(&base_dir_str.to_string())
739+
})
740+
.collect();
741+
742+
// Add the new version directory to the front of PATH
743+
let new_path = format!("{};{}", version_dir_str, filtered_entries.join(";"));
744+
745+
// Update PATH
746+
let output = Command::new("setx")
747+
.args(&["PATH", &new_path])
748+
.output()
749+
.map_err(|e| DigstoreError::ConfigurationError {
750+
reason: format!("Failed to update PATH: {}", e),
751+
})?;
752+
753+
if output.status.success() {
754+
println!(" {} Updated PATH to use version {}", "✓".green(), version.bright_cyan());
755+
756+
// Also update current environment
757+
std::env::set_var("PATH", &new_path);
758+
} else {
759+
let stderr = String::from_utf8_lossy(&output.stderr);
760+
println!(" {} Could not update PATH automatically: {}", "!".yellow(), stderr);
761+
println!(" {} Manually add to PATH: {}", "→".cyan(), version_dir.display());
762+
}
763+
764+
Ok(())
765+
}
766+
721767
/// Clean up system PATH entries that point to old installation locations
722768
fn cleanup_system_path_entries(&self) -> Result<()> {
723769
println!(" {} Cleaning up old PATH entries...", "•".cyan());

0 commit comments

Comments
 (0)