stmo_cli/commands/
update.rs1#![allow(clippy::missing_errors_doc)]
2
3use anyhow::Result;
4use std::fs;
5use std::path::{Path, PathBuf};
6
7fn cargo_home() -> Option<PathBuf> {
8 std::env::var("CARGO_HOME")
9 .ok()
10 .map(PathBuf::from)
11 .or_else(|| {
12 std::env::var("HOME")
13 .ok()
14 .map(|h| PathBuf::from(h).join(".cargo"))
15 })
16}
17
18fn dir_writable(dir: &Path) -> bool {
19 let test_path = dir.join(".stmo-cli-write-test");
20 match fs::write(&test_path, b"") {
21 Ok(()) => {
22 let _ = fs::remove_file(&test_path);
23 true
24 }
25 Err(e) => e.kind() != std::io::ErrorKind::PermissionDenied,
26 }
27}
28
29fn cargo_writable() -> bool {
30 let Some(home) = cargo_home() else {
31 return true;
32 };
33 if !home.exists() {
34 return true;
35 }
36 dir_writable(&home)
37}
38
39pub fn update() -> Result<()> {
40 if !cargo_writable() {
41 anyhow::bail!(
42 "Cannot update stmo-cli in the current environment \
43 (write access to ~/.cargo/ is restricted).\n\
44 Run this command outside the sandbox:\n cargo binstall stmo-cli"
45 );
46 }
47
48 let status = std::process::Command::new("cargo")
49 .args(["binstall", "--no-confirm", "stmo-cli"])
50 .status();
51
52 match status {
53 Ok(s) if s.success() => {
54 println!("stmo-cli updated successfully.");
55 return Ok(());
56 }
57 _ => eprintln!("cargo binstall not available, falling back to cargo install"),
58 }
59
60 let status = std::process::Command::new("cargo")
61 .args(["install", "stmo-cli"])
62 .status()?;
63
64 if status.success() {
65 println!("stmo-cli updated successfully.");
66 Ok(())
67 } else {
68 anyhow::bail!("cargo install stmo-cli failed");
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use tempfile::TempDir;
76
77 #[test]
78 fn test_dir_writable_on_writable_dir() {
79 let temp = TempDir::new().unwrap();
80 assert!(dir_writable(temp.path()));
81 }
82
83 #[cfg(unix)]
84 #[test]
85 fn test_dir_writable_on_readonly_dir() {
86 use std::os::unix::fs::PermissionsExt;
87 let temp = TempDir::new().unwrap();
88 fs::set_permissions(temp.path(), fs::Permissions::from_mode(0o555)).unwrap();
89 let result = dir_writable(temp.path());
90 fs::set_permissions(temp.path(), fs::Permissions::from_mode(0o755)).unwrap();
91 assert!(!result);
92 }
93}