fix: separate profile base from working directory
This commit is contained in:
parent
061e52c35c
commit
1688f2a640
|
|
@ -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<PathBuf>,
|
||||
worker_metadata_dir: Option<PathBuf>,
|
||||
|
|
@ -56,11 +56,11 @@ pub struct ProfileRuntimeWorkerFactory {
|
|||
}
|
||||
|
||||
impl ProfileRuntimeWorkerFactory {
|
||||
pub fn new(workspace_root: impl Into<PathBuf>) -> Self {
|
||||
let workspace_root = workspace_root.into();
|
||||
pub fn new(profile_base_dir: impl Into<PathBuf>) -> 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<WorkerHandle, String> {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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<PathBuf, WorkingDirectoryDiagnostic> {
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user