diff --git a/crates/worker-runtime/src/worker_backend.rs b/crates/worker-runtime/src/worker_backend.rs index 71f94397..d6c9d357 100644 --- a/crates/worker-runtime/src/worker_backend.rs +++ b/crates/worker-runtime/src/worker_backend.rs @@ -47,7 +47,7 @@ pub trait RuntimeWorkerFactory: Send + Sync + 'static { /// `WorkerController`. #[derive(Debug, Clone)] pub struct ProfileRuntimeWorkerFactory { - workspace_root: PathBuf, + profile_base_dir: PathBuf, cwd: PathBuf, store_dir: Option, worker_metadata_dir: Option, @@ -56,11 +56,11 @@ pub struct ProfileRuntimeWorkerFactory { } impl ProfileRuntimeWorkerFactory { - pub fn new(workspace_root: impl Into) -> Self { - let workspace_root = workspace_root.into(); + pub fn new(profile_base_dir: impl Into) -> Self { + let profile_base_dir = profile_base_dir.into(); Self { - cwd: workspace_root.clone(), - workspace_root, + cwd: profile_base_dir.clone(), + profile_base_dir, store_dir: None, worker_metadata_dir: None, profile: None, @@ -161,19 +161,22 @@ impl RuntimeWorkerFactory for ProfileRuntimeWorkerFactory { ) -> Result { let worker_name = Self::runtime_worker_name(&request); let profile = self.runtime_profile(&request); - let workspace_root = request + let worker_root = request .working_directory .as_ref() - .map(|binding| binding.workspace_root().to_path_buf()) - .unwrap_or_else(|| self.workspace_root.clone()); + .map(|binding| binding.root().to_path_buf()) + .unwrap_or_else(|| self.profile_base_dir.clone()); let cwd = request .working_directory .as_ref() .map(|binding| binding.cwd().to_path_buf()) .unwrap_or_else(|| self.cwd.clone()); + // Profile discovery uses the runtime profile base, not the Worker working directory. + // The working directory may be an arbitrary repository checkout that intentionally + // does not carry Yoi profile files. let (mut manifest, loader) = worker::entrypoint::resolve_runtime_profile_manifest( profile.as_deref(), - &workspace_root, + &self.profile_base_dir, &worker_name, )?; manifest.worker.name = worker_name; @@ -194,10 +197,9 @@ impl RuntimeWorkerFactory for ProfileRuntimeWorkerFactory { })?; let store = CombinedStore::new(session_store, worker_metadata_store); - let worker = - Worker::from_manifest_with_context(manifest, store, loader, workspace_root, cwd) - .await - .map_err(|err| format!("failed to create Worker from profile: {err}"))?; + let worker = Worker::from_manifest_with_context(manifest, store, loader, worker_root, cwd) + .await + .map_err(|err| format!("failed to create Worker from profile: {err}"))?; let runtime_base = self.runtime_base_dir()?; let (handle, _shutdown_rx) = WorkerController::spawn(worker, &runtime_base) diff --git a/crates/worker-runtime/src/working_directory.rs b/crates/worker-runtime/src/working_directory.rs index 1053ba88..00b2c06d 100644 --- a/crates/worker-runtime/src/working_directory.rs +++ b/crates/worker-runtime/src/working_directory.rs @@ -58,15 +58,15 @@ impl WorkingDirectory { #[derive(Clone, Debug, PartialEq, Eq)] pub struct WorkingDirectoryBinding { pub working_directory: WorkingDirectory, - pub workspace_root: PathBuf, + pub root: PathBuf, pub cwd: PathBuf, working_directory_root: PathBuf, source_repository_path: PathBuf, } impl WorkingDirectoryBinding { - pub fn workspace_root(&self) -> &Path { - &self.workspace_root + pub fn root(&self) -> &Path { + &self.root } pub fn cwd(&self) -> &Path { @@ -183,7 +183,7 @@ impl LocalGitWorktreeMaterializer { ) -> Result<(), WorkingDirectoryDiagnostic> { let record = WorkingDirectoryMaterializationRecord { working_directory: binding.working_directory.clone(), - workspace_root: binding.workspace_root.clone(), + root: binding.root.clone(), source_repository_path: binding.source_repository_path.clone(), }; let path = binding.working_directory_root.join(MATERIALIZATION_RECORD); @@ -221,8 +221,8 @@ impl LocalGitWorktreeMaterializer { })?; Ok(WorkingDirectoryBinding { working_directory: record.working_directory, - workspace_root: record.workspace_root.clone(), - cwd: record.workspace_root, + root: record.root.clone(), + cwd: record.root, working_directory_root, source_repository_path: record.source_repository_path, }) @@ -302,16 +302,16 @@ impl LocalGitWorktreeMaterializer { .filter(|value| !value.is_empty()); let working_directory_root = self.working_directory_root(&working_directory_id); - let workspace_root = working_directory_root + let worktree_root = working_directory_root .join("root") .join(sanitize_path_component(&request.repository.id)); - if workspace_root.exists() { + if worktree_root.exists() { return Err(WorkingDirectoryDiagnostic::new( "working_directory_exists", "working directory working_directory target already exists; cleanup or choose a new working_directory", )); } - fs::create_dir_all(workspace_root.parent().ok_or_else(|| { + fs::create_dir_all(worktree_root.parent().ok_or_else(|| { WorkingDirectoryDiagnostic::new( "working_directory_invalid_target", "working directory working_directory target has no parent directory", @@ -324,14 +324,14 @@ impl LocalGitWorktreeMaterializer { ) })?; - let workspace_root_arg = path_str(&workspace_root)?; + let workspace_worktree_root_arg = path_str(&worktree_root)?; git_status( &source_root, [ "worktree", "add", "--detach", - workspace_root_arg.as_str(), + workspace_worktree_root_arg.as_str(), resolved_commit.as_str(), ], )?; @@ -363,8 +363,8 @@ impl LocalGitWorktreeMaterializer { }; let binding = WorkingDirectoryBinding { working_directory, - workspace_root: workspace_root.clone(), - cwd: workspace_root, + root: worktree_root.clone(), + cwd: worktree_root, working_directory_root, source_repository_path: source_root, }; @@ -412,7 +412,7 @@ impl WorkingDirectoryMaterializer for LocalGitWorktreeMaterializer { "working directory working_directory is not active", )); } - let cwd = validate_relative_cwd(binding.workspace_root(), relative_cwd)?; + let cwd = validate_relative_cwd(binding.root(), relative_cwd)?; Ok(WorkingDirectoryBinding { cwd, ..binding }) } @@ -481,26 +481,26 @@ impl WorkingDirectoryMaterializer for LocalGitWorktreeMaterializer { "working directory working directory root is unavailable; backend-private path details were omitted", ) })?; - let workspace_root = binding.workspace_root.canonicalize().map_err(|_| { + let root = binding.root.canonicalize().map_err(|_| { WorkingDirectoryDiagnostic::new( "working_directory_cleanup_target_invalid", "working directory root is unavailable; backend-private path details were omitted", ) })?; - if !workspace_root.starts_with(&working_directory_root) { + if !root.starts_with(&working_directory_root) { return Err(WorkingDirectoryDiagnostic::new( "working_directory_cleanup_escape_rejected", "working directory cleanup target is outside the working directory root", )); } - let workspace_root_arg = path_str(&workspace_root)?; + let workspace_worktree_root_arg = path_str(&root)?; let remove_result = git_status( binding.source_repository_path(), - ["worktree", "remove", "--force", workspace_root_arg.as_str()], + ["worktree", "remove", "--force", workspace_worktree_root_arg.as_str()], ) .or_else(|_| { - if workspace_root.exists() { - fs::remove_dir_all(&workspace_root).map_err(|_| { + if root.exists() { + fs::remove_dir_all(&root).map_err(|_| { WorkingDirectoryDiagnostic::new( "working_directory_cleanup_failed", "failed to remove working directory; backend-private path details were omitted", @@ -517,7 +517,7 @@ impl WorkingDirectoryMaterializer for LocalGitWorktreeMaterializer { }; let updated = WorkingDirectoryBinding { working_directory, - workspace_root: binding.workspace_root.clone(), + root: binding.root.clone(), cwd: binding.cwd.clone(), working_directory_root: binding.working_directory_root.clone(), source_repository_path: binding.source_repository_path.clone(), @@ -530,7 +530,7 @@ impl WorkingDirectoryMaterializer for LocalGitWorktreeMaterializer { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] struct WorkingDirectoryMaterializationRecord { working_directory: WorkingDirectory, - workspace_root: PathBuf, + root: PathBuf, source_repository_path: PathBuf, } @@ -632,10 +632,10 @@ fn validate_working_directory_id( } fn validate_relative_cwd( - workspace_root: &Path, + root: &Path, relative_cwd: Option<&str>, ) -> Result { - let workspace_root = workspace_root.canonicalize().map_err(|_| { + let root = root.canonicalize().map_err(|_| { WorkingDirectoryDiagnostic::new( "working_directory_root_unavailable", "working directory root is unavailable; backend-private path details were omitted", @@ -643,7 +643,7 @@ fn validate_relative_cwd( })?; let relative = relative_cwd.unwrap_or(".").trim(); if relative.is_empty() || relative == "." { - return Ok(workspace_root); + return Ok(root); } let relative_path = Path::new(relative); if relative_path.is_absolute() @@ -659,16 +659,13 @@ fn validate_relative_cwd( "working directory relative_cwd must be a relative path inside the working directory root", )); } - let target = workspace_root - .join(relative_path) - .canonicalize() - .map_err(|_| { - WorkingDirectoryDiagnostic::new( - "working_directory_relative_cwd_unavailable", - "working directory relative_cwd does not identify an existing directory", - ) - })?; - if !target.starts_with(&workspace_root) || !target.is_dir() { + let target = root.join(relative_path).canonicalize().map_err(|_| { + WorkingDirectoryDiagnostic::new( + "working_directory_relative_cwd_unavailable", + "working directory relative_cwd does not identify an existing directory", + ) + })?; + if !target.starts_with(&root) || !target.is_dir() { return Err(WorkingDirectoryDiagnostic::new( "working_directory_relative_cwd_escape_rejected", "working directory relative_cwd must resolve to a directory inside the working directory root", @@ -737,9 +734,9 @@ mod tests { .materialize(&worker_ref(1), &request(repo.path())) .unwrap(); - assert!(binding.workspace_root.starts_with(runtime_root.path())); - assert!(binding.workspace_root.join("README.md").exists()); - let branch = git_stdout(binding.workspace_root(), ["branch", "--show-current"]).unwrap(); + assert!(binding.root.starts_with(runtime_root.path())); + assert!(binding.root.join("README.md").exists()); + let branch = git_stdout(binding.root(), ["branch", "--show-current"]).unwrap(); assert!( branch.is_empty(), "worktree should be detached, got {branch}" @@ -772,11 +769,11 @@ mod tests { .materialize(&worker_ref(2), &request(repo.path())) .unwrap(); - assert_ne!(first.workspace_root, second.workspace_root); - assert!(first.workspace_root.starts_with(runtime_root.path())); - assert!(second.workspace_root.starts_with(runtime_root.path())); - assert!(!first.workspace_root.starts_with(repo.path())); - assert!(!second.workspace_root.starts_with(repo.path())); + assert_ne!(first.root, second.root); + assert!(first.root.starts_with(runtime_root.path())); + assert!(second.root.starts_with(runtime_root.path())); + assert!(!first.root.starts_with(repo.path())); + assert!(!second.root.starts_with(repo.path())); } #[test] @@ -893,8 +890,7 @@ mod tests { #[cfg(unix)] { - std::os::unix::fs::symlink("/tmp", working_directory.workspace_root().join("escape")) - .unwrap(); + std::os::unix::fs::symlink("/tmp", working_directory.root().join("escape")).unwrap(); assert_eq!( materializer .bind_working_directory(&working_directory.working_directory.id, Some("escape")) @@ -913,11 +909,11 @@ mod tests { let binding = materializer .materialize(&worker_ref(1), &request(repo.path())) .unwrap(); - let workspace_root = binding.workspace_root.clone(); + let root = binding.root.clone(); materializer.cleanup(&binding).unwrap(); - assert!(!workspace_root.exists()); + assert!(!root.exists()); let raw = fs::read_to_string( binding .working_directory_root() diff --git a/crates/workspace-server/src/hosts.rs b/crates/workspace-server/src/hosts.rs index f1655640..b01f863a 100644 --- a/crates/workspace-server/src/hosts.rs +++ b/crates/workspace-server/src/hosts.rs @@ -2541,10 +2541,12 @@ fn embedded_runtime_diagnostic(error: &EmbeddedRuntimeError) -> RuntimeDiagnosti DiagnosticSeverity::Warning, "Embedded Worker has no execution backend attached".to_string(), ), - EmbeddedRuntimeError::WorkerExecutionRejected { .. } => diagnostic( + EmbeddedRuntimeError::WorkerExecutionRejected { + operation, outcome, .. + } => diagnostic( "embedded_worker_execution_rejected", DiagnosticSeverity::Warning, - "Embedded Worker execution backend rejected the operation".to_string(), + format!("Embedded Worker execution backend rejected {operation:?} with {outcome:?}"), ), EmbeddedRuntimeError::LimitTooLarge { requested, max } => diagnostic( "embedded_runtime_limit_too_large",