on main help sign in
fix: now project dir correctly mounted
5659f2arobcholz
src/explain.rs+1−1
@@ -46,7 +46,7 @@ fn default_mounts(cwd: &Path) -> Result<Vec<tui::MountListRow>, Box<dyn Error +
46 .file_name()
46 .file_name()
47 .and_then(|name| name.to_str())
47 .and_then(|name| name.to_str())
48 .unwrap_or("project");
48 .unwrap_or("project");
49− let project_guest = format!("/root/{project_name}");
49+ let project_guest = format!("~/{project_name}");
50 let project_host = display_path(cwd);
50 let project_host = display_path(cwd);
51 let mut rows = vec![tui::MountListRow {
51 let mut rows = vec![tui::MountListRow {
52 host: project_host,
52 host: project_host,
src/instance.rs+2
@@ -394,6 +394,7 @@ fn ssh_port_open(ip: &str) -> bool {
394pub(crate) fn build_ssh_login_actions(
394pub(crate) fn build_ssh_login_actions(
395 config: &Arc<Mutex<InstanceConfig>>,
395 config: &Arc<Mutex<InstanceConfig>>,
396 project_name: &str,
396 project_name: &str,
397+ project_guest_dir: &str,
397 guest_dir: &str,
398 guest_dir: &str,
398 key_name: &str,
399 key_name: &str,
399 home_links_script: &str,
400 home_links_script: &str,
@@ -409,6 +410,7 @@ pub(crate) fn build_ssh_login_actions(
409 .replace("__SSH_USER__", &ssh_user)
410 .replace("__SSH_USER__", &ssh_user)
410 .replace("__SUDO_PASSWORD__", &sudo_password)
411 .replace("__SUDO_PASSWORD__", &sudo_password)
411 .replace("__PROJECT_NAME__", project_name)
412 .replace("__PROJECT_NAME__", project_name)
413+ .replace("__PROJECT_GUEST_DIR__", project_guest_dir)
412 .replace("__KEY_PATH__", &key_path)
414 .replace("__KEY_PATH__", &key_path)
413 .replace("__VIBEBOX_SHELL_SCRIPT__", &commands::render_shell_script())
415 .replace("__VIBEBOX_SHELL_SCRIPT__", &commands::render_shell_script())
414 .replace("__VIBEBOX_HOME_LINKS__", home_links_script);
416 .replace("__VIBEBOX_HOME_LINKS__", home_links_script);
src/ssh.sh+17−1
@@ -3,6 +3,7 @@ set -eu
3
3
4SSH_USER="__SSH_USER__"
4SSH_USER="__SSH_USER__"
5PROJECT_NAME="__PROJECT_NAME__"
5PROJECT_NAME="__PROJECT_NAME__"
6+PROJECT_GUEST_DIR="__PROJECT_GUEST_DIR__"
6KEY_PATH="__KEY_PATH__"
7KEY_PATH="__KEY_PATH__"
7
8
8diag() { echo "[vibebox][diag] $*" >&2; }
9diag() { echo "[vibebox][diag] $*" >&2; }
@@ -49,7 +50,7 @@ dump_diag() {
49}
50}
50
51
51# 1) tmpfs mount
52# 1) tmpfs mount
52−TARGET="/root/${PROJECT_NAME}/.vibebox"
53+TARGET="${PROJECT_GUEST_DIR}/.vibebox"
53if [ -d "$TARGET" ] && ! mountpoint -q "$TARGET"; then
54if [ -d "$TARGET" ] && ! mountpoint -q "$TARGET"; then
54 mount -t tmpfs tmpfs "$TARGET"
55 mount -t tmpfs tmpfs "$TARGET"
55fi
56fi
@@ -78,6 +79,21 @@ __VIBEBOX_SHELL_SCRIPT__
78VIBEBOX_SHELL_EOF
79VIBEBOX_SHELL_EOF
79chmod 644 /etc/profile.d/vibebox.sh
80chmod 644 /etc/profile.d/vibebox.sh
80
81
82+# Auto-cd into project for interactive shells
83+cat > /etc/profile.d/vibebox-project.sh <<'VIBEBOX_PROJECT_EOF'
84+case "$-" in
85+ *i*)
86+ project_home="${HOME}/__PROJECT_NAME__"
87+ if [ "$USER" = "__SSH_USER__" ] && [ -d "$project_home" ]; then
88+ cd "$project_home"
89+ elif [ "$USER" = "__SSH_USER__" ] && [ -d "__PROJECT_GUEST_DIR__" ]; then
90+ cd "__PROJECT_GUEST_DIR__"
91+ fi
92+ ;;
93+esac
94+VIBEBOX_PROJECT_EOF
95+chmod 644 /etc/profile.d/vibebox-project.sh
96+
81if ! grep -q "vibebox-aliases" "${USER_HOME}/.bashrc" 2>/dev/null; then
97if ! grep -q "vibebox-aliases" "${USER_HOME}/.bashrc" 2>/dev/null; then
82 {
98 {
83 echo ""
99 echo ""
src/vm.rs+3−10
@@ -32,6 +32,7 @@ const DEBIAN_COMPRESSED_DISK_URL: &str = "https://cloud.debian.org/images/cloud/
32const DEBIAN_COMPRESSED_SHA: &str = "6ab9be9e6834adc975268367f2f0235251671184345c34ee13031749fdfbf66fe4c3aafd949a2d98550426090e9ac645e79009c51eb0eefc984c15786570bb38";
32const DEBIAN_COMPRESSED_SHA: &str = "6ab9be9e6834adc975268367f2f0235251671184345c34ee13031749fdfbf66fe4c3aafd949a2d98550426090e9ac645e79009c51eb0eefc984c15786570bb38";
33const DEBIAN_COMPRESSED_SIZE_BYTES: u64 = 280901576;
33const DEBIAN_COMPRESSED_SIZE_BYTES: u64 = 280901576;
34const SHARED_DIRECTORIES_TAG: &str = "shared";
34const SHARED_DIRECTORIES_TAG: &str = "shared";
35+pub const PROJECT_GUEST_BASE: &str = "/usr/local/vibebox-mounts";
35
36
36const BYTES_PER_MB: u64 = 1024 * 1024;
37const BYTES_PER_MB: u64 = 1024 * 1024;
37const DEFAULT_CPU_COUNT: usize = 2;
38const DEFAULT_CPU_COUNT: usize = 2;
@@ -247,7 +248,8 @@ where
247 let mut directory_shares = Vec::new();
248 let mut directory_shares = Vec::new();
248
249
249 if !args.no_default_mounts {
250 if !args.no_default_mounts {
251+ let project_guest_dir = PathBuf::from(PROJECT_GUEST_BASE).join(project_name);
250− login_actions.push(Send(format!("cd {project_name}")));
252+ login_actions.push(Send(format!("cd {}", project_guest_dir.display())));
251
253
252 // discourage read/write of .git folder from within the VM. note that this isn't secure, since the VM runs as root and could unmount this.
254 // discourage read/write of .git folder from within the VM. note that this isn't secure, since the VM runs as root and could unmount this.
253 // I couldn't find an alternative way to do this --- the MacOS sandbox doesn't apply to the Apple Virtualization system
255 // I couldn't find an alternative way to do this --- the MacOS sandbox doesn't apply to the Apple Virtualization system
@@ -255,15 +257,6 @@ where
255 login_actions.push(Send(r"mount -t tmpfs tmpfs .git/".into()));
257 login_actions.push(Send(r"mount -t tmpfs tmpfs .git/".into()));
256 }
258 }
257
259
258− directory_shares.push(
259− DirectoryShare::new(
260− project_root,
261− PathBuf::from("/root/").join(project_name),
262− false,
263− )
264− .expect("Project directory must exist"),
265− );
266−
267 directory_shares.push(mise_directory_share);
260 directory_shares.push(mise_directory_share);
268 }
261 }
269
262
src/vm_manager.rs+32−3
@@ -25,7 +25,7 @@ use crate::{
25 session_manager::{
25 session_manager::{
26 GLOBAL_DIR_NAME, INSTANCE_FILENAME, VM_MANAGER_PID_NAME, VM_MANAGER_SOCKET_NAME,
26 GLOBAL_DIR_NAME, INSTANCE_FILENAME, VM_MANAGER_PID_NAME, VM_MANAGER_SOCKET_NAME,
27 },
27 },
28− vm::{self, DirectoryShare, LoginAction, VmInput},
28+ vm::{self, DirectoryShare, LoginAction, PROJECT_GUEST_BASE, VmInput},
29};
29};
30
30
31const VM_MANAGER_LOCK_NAME: &str = "vm.lock";
31const VM_MANAGER_LOCK_NAME: &str = "vm.lock";
@@ -200,6 +200,30 @@ fn cleanup_stale_manager(instance_dir: &Path) {
200 let _ = fs::remove_file(&pid_path);
200 let _ = fs::remove_file(&pid_path);
201}
201}
202
202
203+fn inject_project_mount(
204+ mounts: &mut Vec<String>,
205+ project_root: &Path,
206+ ssh_user: &str,
207+ project_name: &str,
208+) {
209+ let guest_tilde = format!("~/{project_name}");
210+ let guest_home = format!("/home/{ssh_user}/{project_name}");
211+ let guest_base = format!("{PROJECT_GUEST_BASE}/{project_name}");
212+ let already_mapped = mounts.iter().any(|spec| {
213+ let parts: Vec<&str> = spec.split(':').collect();
214+ if parts.len() < 2 {
215+ return false;
216+ }
217+ let guest = parts[1];
218+ guest == guest_tilde || guest == guest_home || guest == guest_base
219+ });
220+ if already_mapped {
221+ return;
222+ }
223+ let host = project_root.display();
224+ mounts.insert(0, format!("{host}:{guest_tilde}:read-write"));
225+}
226+
203fn is_socket_path(path: &Path) -> bool {
227fn is_socket_path(path: &Path) -> bool {
204 fs::metadata(path)
228 fs::metadata(path)
205 .map(|meta| meta.file_type().is_socket())
229 .map(|meta| meta.file_type().is_socket())
@@ -252,7 +276,7 @@ fn rewrite_mount_spec(spec: &str, ssh_user: &str) -> (String, Option<HomeLink>)
252 return (spec.to_string(), None);
276 return (spec.to_string(), None);
253 }
277 }
254
278
255− let root_base = "/usr/local/vibebox-mounts";
279+ let root_base = PROJECT_GUEST_BASE;
256 let root_path = if rel.is_empty() {
280 let root_path = if rel.is_empty() {
257 root_base.to_string()
281 root_base.to_string()
258 } else {
282 } else {
@@ -563,7 +587,7 @@ impl VmExecutor for RealVmExecutor {
563
587
564fn run_manager_with(
588fn run_manager_with(
565 project_root: &Path,
589 project_root: &Path,
566− args: vm::VmArg,
590+ mut args: vm::VmArg,
567 auto_shutdown_ms: u64,
591 auto_shutdown_ms: u64,
568 executor: &dyn VmExecutor,
592 executor: &dyn VmExecutor,
569 options: ManagerOptions,
593 options: ManagerOptions,
@@ -602,8 +626,12 @@ fn run_manager_with(
602 .lock()
626 .lock()
603 .map(|cfg| cfg.ssh_user_display())
627 .map(|cfg| cfg.ssh_user_display())
604 .unwrap_or_else(|_| DEFAULT_SSH_USER.to_string());
628 .unwrap_or_else(|_| DEFAULT_SSH_USER.to_string());
629+ if !args.no_default_mounts {
630+ inject_project_mount(&mut args.mounts, project_root, &ssh_user, &project_name);
631+ }
605 let (args, home_links_script) = prepare_mounts_and_links(args, &ssh_user);
632 let (args, home_links_script) = prepare_mounts_and_links(args, &ssh_user);
606
633
634+ let project_guest_dir = format!("{PROJECT_GUEST_BASE}/{project_name}");
607 let ssh_guest_dir = format!("/root/{}", GLOBAL_DIR_NAME);
635 let ssh_guest_dir = format!("/root/{}", GLOBAL_DIR_NAME);
608 let extra_shares = vec![DirectoryShare::new(
636 let extra_shares = vec![DirectoryShare::new(
609 instance_dir.clone(),
637 instance_dir.clone(),
@@ -613,6 +641,7 @@ fn run_manager_with(
613 let extra_login_actions = build_ssh_login_actions(
641 let extra_login_actions = build_ssh_login_actions(
614 &config,
642 &config,
615 &project_name,
643 &project_name,
644+ &project_guest_dir,
616 ssh_guest_dir.as_str(),
645 ssh_guest_dir.as_str(),
617 "ssh_key",
646 "ssh_key",
618 &home_links_script,
647 &home_links_script,