From 4976a9e9d8efeb6c548c6a92258345381e6e7e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=B0=8F=E7=99=BD?= <364772080@qq.com> Date: Sat, 27 Jul 2024 22:24:05 +0800 Subject: [PATCH] windows: Fix `eslint` installation (#15331) Close #13786. To make `eslint` running on Windows, I made the following changes: 1. Ensure that `zed` downloads the `.zip` file. 2. Handle the `$shared` symbolic link by copying files to the link location. 3. In #13891, I mentioned that the `npm` `post-install` script was always failing. After debugging, I found it was due to missing environment variables. This has been fixed, and I will submit a new PR to address the changes in #13891. With this PR, `eslint` can now successfully run on Windows. Video: https://github.com/user-attachments/assets/e85451b8-0388-490a-8a75-01c12d744f7c Release Notes: - Fixed `eslint` not running on Windows ([#13786](https://github.com/zed-industries/zed/issues/13786)). --------- Co-authored-by: Marshall Bowers --- crates/http_client/src/github.rs | 8 +++++ crates/languages/src/typescript.rs | 44 +++++++++++++++++++++++++ crates/node_runtime/src/node_runtime.rs | 7 ++++ 3 files changed, 59 insertions(+) diff --git a/crates/http_client/src/github.rs b/crates/http_client/src/github.rs index a3293ed239..a64a5bae5c 100644 --- a/crates/http_client/src/github.rs +++ b/crates/http_client/src/github.rs @@ -120,6 +120,7 @@ pub async fn get_release_by_tag_name( #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum AssetKind { TarGz, + Zip, } pub fn build_asset_url(repo_name_with_owner: &str, tag: &str, kind: AssetKind) -> Result { @@ -132,6 +133,7 @@ pub fn build_asset_url(repo_name_with_owner: &str, tag: &str, kind: AssetKind) - "{tag}.{extension}", extension = match kind { AssetKind::TarGz => "tar.gz", + AssetKind::Zip => "zip", } ); url.path_segments_mut() @@ -154,5 +156,11 @@ mod tests { tarball, "https://github.com/microsoft/vscode-eslint/archive/refs/tags/release%2F2.3.5.tar.gz" ); + + let zip = build_asset_url(repo_name_with_owner, tag, AssetKind::Zip).unwrap(); + assert_eq!( + zip, + "https://github.com/microsoft/vscode-eslint/archive/refs/tags/release%2F2.3.5.zip" + ); } } diff --git a/crates/languages/src/typescript.rs b/crates/languages/src/typescript.rs index 46c6a75b84..75649cfab8 100644 --- a/crates/languages/src/typescript.rs +++ b/crates/languages/src/typescript.rs @@ -296,7 +296,11 @@ pub struct EsLintLspAdapter { impl EsLintLspAdapter { const CURRENT_VERSION: &'static str = "release/2.4.4"; + + #[cfg(not(windows))] const GITHUB_ASSET_KIND: AssetKind = AssetKind::TarGz; + #[cfg(windows)] + const GITHUB_ASSET_KIND: AssetKind = AssetKind::Zip; const SERVER_PATH: &'static str = "vscode-eslint/server/out/eslintServer.js"; const SERVER_NAME: &'static str = "eslint"; @@ -443,6 +447,13 @@ impl LspAdapter for EsLintLspAdapter { let archive = Archive::new(decompressed_bytes); archive.unpack(&destination_path).await?; } + AssetKind::Zip => { + node_runtime::extract_zip( + &destination_path, + BufReader::new(response.body_mut()), + ) + .await?; + } } let mut dir = fs::read_dir(&destination_path).await?; @@ -450,6 +461,20 @@ impl LspAdapter for EsLintLspAdapter { let repo_root = destination_path.join("vscode-eslint"); fs::rename(first.path(), &repo_root).await?; + #[cfg(target_os = "windows")] + { + handle_symlink( + repo_root.join("$shared"), + repo_root.join("client").join("src").join("shared"), + ) + .await?; + handle_symlink( + repo_root.join("$shared"), + repo_root.join("server").join("src").join("shared"), + ) + .await?; + } + self.node .run_npm_subcommand(Some(&repo_root), "install", &[]) .await?; @@ -505,6 +530,25 @@ async fn get_cached_eslint_server_binary( .log_err() } +#[cfg(target_os = "windows")] +async fn handle_symlink(src_dir: PathBuf, dest_dir: PathBuf) -> Result<()> { + if fs::metadata(&src_dir).await.is_err() { + return Err(anyhow!("Directory {} not present.", src_dir.display())); + } + if fs::metadata(&dest_dir).await.is_ok() { + fs::remove_file(&dest_dir).await?; + } + fs::create_dir_all(&dest_dir).await?; + let mut entries = fs::read_dir(&src_dir).await?; + while let Some(entry) = entries.try_next().await? { + let entry_path = entry.path(); + let entry_name = entry.file_name(); + let dest_path = dest_dir.join(&entry_name); + fs::copy(&entry_path, &dest_path).await?; + } + Ok(()) +} + #[cfg(test)] mod tests { use gpui::{Context, TestAppContext}; diff --git a/crates/node_runtime/src/node_runtime.rs b/crates/node_runtime/src/node_runtime.rs index 58c3f8f673..cc4bacc54d 100644 --- a/crates/node_runtime/src/node_runtime.rs +++ b/crates/node_runtime/src/node_runtime.rs @@ -290,6 +290,13 @@ impl NodeRuntime for RealNodeRuntime { { command.env("SYSTEMROOT", val); } + // Without ComSpec, the post-install will always fail. + if let Some(val) = std::env::var("ComSpec") + .context("Missing environment variable: ComSpec!") + .log_err() + { + command.env("ComSpec", val); + } command.creation_flags(windows::Win32::System::Threading::CREATE_NO_WINDOW.0); }