diff --git a/.github/workflows/test-sys.yaml b/.github/workflows/test-sys.yaml index 9007ee55cc3..f440ede6fda 100644 --- a/.github/workflows/test-sys.yaml +++ b/.github/workflows/test-sys.yaml @@ -213,6 +213,19 @@ jobs: CARGO_TARGET: --target ${{ matrix.target }} WAPM_DEV_TOKEN: ${{ secrets.WAPM_DEV_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Test integration CLI + if: matrix.run_test && matrix.os == 'windows-2019' + shell: bash + run: | + make build-wasmer && + cargo test --package wasmer-integration-tests-cli --test run -- test_wasmer_run_complex_url --exact --nocapture + env: + TARGET: ${{ matrix.target }} + TARGET_DIR: target/${{ matrix.target }}/release + CARGO_TARGET: --target x86_64-pc-windows-msvc + WAPM_DEV_TOKEN: ${{ secrets.WAPM_DEV_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # cargo test --package wasmer-integration-tests-cli --test run -- test_wasmer_run_complex_url --exact --nocapture #- name: Test integration CLI # if: matrix.run_test && matrix.os == 'windows-2019' # shell: bash diff --git a/Cargo.lock b/Cargo.lock index cc962bfc7cb..d56101e4d82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,12 +46,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30275ad0ad84ec1c06dde3b3f7d23c6006b7d76d61a85e7060b426b747eff70d" - [[package]] name = "any_ascii" version = "0.1.7" @@ -878,16 +872,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" -dependencies = [ - "cfg-if 0.1.10", - "dirs-sys", -] - [[package]] name = "dirs" version = "4.0.0" @@ -1879,6 +1863,12 @@ dependencies = [ "syn", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matchers" version = "0.1.0" @@ -2309,7 +2299,7 @@ dependencies = [ "csv", "encode_unicode 1.0.0", "lazy_static", - "term 0.7.0", + "term", "unicode-width", ] @@ -3098,13 +3088,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] -name = "spinner" -version = "0.5.0" +name = "spinoff" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e3a7cd01625b7e43e62815677d692cb59b221c2fdc2853d1eb86a260ee0c272" +checksum = "812db6f40551bdcdb10e1d2070ec33f69805d2bfb7e59426c7d14e7e1b4194dd" dependencies = [ - "ansi_term", - "term 0.6.1", + "colored 2.0.0", + "maplit", + "once_cell", + "strum", ] [[package]] @@ -3177,6 +3169,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck 0.4.0", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "subtle" version = "2.4.1" @@ -3241,16 +3255,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "term" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" -dependencies = [ - "dirs 2.0.2", - "winapi", -] - [[package]] name = "term" version = "0.7.0" @@ -3316,7 +3320,7 @@ dependencies = [ "getopts", "libc", "num_cpus", - "term 0.7.0", + "term", ] [[package]] @@ -4019,7 +4023,7 @@ dependencies = [ "clap 3.2.23", "colored 2.0.0", "dialoguer", - "dirs 4.0.0", + "dirs", "distance", "fern", "http_req", @@ -4032,7 +4036,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "spinner", + "spinoff", "target-lexicon 0.12.5", "tempdir", "tempfile", @@ -4260,7 +4264,7 @@ name = "wasmer-registry" version = "3.0.1" dependencies = [ "anyhow", - "dirs 4.0.0", + "dirs", "flate2", "futures-util", "graphql_client", diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index de1c77c51bf..7f3054d4936 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -43,7 +43,7 @@ wasmer-vfs = { version = "=3.0.1", path = "../vfs", default-features = false, f atty = "0.2" colored = "2.0" anyhow = "1.0" -spinner = "0.5.0" +spinoff = "0.5.4" clap = { version = "3.2.22", features = ["derive", "env"] } # For the function names autosuggestion distance = "0.4" @@ -166,6 +166,12 @@ http = [ "serde", ] +[target.'cfg(target_os = "windows")'.dependencies] +colored = "2.0.0" + +[package.metadata.binstall] +pkg-fmt = "tgz" + [package.metadata.binstall.overrides.aarch64-apple-darwin] pkg-url = "{ repo }/releases/download/v{ version }/wasmer-darwin-arm64.{ archive-format }" bin-dir = "bin/{ bin }" diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 52c597287ea..994ac02ecb2 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -645,17 +645,20 @@ impl Run { } } -fn start_spinner(msg: String) -> Option { +fn start_spinner(msg: String) -> Option { if !isatty::stdout_isatty() { return None; } - Some( - spinner::SpinnerBuilder::new(msg) - .spinner(vec![ - "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈", - ]) - .start(), - ) + #[cfg(target_os = "windows")] + { + use colored::control; + let _ = control::set_virtual_terminal(true); + } + Some(spinoff::Spinner::new( + spinoff::Spinners::Dots, + msg, + spinoff::Color::White, + )) } /// Before looking up a command from the registry, try to see if we have @@ -706,8 +709,7 @@ pub(crate) fn try_autoinstall_package( force_install, ); if let Some(sp) = sp.take() { - sp.close(); - print!("\r"); + sp.clear(); } let _ = std::io::stdout().flush(); let (_, package_dir) = match result { @@ -765,8 +767,8 @@ fn try_lookup_command(sv: &mut SplitVersion) -> Result Result Result<(), anyhow::Error> { let debug_msgs_allowed = isatty::stdout_isatty(); - if let Ok(url) = url::Url::parse(&format!("{}", r.path.display())) { - let result = try_run_url(&url, args, r, debug); - return result; - } - // Check "r.path" is a file or a package / command name if r.path.exists() { if r.path.is_dir() && r.path.join("wapm.toml").exists() { @@ -848,6 +844,18 @@ pub(crate) fn try_run_package_or_file( return r.execute(); } + // c:// might be parsed as a URL on Windows + let url_string = format!("{}", r.path.display()); + if let Ok(url) = url::Url::parse(&url_string) { + if url.scheme() == "http" || url.scheme() == "https" { + match try_run_url(&url, args, r, debug) { + Err(ExecuteLocalPackageError::BeforeExec(_)) => {} + Err(ExecuteLocalPackageError::DuringExec(e)) => return Err(e), + Ok(o) => return Ok(o), + } + } + } + let package = format!("{}", r.path.display()); let mut is_fake_sv = false; @@ -915,9 +923,15 @@ pub(crate) fn try_run_package_or_file( try_autoinstall_package(args, &sv, package_download_info, r.force_install) } -fn try_run_url(url: &Url, _args: &[String], r: &Run, _debug: bool) -> Result<(), anyhow::Error> { - let checksum = wasmer_registry::get_remote_webc_checksum(url) - .map_err(|e| anyhow::anyhow!("error fetching {url}: {e}"))?; +fn try_run_url( + url: &Url, + _args: &[String], + r: &Run, + _debug: bool, +) -> Result<(), ExecuteLocalPackageError> { + let checksum = wasmer_registry::get_remote_webc_checksum(url).map_err(|e| { + ExecuteLocalPackageError::BeforeExec(anyhow::anyhow!("error fetching {url}: {e}")) + })?; let packages = wasmer_registry::get_all_installed_webc_packages(); @@ -926,20 +940,23 @@ fn try_run_url(url: &Url, _args: &[String], r: &Run, _debug: bool) -> Result<(), let result = wasmer_registry::install_webc_package(url, &checksum); - result.map_err(|e| anyhow::anyhow!("error fetching {url}: {e}"))?; + result.map_err(|e| { + ExecuteLocalPackageError::BeforeExec(anyhow::anyhow!("error fetching {url}: {e}")) + })?; if let Some(sp) = sp { - sp.close(); + sp.clear(); } } let webc_dir = wasmer_registry::get_webc_dir(); let webc_install_path = webc_dir - .context("Error installing package: no webc dir")? + .context("Error installing package: no webc dir") + .map_err(ExecuteLocalPackageError::BeforeExec)? .join(checksum); let mut r = r.clone(); r.path = webc_install_path; - r.execute() + r.execute().map_err(ExecuteLocalPackageError::DuringExec) } diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 4da04f8f851..5d27986710f 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -593,7 +593,7 @@ pub fn query_package_from_registry( let v = response.package_version.as_ref().ok_or_else(|| { QueryPackageError::ErrorSendingQuery(format!( - "Invalid response for crate {name:?}: no manifest" + "Invalid response for crate {name:?}: no package version: {response:#?}" )) })?; @@ -1136,7 +1136,8 @@ pub fn get_checksum_hash(bytes: &[u8]) -> String { /// file is already installed before downloading it pub fn get_remote_webc_checksum(url: &Url) -> Result { let request_max_bytes = webc::WebC::get_signature_offset_start() + 4 + 1024 + 8 + 8; - let data = get_webc_bytes(url, Some(0..request_max_bytes)).context("get_webc_bytes failed")?; + let data = get_webc_bytes(url, Some(0..request_max_bytes)) + .with_context(|| format!("get_webc_bytes failed on {url}"))?; let checksum = webc::WebC::get_checksum_bytes(&data) .map_err(|e| anyhow::anyhow!("{e}")) .context("get_checksum_bytes failed")? diff --git a/tests/integration/cli/Cargo.toml b/tests/integration/cli/Cargo.toml index ed384c5a8c6..9f9beedf6d5 100644 --- a/tests/integration/cli/Cargo.toml +++ b/tests/integration/cli/Cargo.toml @@ -16,6 +16,7 @@ target-lexicon = "0.12.4" [dependencies] anyhow = "1" tempfile = "3" +target-lexicon = "0.12.5" [features] default = ["webc_runner"] diff --git a/tests/integration/cli/src/assets.rs b/tests/integration/cli/src/assets.rs index 53f15bd7997..b341bbb69c1 100644 --- a/tests/integration/cli/src/assets.rs +++ b/tests/integration/cli/src/assets.rs @@ -63,21 +63,58 @@ pub fn get_wasmer_path() -> PathBuf { ret = PathBuf::from(format!("{}wasmer", WASMER_TARGET_PATH2)); } if !ret.exists() { - match get_repo_root_path() { + ret = match get_repo_root_path() { Some(s) => { #[cfg(target_os = "windows")] { - return s.join("target").join("release").join("wasmer.exe"); + s.join("target").join("release").join("wasmer.exe") } #[cfg(not(target_os = "windows"))] { - return s.join("target").join("release").join("wasmer"); + s.join("target").join("release").join("wasmer") } } None => { panic!("Could not find wasmer executable path! {:?}", ret); } + }; + } + + if !ret.exists() { + ret = match get_repo_root_path() { + Some(s) => { + #[cfg(target_os = "windows")] + { + s.join("target") + .join(target_lexicon::HOST.to_string()) + .join("release") + .join("wasmer.exe") + } + #[cfg(not(target_os = "windows"))] + { + s.join("target") + .join(target_lexicon::HOST.to_string()) + .join("release") + .join("wasmer") + } + } + None => { + panic!("Could not find wasmer executable path! {:?}", ret); + } + }; + } + + if !ret.exists() { + if let Some(root) = get_repo_root_path() { + use std::process::Stdio; + let _ = std::process::Command::new("ls") + .arg(root.join("target")) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .stdin(Stdio::null()) + .output(); } + panic!("cannot find wasmer / wasmer.exe for integration test!"); } ret } diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index 2de8a173956..8146e55789b 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -1,24 +1,24 @@ //! Basic tests for the `run` subcommand -use anyhow::bail; -use std::path::PathBuf; +use anyhow::{bail, Context}; +use std::path::{Path, PathBuf}; use std::process::Command; use wasmer_integration_tests_cli::{get_repo_root_path, get_wasmer_path, ASSET_PATH, C_ASSET_PATH}; -fn wasi_test_python_path() -> String { - format!("{}/{}", C_ASSET_PATH, "python-0.1.0.wasmer") +fn wasi_test_python_path() -> PathBuf { + Path::new(C_ASSET_PATH).join("python-0.1.0.wasmer") } -fn wasi_test_wasm_path() -> String { - format!("{}/{}", C_ASSET_PATH, "qjs.wasm") +fn wasi_test_wasm_path() -> PathBuf { + Path::new(C_ASSET_PATH).join("qjs.wasm") } -fn test_no_imports_wat_path() -> String { - format!("{}/{}", ASSET_PATH, "fib.wat") +fn test_no_imports_wat_path() -> PathBuf { + Path::new(ASSET_PATH).join("fib.wat") } -fn test_no_start_wat_path() -> String { - format!("{}/{}", ASSET_PATH, "no_start.wat") +fn test_no_start_wat_path() -> PathBuf { + Path::new(ASSET_PATH).join("no_start.wat") } #[cfg(any(target_os = "linux", target_os = "macos"))] @@ -192,11 +192,7 @@ fn test_wasmer_create_exe_pirita_works() -> anyhow::Result<()> { "packaging /package to .tar.gz: {}", tmp_targz_path.display() ); - package_directory( - &package_path, - &std::path::Path::new("./out.tar.gz").to_path_buf(), - ); - std::fs::copy("./out.tar.gz", &tmp_targz_path).unwrap(); + package_directory(&package_path, &tmp_targz_path); println!("packaging done"); println!( "tmp tar gz path: {} - exists: {:?}", @@ -546,3 +542,52 @@ fn run_no_start_wasm_report_error() -> anyhow::Result<()> { assert_eq!(result.contains("Can not find any export functions."), true); Ok(()) } + +// Test that wasmer can run a complex path +#[test] +fn test_wasmer_run_complex_url() -> anyhow::Result<()> { + let wasm_test_path = wasi_test_wasm_path(); + let wasm_test_path = wasm_test_path.canonicalize().unwrap_or(wasm_test_path); + let mut wasm_test_path = format!("{}", wasm_test_path.display()); + if wasm_test_path.starts_with(r#"\\?\"#) { + wasm_test_path = wasm_test_path.replacen(r#"\\?\"#, "", 1); + } + #[cfg(target_os = "windows")] + { + wasm_test_path = wasm_test_path.replace("D:\\", "D://"); + wasm_test_path = wasm_test_path.replace("C:\\", "C://"); + wasm_test_path = wasm_test_path.replace("c:\\", "c://"); + wasm_test_path = wasm_test_path.replace("\\", "/"); + // wasmer run used to fail on c:\Users\username\wapm_packages\ ... + assert!( + wasm_test_path.contains("://"), + "wasm_test_path path is not complex enough" + ); + } + + let mut cmd = Command::new(get_wasmer_path()); + cmd.arg("run"); + cmd.arg(wasm_test_path); + cmd.arg("--"); + cmd.arg("-q"); + + let cmd_str = format!("{cmd:?}"); + let output = cmd.output().with_context(|| { + anyhow::anyhow!( + "failed to run {cmd_str} with {}", + get_wasmer_path().display() + ) + })?; + + if !output.status.success() { + bail!( + "wasmer run qjs.wasm failed with: stdout: {}\n\nstderr: {}", + std::str::from_utf8(&output.stdout) + .expect("stdout is not utf8! need to handle arbitrary bytes"), + std::str::from_utf8(&output.stderr) + .expect("stderr is not utf8! need to handle arbitrary bytes") + ); + } + + Ok(()) +}