Fix pulling metadata out of broken symlinks (#22396)
## Problem When developing extensions locally, developers will commonly put their source code in a specific directory. Zed uses this directory to create a symlink starting from `$HOME/Library/Application Support/Zed/extensions/installed` (MacOS path). When a developer then moves this source code and tries to reinstall the extension, Zed will fail with an unhelpful message (you can check the #Testing section). ## Change Summary With this PR, we fix this behaviour by handling broken symlinks specifically when returning the metadata on `fs::metadata`. Today, we 1. Pull the symlink metadata. 2. Return it if the file was not a symlink OR if it is, pull the metadata for the pointed file. After this change gets merged, we return the Symlink metadata if the symlink is broken. This makes the symlink be recreated since we remove the symlink either way. ## Risks associated with this change It's possible changing this behaviour will show additional cases where we are handling broken symlinks incorrectly. I expect this to be a better scenario AND backwards compatible. We have the same behaviour we had for 1. existing symlinks 2. normal files. ## Testing The way I have been reproducing this is by having a private extension of my own. I install it using the `zed: install dev extension` command after running `RUST_LOG=debug RUST_BACKTRACE=1 scripts/zed-local -1`. Then I move the extension to a different directory. Zed will now keeps a broken link on its `installed` directory: ``` ❯ ll Permissions Size User Date Modified Name lrwxr-xr-x@ - enrikes 24 Dec 12:15 brazil-config-zed-extension -> /Volumes/workplace/BrazilConfigZedExtension drwxr-xr-x@ - enrikes 5 Dec 14:48 java drwxr-xr-x@ - enrikes 12 Dec 13:04 kotlin drwxr-xr-x@ - enrikes 25 Oct 08:13 rose-pine-theme ``` Before the patch, Zed shows on its logs: ``` 2024-12-24T16:44:02+01:00 INFO extension::extension_builder] compiled Rust extension /Users/enrikes/Documents/BrazilConfigZedExtension [2024-12-24T16:44:02+01:00 INFO extension::extension_builder] compiling grammar brazil_config for extension /Users/enrikes/Documents/BrazilConfigZedExtension [2024-12-24T16:44:02+01:00 INFO extension::extension_builder] checking out brazil_config parser [2024-12-24T16:44:04+01:00 INFO extension::extension_builder] compiling brazil_config parser [2024-12-24T16:44:05+01:00 INFO extension::extension_builder] compiled grammar brazil_config for extension /Users/enrikes/Documents/BrazilConfigZedExtension [2024-12-24T16:44:05+01:00 INFO extension::extension_builder] finished compiling extension /Users/enrikes/Documents/BrazilConfigZedExtension [2024-12-24T16:44:05+01:00 ERROR extensions_ui] No such file or directory (os error 2) Stack backtrace: 0: std::backtrace_rs::backtrace::libunwind::trace at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/../../backtrace/src/backtrace/libunwind.rs:116:5 1: std::backtrace_rs::backtrace::trace_unsynchronized at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5 2: std::backtrace::Backtrace::create at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/backtrace.rs:331:13 3: anyhow::error::<impl core::convert::From<E> for anyhow::Error>::from at /Users/enrikes/.cargo/registry/src/index.crates.io-6f17d22bba15001f/anyhow-1.0.94/src/backtrace.rs:27:14 4: <core::result::Result<T,F> as core::ops::try_trait::FromResidual<core::result::Result<core::convert::Infallible,E>>>::from_residual at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/result.rs:1989:27 5: <fs::RealFs as fs::Fs>::metadata::{{closure}} at ./crates/fs/src/fs.rs:603:13 ``` After the patch, the extension is installed and the symlink replaced for a new one pointing to the user's directory choice. ``` 2024-12-24T16:53:33.916022+01:00 [INFO] compiled Rust extension /Users/enrikes/Documents/BrazilConfigZedExtension 2024-12-24T16:53:33.916094+01:00 [INFO] compiling grammar brazil_config for extension /Users/enrikes/Documents/BrazilConfigZedExtension 2024-12-24T16:53:33.916225+01:00 [INFO] checking out brazil_config parser 2024-12-24T16:53:35.481602+01:00 [INFO] compiling brazil_config parser 2024-12-24T16:53:35.964189+01:00 [INFO] compiled grammar brazil_config for extension /Users/enrikes/Documents/BrazilConfigZedExtension 2024-12-24T16:53:35.964319+01:00 [INFO] finished compiling extension /Users/enrikes/Documents/BrazilConfigZedExtension 2024-12-24T16:53:36.213608+01:00 [INFO] rebuilt extension index in 39.108542ms 2024-12-24T16:53:36.213835+01:00 [INFO] extensions updated. loading 0, reloading 1, unloading 0 2024-12-24T16:53:36.375928+01:00 [INFO] rebuilt extension index in 34.478167ms 2024-12-24T16:53:36.376054+01:00 [INFO] extensions updated. loading 0, reloading 1, unloading 0 ``` and ``` ❯ ll lrwxr-xr-x@ - enrikes 24 Dec 16:53 brazil-config-zed-extension -> /Users/enrikes/Documents/BrazilConfigZedExtension drwxr-xr-x@ - enrikes 5 Dec 14:48 java drwxr-xr-x@ - enrikes 12 Dec 13:04 kotlin drwxr-xr-x@ - enrikes 25 Oct 08:13 rose-pine-theme ``` Release Notes: - Fix broken symlinks when installing dev extensions --------- Co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
parent
75c5344754
commit
8c215d43ad
1 changed files with 13 additions and 5 deletions
|
@ -4,7 +4,7 @@ mod mac_watcher;
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
pub mod fs_watcher;
|
pub mod fs_watcher;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
use git::status::FileStatus;
|
use git::status::FileStatus;
|
||||||
use git::GitHostingProviderRegistry;
|
use git::GitHostingProviderRegistry;
|
||||||
|
@ -589,11 +589,19 @@ impl Fs for RealFs {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let path_buf = path.to_path_buf();
|
||||||
|
let path_exists = smol::unblock(move || {
|
||||||
|
path_buf
|
||||||
|
.try_exists()
|
||||||
|
.with_context(|| format!("checking existence for path {path_buf:?}"))
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
let is_symlink = symlink_metadata.file_type().is_symlink();
|
let is_symlink = symlink_metadata.file_type().is_symlink();
|
||||||
let metadata = if is_symlink {
|
let metadata = match (is_symlink, path_exists) {
|
||||||
smol::fs::metadata(path).await?
|
(true, true) => smol::fs::metadata(path)
|
||||||
} else {
|
.await
|
||||||
symlink_metadata
|
.with_context(|| "accessing symlink for path {path}")?,
|
||||||
|
_ => symlink_metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue