diff options
author | Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> | 2024-08-08 09:19:05 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-08 00:19:05 -0700 |
commit | 507e5b74ff21161ba8bd947d7d9cee317c0af379 (patch) | |
tree | 8cfd58d46034803cd296d7b3a159c3f84896ec88 /cli/util | |
parent | 4e4c96bf66111c6e8ba976ed24594edf7abfcbfb (diff) |
fix: Don't shell out to `unzip` in deno upgrade/compile (#24926)
Use the `zip` crate instead
Fixes #23988.
Diffstat (limited to 'cli/util')
-rw-r--r-- | cli/util/archive.rs | 118 | ||||
-rw-r--r-- | cli/util/mod.rs | 1 |
2 files changed, 119 insertions, 0 deletions
diff --git a/cli/util/archive.rs b/cli/util/archive.rs new file mode 100644 index 000000000..e2183d57a --- /dev/null +++ b/cli/util/archive.rs @@ -0,0 +1,118 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use std::fs; +use std::path::Path; +use std::path::PathBuf; +use std::process::Command; + +use deno_core::anyhow::bail; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; + +fn unzip_with_shell( + archive_path: &Path, + archive_data: &[u8], + dest_path: &Path, +) -> Result<(), AnyError> { + fs::write(archive_path, archive_data)?; + let unpack_status = if cfg!(windows) { + Command::new("tar.exe") + .arg("xf") + .arg(archive_path) + .arg("-C") + .arg(dest_path) + .spawn() + .map_err(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + std::io::Error::new( + std::io::ErrorKind::NotFound, + "`tar.exe` was not found in your PATH", + ) + } else { + err + } + })? + .wait()? + } else { + Command::new("unzip") + .current_dir(dest_path) + .arg(archive_path) + .spawn() + .map_err(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + std::io::Error::new( + std::io::ErrorKind::NotFound, + "`unzip` was not found in your PATH, please install `unzip`", + ) + } else { + err + } + })? + .wait()? + }; + + if !unpack_status.success() { + bail!("Failed to unpack archive."); + } + + Ok(()) +} + +fn unzip( + archive_name: &str, + archive_data: &[u8], + dest_path: &Path, +) -> Result<(), AnyError> { + let mut archive = zip::ZipArchive::new(std::io::Cursor::new(archive_data))?; + archive + .extract(dest_path) + .with_context(|| format!("failed to extract archive: {archive_name}"))?; + + Ok(()) +} + +pub struct UnpackArgs<'a> { + pub exe_name: &'a str, + pub archive_name: &'a str, + pub archive_data: &'a [u8], + pub is_windows: bool, + pub dest_path: &'a Path, +} + +pub fn unpack_into_dir(args: UnpackArgs) -> Result<PathBuf, AnyError> { + let UnpackArgs { + exe_name, + archive_name, + archive_data, + is_windows, + dest_path, + } = args; + let exe_ext = if is_windows { "exe" } else { "" }; + let archive_path = dest_path.join(exe_name).with_extension("zip"); + let exe_path = dest_path.join(exe_name).with_extension(exe_ext); + assert!(!exe_path.exists()); + + let archive_ext = Path::new(archive_name) + .extension() + .and_then(|ext| ext.to_str()) + .unwrap(); + match archive_ext { + "zip" => match unzip(archive_name, archive_data, dest_path) { + Ok(()) if !exe_path.exists() => { + log::warn!("unpacking via the zip crate didn't produce the executable"); + // No error but didn't produce exe, fallback to shelling out + unzip_with_shell(&archive_path, archive_data, dest_path)?; + } + Ok(_) => {} + Err(e) => { + log::warn!("unpacking via zip crate failed: {e}"); + // Fallback to shelling out + unzip_with_shell(&archive_path, archive_data, dest_path)?; + } + }, + ext => bail!("Unsupported archive type: '{ext}'"), + } + + assert!(exe_path.exists()); + Ok(exe_path) +} diff --git a/cli/util/mod.rs b/cli/util/mod.rs index b7eef95d3..b9071c496 100644 --- a/cli/util/mod.rs +++ b/cli/util/mod.rs @@ -1,6 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Note: Only add code in this folder that has no application specific logic +pub mod archive; pub mod checksum; pub mod console; pub mod diff; |