From d12488e14fd3c93ff338f4e9edc3f4c1ece665d4 Mon Sep 17 00:00:00 2001 From: Siddharth Date: Tue, 12 Mar 2024 08:50:14 +0000 Subject: [PATCH 01/10] chore: updates powershell to power-shell as per the CLI app (#1095) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a55088..f0349a6 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ Where `` can be one of the supported shells: - `bash` - `zsh` - `fish` -- `powershell` +- `power-shell` Please follow your shell instructions to install them. From a5a497b7e2c4cc615fedf366f9eeab4e322443ed Mon Sep 17 00:00:00 2001 From: Megamannen Date: Tue, 12 Mar 2024 09:50:33 +0100 Subject: [PATCH 02/10] README.md - Simplified Windows PowerShell setup (#1047) * Update README.md docs: replaced section about profile location on Windows In now uses the built in env variable $profile * Update README.md style: styled section about windows profile --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f0349a6..1f16113 100644 --- a/README.md +++ b/README.md @@ -174,10 +174,10 @@ fnm env --use-on-cd | Out-String | Invoke-Expression ``` - For macOS/Linux, the profile is located at `~/.config/powershell/Microsoft.PowerShell_profile.ps1` -- On Windows, PowerShell comes pre-installed, but there are two versions of it. [Read more about it here](https://learn.microsoft.com/en-us/powershell/scripting/windows-powershell/install/installing-windows-powershell). The profile is located at different places depending on which version you're using: - - Built in PowerShell (aka "Windows PowerShell"): `~\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1` - - The newer, PowerShell >= 7, that's not built in: `~\Documents\PowerShell\Microsoft.PowerShell_profile.ps1` - +- On Windows to edit your profile you can run this in a PowerShell + ```powershell + notepad $profile + ``` #### Windows Command Prompt aka Batch aka WinCMD fnm is also supported but is not entirely covered. [You can set up a startup script](https://superuser.com/a/144348) and append the following line: From 990f3d06cf81091d511ecfc67f23842c2b82ca5f Mon Sep 17 00:00:00 2001 From: Ulf Kamp <49397600+ukamp@users.noreply.github.com> Date: Fri, 24 May 2024 07:21:38 +0200 Subject: [PATCH 03/10] Updated README.md => Fixed infite Loop from Windows console startup script (#1124) This startup script does not produce the error "i was unexpected at this time" and also won't freeze the console because of an infite loop => "for /F" will launch a new instance of cmd. --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1f16113..558bedf 100644 --- a/README.md +++ b/README.md @@ -178,16 +178,20 @@ fnm env --use-on-cd | Out-String | Invoke-Expression ```powershell notepad $profile ``` + #### Windows Command Prompt aka Batch aka WinCMD -fnm is also supported but is not entirely covered. [You can set up a startup script](https://superuser.com/a/144348) and append the following line: +fnm is also supported but is not entirely covered. [You can set up a startup script](https://superuser.com/a/144348) and append the following lines: ```batch -FOR /f "tokens=*" %i IN ('fnm env --use-on-cd') DO CALL %i +@echo off +:: for /F will launch a new instance of cmd so we create a guard to prevent an infnite loop +if not defined FNM_AUTORUN_GUARD ( + set "FNM_AUTORUN_GUARD=AutorunGuard" + FOR /f "tokens=*" %%z IN ('fnm env --use-on-cd') DO CALL %%z +) ``` -⚠️ If you get the error `i was unexpected at this time`, please make a .cmd file as suggested by the first step in the Usage with Cmder secton add it's path to the `AutoRun` registry key. - #### Usage with Cmder Usage is very similar to the normal WinCMD install, apart for a few tweaks to allow being called from the cmder startup script. The example **assumes** that the `CMDER_ROOT` environment variable is **set** to the **root directory** of your Cmder installation. From 463cc90007b26490180b38fea921fd045e377de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=B1=AA?= <504595380@qq.com> Date: Fri, 24 May 2024 13:24:20 +0800 Subject: [PATCH 04/10] fix(install.sh): support windows mingw platform (#1112) --- .ci/install.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.ci/install.sh b/.ci/install.sh index 1494da5..e86ae7a 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -5,6 +5,10 @@ set -e RELEASE="latest" OS="$(uname -s)" +case "${OS}" in + MINGW* | Win*) OS="Windows" ;; +esac + if [ -d "$HOME/.fnm" ]; then INSTALL_DIR="$HOME/.fnm" elif [ -n "$XDG_DATA_HOME" ]; then @@ -70,6 +74,9 @@ set_filename() { elif [ "$OS" = "Darwin" ]; then USE_HOMEBREW="true" echo "Downloading fnm using Homebrew..." + elif [ "$OS" = "Windows" ] ; then + FILENAME="fnm-windows" + echo "Downloading the latest fnm binary from GitHub..." else echo "OS $OS is not supported." echo "If you think that's a bug - please file an issue to https://github.com/Schniz/fnm/issues" @@ -180,6 +187,7 @@ setup_shell() { echo ' set PATH "'"$INSTALL_DIR"'" $PATH' echo ' fnm env | source' + echo '' >>$CONF_FILE echo '# fnm' >>$CONF_FILE echo 'set PATH "'"$INSTALL_DIR"'" $PATH' >>$CONF_FILE echo 'fnm env | source' >>$CONF_FILE From 8f3acbb8ee4991b838ce3fe146d62864aaa290b9 Mon Sep 17 00:00:00 2001 From: Vinfall <91039000+Vinfall@users.noreply.github.com> Date: Fri, 24 May 2024 05:24:40 +0000 Subject: [PATCH 05/10] fix: support x64-musl (#1109) * fix: support x64-musl Yes, an easy fix as silly as bypassing the hardcoded check and then set `FNM_NODE_DIST_MIRROR` & `FNM_ARCH` in shell rc. * Create poor-otters-cheer.md --------- Co-authored-by: Gal Schlezinger --- .changeset/poor-otters-cheer.md | 5 +++++ src/arch.rs | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 .changeset/poor-otters-cheer.md diff --git a/.changeset/poor-otters-cheer.md b/.changeset/poor-otters-cheer.md new file mode 100644 index 0000000..86d6aca --- /dev/null +++ b/.changeset/poor-otters-cheer.md @@ -0,0 +1,5 @@ +--- +"fnm": patch +--- + +support `x64-musl` arch by adding a `--arch x64-musl` to fnm env diff --git a/src/arch.rs b/src/arch.rs index 88985a6..080552c 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -4,6 +4,7 @@ use crate::version::Version; pub enum Arch { X86, X64, + X64Musl, Arm64, Armv7l, Ppc64le, @@ -43,6 +44,7 @@ impl std::str::FromStr for Arch { match s { "x86" => Ok(Arch::X86), "x64" => Ok(Arch::X64), + "x64-musl" => Ok(Arch::X64Musl), "arm64" => Ok(Arch::Arm64), "armv7l" => Ok(Arch::Armv7l), "ppc64le" => Ok(Arch::Ppc64le), @@ -58,6 +60,7 @@ impl std::fmt::Display for Arch { let arch_str = match self { Arch::X86 => String::from("x86"), Arch::X64 => String::from("x64"), + Arch::X64Musl => String::from("x64-musl"), Arch::Arm64 => String::from("arm64"), Arch::Armv7l => String::from("armv7l"), Arch::Ppc64le => String::from("ppc64le"), From 66efc5b90c71f2f24592dbf8d7e28c9f53bcf5f9 Mon Sep 17 00:00:00 2001 From: eblocha <55192399+eblocha@users.noreply.github.com> Date: Sat, 25 May 2024 14:10:52 -0400 Subject: [PATCH 06/10] Add progress bar to install command (#1028) --- .changeset/show-download-progress.md | 5 + Cargo.lock | 74 +++++++++++++- Cargo.toml | 2 + docs/commands.md | 3 + src/commands/install.rs | 13 +++ src/downloader.rs | 21 ++-- src/main.rs | 1 + src/progress.rs | 147 +++++++++++++++++++++++++++ 8 files changed, 258 insertions(+), 8 deletions(-) create mode 100644 .changeset/show-download-progress.md create mode 100644 src/progress.rs diff --git a/.changeset/show-download-progress.md b/.changeset/show-download-progress.md new file mode 100644 index 0000000..552f423 --- /dev/null +++ b/.changeset/show-download-progress.md @@ -0,0 +1,5 @@ +--- +"fnm": minor +--- + +Show a progress bar when downloading and extracting node diff --git a/Cargo.lock b/Cargo.lock index c8ad86e..fbc969f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -357,6 +357,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.45.0", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -540,6 +553,12 @@ dependencies = [ "winreg", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.32" @@ -638,6 +657,8 @@ dependencies = [ "embed-resource", "encoding_rs_io", "env_logger", + "http", + "indicatif", "indoc", "junction", "log", @@ -912,6 +933,19 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indicatif" +version = "0.17.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b297dc40733f23a0e52728a58fa9489a5b7638a324932de16b41adc3ef80730" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + [[package]] name = "indoc" version = "2.0.2" @@ -1164,6 +1198,12 @@ dependencies = [ "libc", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "object" version = "0.30.4" @@ -1248,6 +1288,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "portable-atomic" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" + [[package]] name = "pretty_assertions" version = "1.4.0" @@ -2112,7 +2158,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", ] [[package]] @@ -2130,13 +2176,37 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b9c3a13..f6f9326 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,11 +30,13 @@ sysinfo = "0.29.3" thiserror = "1.0.44" clap_complete = "4.3.1" anyhow = "1.0.71" +indicatif = "0.17.6" [dev-dependencies] pretty_assertions = "1.4.0" duct = "0.13.6" test-log = "0.2.12" +http = "0.2.9" [build-dependencies] embed-resource = "1.8.0" diff --git a/docs/commands.md b/docs/commands.md index 00a98d3..d3072aa 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -216,6 +216,9 @@ Options: --latest Install latest version + --no-progress + Do not display a progress bar + --log-level The log level of fnm commands diff --git a/src/commands/install.rs b/src/commands/install.rs index 2c55384..ed20fe9 100644 --- a/src/commands/install.rs +++ b/src/commands/install.rs @@ -3,6 +3,7 @@ use crate::alias::create_alias; use crate::arch::get_safe_arch; use crate::config::FnmConfig; use crate::downloader::{install_node_dist, Error as DownloaderError}; +use crate::log_level::LogLevel; use crate::lts::LtsType; use crate::outln; use crate::remote_node_index; @@ -25,6 +26,10 @@ pub struct Install { /// Install latest version #[clap(long, conflicts_with_all = &["version", "lts"])] pub latest: bool, + + /// Do not display a progress bar + #[clap(long)] + pub no_progress: bool, } impl Install { @@ -34,16 +39,19 @@ impl Install { version: v, lts: false, latest: false, + no_progress: _, } => Ok(v), Self { version: None, lts: true, latest: false, + no_progress: _, } => Ok(Some(UserVersion::Full(Version::Lts(LtsType::Latest)))), Self { version: None, lts: false, latest: true, + no_progress: _, } => Ok(Some(UserVersion::Full(Version::Latest))), _ => Err(Error::TooManyVersionsProvided), } @@ -56,6 +64,8 @@ impl Command for Install { fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { let current_dir = std::env::current_dir().unwrap(); + let show_progress = !self.no_progress && config.log_level().is_writable(&LogLevel::Info); + let current_version = self .version()? .or_else(|| get_user_version_for_directory(current_dir, config)) @@ -131,6 +141,7 @@ impl Command for Install { &config.node_dist_mirror, config.installations_dir(), safe_arch, + show_progress, ) { Err(err @ DownloaderError::VersionAlreadyInstalled { .. }) => { outln!(config, Error, "{} {}", "warning:".bold().yellow(), err); @@ -225,6 +236,7 @@ mod tests { version: UserVersion::from_str("12.0.0").ok(), lts: false, latest: false, + no_progress: true, } .apply(&config) .expect("Can't install"); @@ -250,6 +262,7 @@ mod tests { version: None, lts: false, latest: true, + no_progress: true, } .apply(&config) .expect("Can't install"); diff --git a/src/downloader.rs b/src/downloader.rs index 3e47039..a020f57 100644 --- a/src/downloader.rs +++ b/src/downloader.rs @@ -2,8 +2,11 @@ use crate::arch::Arch; use crate::archive; use crate::archive::{Error as ExtractError, Extract}; use crate::directory_portal::DirectoryPortal; +use crate::progress::ResponseProgress; use crate::version::Version; +use indicatif::ProgressDrawTarget; use log::debug; +use std::io::Read; use std::path::Path; use std::path::PathBuf; use thiserror::Error; @@ -63,10 +66,7 @@ fn download_url(base_url: &Url, version: &Version, arch: &Arch) -> Url { .unwrap() } -pub fn extract_archive_into>( - path: P, - response: crate::http::Response, -) -> Result<(), Error> { +fn extract_archive_into(path: impl AsRef, response: impl Read) -> Result<(), Error> { #[cfg(unix)] let extractor = archive::TarXz::new(response); #[cfg(windows)] @@ -81,6 +81,7 @@ pub fn install_node_dist>( node_dist_mirror: &Url, installations_dir: P, arch: &Arch, + show_progress: bool, ) -> Result<(), Error> { let installation_dir = PathBuf::from(installations_dir.as_ref()).join(version.v_str()); @@ -109,7 +110,14 @@ pub fn install_node_dist>( } debug!("Extracting response..."); - extract_archive_into(&portal, response)?; + if show_progress { + extract_archive_into( + &portal, + ResponseProgress::new(response, ProgressDrawTarget::stderr()), + )?; + } else { + extract_archive_into(&portal, response)?; + } debug!("Extraction completed"); let installed_directory = std::fs::read_dir(&portal)? @@ -171,7 +179,8 @@ mod tests { let version = Version::parse("12.0.0").unwrap(); let arch = Arch::X64; let node_dist_mirror = Url::parse("https://nodejs.org/dist/").unwrap(); - install_node_dist(&version, &node_dist_mirror, path, &arch).expect("Can't install Node 12"); + install_node_dist(&version, &node_dist_mirror, path, &arch, false) + .expect("Can't install Node 12"); let mut location_path = path.join(version.v_str()).join("installation"); diff --git a/src/main.rs b/src/main.rs index cbc4f0f..cfb2a64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ mod installed_versions; mod lts; mod package_json; mod path_ext; +mod progress; mod remote_node_index; mod shell; mod system_info; diff --git a/src/progress.rs b/src/progress.rs new file mode 100644 index 0000000..ec8e301 --- /dev/null +++ b/src/progress.rs @@ -0,0 +1,147 @@ +use std::io::Read; + +use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle}; +use reqwest::blocking::Response; + +pub struct ResponseProgress { + progress: Option, + response: Response, +} + +fn make_progress_bar(size: u64, target: ProgressDrawTarget) -> ProgressBar { + let bar = ProgressBar::with_draw_target(Some(size), target); + + bar.set_style( + ProgressStyle::with_template( + "[{elapsed_precise}] [{bar:40}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})", + ) + .unwrap() + .progress_chars("#>-"), + ); + + bar +} + +impl ResponseProgress { + pub fn new(response: Response, target: ProgressDrawTarget) -> Self { + Self { + progress: response + .content_length() + .map(|len| make_progress_bar(len, target)), + response, + } + } + + pub fn finish(&self) { + if let Some(ref bar) = self.progress { + bar.finish(); + } + } +} + +impl Read for ResponseProgress { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let size = self.response.read(buf)?; + + if let Some(ref bar) = self.progress { + bar.inc(size as u64); + } + + Ok(size) + } +} + +impl Drop for ResponseProgress { + fn drop(&mut self) { + self.finish(); + } +} + +#[cfg(test)] +mod tests { + use indicatif::{ProgressDrawTarget, TermLike}; + use reqwest::blocking::Response; + use std::{ + io::Read, + sync::{Arc, Mutex}, + }; + + use super::ResponseProgress; + + const CONTENT_LENGTH: usize = 100; + + #[derive(Debug)] + struct MockedTerm { + pub buf: Arc>, + } + + impl TermLike for MockedTerm { + fn width(&self) -> u16 { + 80 + } + + fn move_cursor_up(&self, _n: usize) -> std::io::Result<()> { + Ok(()) + } + + fn move_cursor_down(&self, _n: usize) -> std::io::Result<()> { + Ok(()) + } + + fn move_cursor_right(&self, _n: usize) -> std::io::Result<()> { + Ok(()) + } + + fn move_cursor_left(&self, _n: usize) -> std::io::Result<()> { + Ok(()) + } + + fn write_line(&self, s: &str) -> std::io::Result<()> { + self.buf.lock().unwrap().push_str(s); + Ok(()) + } + + fn write_str(&self, s: &str) -> std::io::Result<()> { + self.buf.lock().unwrap().push_str(s); + Ok(()) + } + + fn clear_line(&self) -> std::io::Result<()> { + Ok(()) + } + + fn flush(&self) -> std::io::Result<()> { + Ok(()) + } + } + + #[test] + fn test_reads_data_and_shows_progress() { + let response: Response = http::Response::builder() + .header("Content-Length", CONTENT_LENGTH) + .body("a".repeat(CONTENT_LENGTH)) + .unwrap() + .into(); + + let mut buf = [0; CONTENT_LENGTH]; + + let out_buf = Arc::new(Mutex::new(String::new())); + + let mut progress = ResponseProgress::new( + response, + ProgressDrawTarget::term_like(Box::new(MockedTerm { + buf: out_buf.clone(), + })), + ); + let size = progress.read(&mut buf[..]).unwrap(); + + drop(progress); + + assert_eq!(size, CONTENT_LENGTH); + assert_eq!(buf, "a".repeat(CONTENT_LENGTH).as_bytes()); + assert!(out_buf + .lock() + .unwrap() + .contains(&format!("[{}]", &"#".repeat(40)))); + } +} From 7c713f2d0bfbc2cacae54ccd22e9309a47bf9203 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Sat, 25 May 2024 22:34:27 +0300 Subject: [PATCH 07/10] add configuration documentation (#1127) * add configuration documentation * add markdown heading * add satisfying --- README.md | 11 +++++-- docs/configuration.md | 72 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 docs/configuration.md diff --git a/README.md b/README.md index 558bedf..76decc0 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,10 @@ Please follow your shell instructions to install them. Environment variables need to be setup before you can start using fnm. This is done by evaluating the output of `fnm env`. -To automatically run `fnm use` when a directory contains a `.node-version` or `.nvmrc` file, add the `--use-on-cd` option to your shell setup. + +> [!NOTE] +> Check out the [Configuration](./docs/configuration.md) section to enable highly +> recommended features, like automatic version switching. Adding a `.node-version` to your project is as simple as: @@ -178,7 +181,7 @@ fnm env --use-on-cd | Out-String | Invoke-Expression ```powershell notepad $profile ``` - + #### Windows Command Prompt aka Batch aka WinCMD fnm is also supported but is not entirely covered. [You can set up a startup script](https://superuser.com/a/144348) and append the following lines: @@ -214,6 +217,10 @@ call "%CMDER_ROOT%\bin\fnm_init.cmd" You can replace `%CMDER_ROOT%` with any other convenient path too. +## [Configuration](./docs/configuration.md) + +[See the available configuration options for an extended configuration documentation](./docs/configuration.md) + ## [Usage](./docs/commands.md) [See the available commands for an extended usage documentation](./docs/commands.md) diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..d64af88 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,72 @@ +# Configuration + +fnm comes with many features out of the box. Some of them are not activated by default as they’re changing your shell default behavior, and some are just a feature flag to avoid breaking changes or just experimental until we decide it is worthwhile to introduce them. + +All these features can be configured by adding flags to the `fnm env` call when initializing the shell. For instance, if your shell set up looks like `eval "$(fnm env)"` then you can add a flag to it by changing it to `eval "$(fnm env --my-flag=value)"` + +Here’s a list of these features and capabilities: + +### `--use-on-cd` + +**✅ Highly recommended** + +`--use-on-cd` appends output to `fnm env`'s output that will hook into your shell upon changing directories, and will switch the Node.js version based on the requirements of the current directory, based on `.node-version` or `.nvmrc` (or `packages.json#engines#node` if `--resolve-engines` was enabled). + +This allows you do avoid thinking about `fnm use`, and only `cd ` to make it work. + +### `--version-file-strategy=recursive` + +**✅ Highly recommended** + +Makes `fnm use` and `fnm install` take parent directories into account when looking for a version file ("dotfile")--when no argument was given. + +So, let's say we have the following directory structure: + +``` +repo/ +├── package.json +├── .node-version <- with content: `20.0.0` +└── packages/ + └── my-package/ <- I am here + └── package.json +``` + +And I'm running the following command: + +```sh-session +repo/packages/my-package$ fnm use +``` + +Then fnm will switch to Node.js v20.0.0. + +Without the explicit flag, the value is set to `local`, which will not traverse the directory tree and therefore will print: + +```sh-session +repo/packages/my-package$ fnm use +error: Can't find version in dotfiles. Please provide a version manually to the command. +``` + +### `--enable-corepack` + +**🧪 Experimental** + +Runs [`corepack enable`](https://nodejs.org/api/corepack.html#enabling-the-feature) when a new version of Node.js is installed. Experimental due to the fact Corepack itself is experimental. + +### `--resolve-engines` + +**🧪 Experimental** + +Treats `package.json#engines#node` as a valid Node.js version file ("dotfile"). So, if you have a package.json with the following content: + +```json +{ + "engines": { + "node": ">=20 <21" + } +} +``` + +Then: + +- `fnm install` will install the latest satisfying Node.js 20.x version available in the Node.js dist server +- `fnm use` will use the latest satisfying Node.js 20.x version available on your system, or prompt to install if no version matched. From ced2f306b0598706a73eb677ec0ac93622b6b7eb Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Sun, 26 May 2024 07:43:25 +0300 Subject: [PATCH 08/10] fix clippy (#1126) * run cargo clippy --fix * cargo fmt * add rust-toolchain.toml * force rust version * fixup! run cargo clippy --fix fix urls * add clippy and rustfmt * update command docs * export Zip on Windows * remove benchmarking for a while --- .github/workflows/rust.yml | 151 +++++++++++++++++++------------------ docs/commands.md | 52 ++++++------- rust-toolchain.toml | 3 + src/arch.rs | 4 +- src/archive/mod.rs | 4 + src/commands/env.rs | 2 +- src/commands/install.rs | 2 +- src/commands/use.rs | 2 +- src/config.rs | 4 +- src/shell/bash.rs | 6 +- src/shell/fish.rs | 6 +- src/shell/infer/mod.rs | 2 +- src/shell/powershell.rs | 6 +- src/shell/zsh.rs | 6 +- 14 files changed, 130 insertions(+), 120 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 46984f6..9046956 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,13 +10,16 @@ concurrency: group: ci-${{ github.head_ref }} cancel-in-progress: true +env: + RUST_VERSION: "1.78" + jobs: fmt: runs-on: ubuntu-latest steps: - uses: hecrj/setup-rust-action@v1 with: - rust-version: stable + rust-version: ${{env.RUST_VERSION}} - uses: Swatinem/rust-cache@v2 - uses: actions/checkout@v3 - name: cargo fmt @@ -27,7 +30,7 @@ jobs: steps: - uses: hecrj/setup-rust-action@v1 with: - rust-version: stable + rust-version: ${{env.RUST_VERSION}} - uses: Swatinem/rust-cache@v2 - uses: actions/checkout@v3 - name: cargo clippy @@ -41,7 +44,7 @@ jobs: steps: - uses: hecrj/setup-rust-action@v1 with: - rust-version: stable + rust-version: ${{env.RUST_VERSION}} - uses: Swatinem/rust-cache@v2 - uses: actions/checkout@v3 - name: Run tests @@ -53,7 +56,7 @@ jobs: steps: - uses: hecrj/setup-rust-action@v1 with: - rust-version: stable + rust-version: ${{env.RUST_VERSION}} - uses: Swatinem/rust-cache@v2 - uses: actions/checkout@v3 - name: Build release binary @@ -71,7 +74,7 @@ jobs: steps: - uses: hecrj/setup-rust-action@v1 with: - rust-version: stable + rust-version: ${{env.RUST_VERSION}} - uses: Swatinem/rust-cache@v2 - uses: actions/checkout@v3 - name: Build release binary @@ -237,7 +240,7 @@ jobs: steps: - uses: hecrj/setup-rust-action@v1 with: - rust-version: stable + rust-version: ${{env.RUST_VERSION}} targets: x86_64-unknown-linux-musl - uses: Swatinem/rust-cache@v2 with: @@ -278,7 +281,7 @@ jobs: uses: docker/setup-qemu-action@v2 - uses: hecrj/setup-rust-action@v1 with: - rust-version: stable + rust-version: ${{env.RUST_VERSION}} - uses: Swatinem/rust-cache@v2 with: key: arm-binary-${{ matrix.arch }} @@ -358,70 +361,70 @@ jobs: run: | pnpm run generate-command-docs --check --binary-path=$(which fnm) - run_e2e_benchmarks: - runs-on: ubuntu-latest - name: bench/linux - needs: [build_static_linux_binary] - permissions: - contents: write - pull-requests: write - steps: - - name: install necessary shells - run: sudo apt-get update && sudo apt-get install -y fish zsh bash hyperfine - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 - with: - name: fnm-linux - path: target/release - - name: mark binary as executable - run: chmod +x target/release/fnm - - name: install fnm as binary - run: | - sudo install target/release/fnm /bin - fnm --version - - uses: pnpm/action-setup@v2.2.4 - with: - run_install: false - - uses: actions/setup-node@v3 - with: - node-version: 18.x - cache: "pnpm" - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "::set-output name=pnpm_cache_dir::$(pnpm store path)" - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - run: pnpm install - - name: Run benchmarks - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SHOULD_STORE: ${{ toJson(!github.event.pull_request) }} - id: benchmark - run: | - delimiter="$(openssl rand -hex 8)" - echo "markdown<<${delimiter}" >> "${GITHUB_OUTPUT}" - node benchmarks/run.mjs --store=$SHOULD_STORE >> "${GITHUB_OUTPUT}" - echo "${delimiter}" >> "${GITHUB_OUTPUT}" - - - name: Create a PR comment - if: ${{ github.event.pull_request }} - uses: thollander/actions-comment-pull-request@v2 - with: - message: | - ## Linux Benchmarks for ${{ github.event.pull_request.head.sha }} - ${{ steps.benchmark.outputs.markdown }} - comment_tag: "benchy comment" - - - name: Create a commit comment - if: ${{ !github.event.pull_request }} - uses: peter-evans/commit-comment@v2 - with: - body: | - ## Linux Benchmarks - ${{ steps.benchmark.outputs.markdown }} + # TODO: use bnz + # run_e2e_benchmarks: + # runs-on: ubuntu-latest + # name: bench/linux + # needs: [build_static_linux_binary] + # permissions: + # contents: write + # pull-requests: write + # steps: + # - name: install necessary shells + # run: sudo apt-get update && sudo apt-get install -y fish zsh bash hyperfine + # - uses: actions/checkout@v3 + # - uses: actions/download-artifact@v3 + # with: + # name: fnm-linux + # path: target/release + # - name: mark binary as executable + # run: chmod +x target/release/fnm + # - name: install fnm as binary + # run: | + # sudo install target/release/fnm /bin + # fnm --version + # - uses: pnpm/action-setup@v2.2.4 + # with: + # run_install: false + # - uses: actions/setup-node@v3 + # with: + # node-version: 18.x + # cache: "pnpm" + # - name: Get pnpm store directory + # id: pnpm-cache + # run: | + # echo "::set-output name=pnpm_cache_dir::$(pnpm store path)" + # - uses: actions/cache@v3 + # name: Setup pnpm cache + # with: + # path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} + # key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + # restore-keys: | + # ${{ runner.os }}-pnpm-store- + # - run: pnpm install + # - name: Run benchmarks + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # SHOULD_STORE: ${{ toJson(!github.event.pull_request) }} + # id: benchmark + # run: | + # delimiter="$(openssl rand -hex 8)" + # echo "markdown<<${delimiter}" >> "${GITHUB_OUTPUT}" + # node benchmarks/run.mjs --store=$SHOULD_STORE >> "${GITHUB_OUTPUT}" + # echo "${delimiter}" >> "${GITHUB_OUTPUT}" + # - name: Create a PR comment + # if: ${{ github.event.pull_request }} + # uses: thollander/actions-comment-pull-request@v2 + # with: + # message: | + # ## Linux Benchmarks for ${{ github.event.pull_request.head.sha }} + # ${{ steps.benchmark.outputs.markdown }} + # comment_tag: "benchy comment" + # + # - name: Create a commit comment + # if: ${{ !github.event.pull_request }} + # uses: peter-evans/commit-comment@v2 + # with: + # body: | + # ## Linux Benchmarks + # ${{ steps.benchmark.outputs.markdown }} diff --git a/docs/commands.md b/docs/commands.md index d3072aa..631f170 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -22,7 +22,7 @@ Commands: Options: --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -55,7 +55,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -82,7 +82,7 @@ Usage: fnm list-remote [OPTIONS] Options: --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -115,7 +115,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -139,7 +139,7 @@ Usage: fnm list [OPTIONS] Options: --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -172,7 +172,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -203,7 +203,7 @@ Options: Install latest LTS --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -242,7 +242,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -273,7 +273,7 @@ Options: Install the version if it isn't installed yet --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -309,7 +309,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -337,7 +337,7 @@ Usage: fnm env [OPTIONS] Options: --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -381,7 +381,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -405,7 +405,7 @@ Usage: fnm completions [OPTIONS] Options: --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -443,7 +443,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -474,7 +474,7 @@ Arguments: Options: --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -507,7 +507,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -535,7 +535,7 @@ Arguments: Options: --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -568,7 +568,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -598,7 +598,7 @@ Arguments: Options: --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -631,7 +631,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -655,7 +655,7 @@ Usage: fnm current [OPTIONS] Options: --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -688,7 +688,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -721,7 +721,7 @@ Arguments: Options: --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -757,7 +757,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] @@ -787,7 +787,7 @@ Arguments: Options: --node-dist-mirror - https://nodejs.org/dist/ mirror + mirror [env: FNM_NODE_DIST_MIRROR] [default: https://nodejs.org/dist] @@ -820,7 +820,7 @@ Options: - recursive: Use the version of Node defined within the current directory and all parent directories --corepack-enabled - Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see https://nodejs.org/api/corepack.html + Enable corepack support for each new installation. This will make fnm call `corepack enable` on every Node.js installation. For more information about corepack see [env: FNM_COREPACK_ENABLED] diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..d38fc82 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.78" +components = ["rustfmt", "clippy"] diff --git a/src/arch.rs b/src/arch.rs index 080552c..b1ed941 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -17,10 +17,10 @@ pub enum Arch { pub fn get_safe_arch<'a>(arch: &'a Arch, version: &Version) -> &'a Arch { use crate::system_info::{platform_arch, platform_name}; - return match (platform_name(), platform_arch(), version) { + match (platform_name(), platform_arch(), version) { ("darwin", "arm64", Version::Semver(v)) if v.major < 16 => &Arch::X64, _ => arch, - }; + } } #[cfg(windows)] diff --git a/src/archive/mod.rs b/src/archive/mod.rs index e3cf33f..ac44763 100644 --- a/src/archive/mod.rs +++ b/src/archive/mod.rs @@ -3,5 +3,9 @@ pub mod tar_xz; pub mod zip; pub use self::extract::{Error, Extract}; + +#[cfg(unix)] pub use self::tar_xz::TarXz; + +#[cfg(windows)] pub use self::zip::Zip; diff --git a/src/commands/env.rs b/src/commands/env.rs index d8cf2b3..f0eacb7 100644 --- a/src/commands/env.rs +++ b/src/commands/env.rs @@ -44,7 +44,7 @@ fn make_symlink(config: &FnmConfig) -> Result { } match symlink_dir(config.default_version_dir(), &temp_dir) { - Ok(_) => Ok(temp_dir), + Ok(()) => Ok(temp_dir), Err(source) => Err(Error::CantCreateSymlink { source, temp_dir }), } } diff --git a/src/commands/install.rs b/src/commands/install.rs index ed20fe9..4ca15a0 100644 --- a/src/commands/install.rs +++ b/src/commands/install.rs @@ -147,7 +147,7 @@ impl Command for Install { outln!(config, Error, "{} {}", "warning:".bold().yellow(), err); } Err(source) => Err(Error::DownloadError { source })?, - Ok(_) => {} + Ok(()) => {} }; if config.corepack_enabled() { diff --git a/src/commands/use.rs b/src/commands/use.rs index 14bbfb4..3f2751f 100644 --- a/src/commands/use.rs +++ b/src/commands/use.rs @@ -153,7 +153,7 @@ fn install_new_version( fn replace_symlink(from: &std::path::Path, to: &std::path::Path) -> std::io::Result<()> { let symlink_deletion_result = fs::remove_symlink_dir(to); match fs::symlink_dir(from, to) { - ok @ Ok(_) => ok, + ok @ Ok(()) => ok, err @ Err(_) => symlink_deletion_result.and(err), } } diff --git a/src/config.rs b/src/config.rs index 1c65399..2f68a9d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,7 +7,7 @@ use url::Url; #[derive(clap::Parser, Debug)] pub struct FnmConfig { - /// https://nodejs.org/dist/ mirror + /// mirror #[clap( long, env = "FNM_NODE_DIST_MIRROR", @@ -67,7 +67,7 @@ pub struct FnmConfig { /// Enable corepack support for each new installation. /// This will make fnm call `corepack enable` on every Node.js installation. - /// For more information about corepack see https://nodejs.org/api/corepack.html + /// For more information about corepack see #[clap( long, env = "FNM_COREPACK_ENABLED", diff --git a/src/shell/bash.rs b/src/shell/bash.rs index cada96d..016809e 100644 --- a/src/shell/bash.rs +++ b/src/shell/bash.rs @@ -28,13 +28,13 @@ impl Shell for Bash { fn use_on_cd(&self, config: &crate::config::FnmConfig) -> anyhow::Result { let autoload_hook = match config.version_file_strategy() { VersionFileStrategy::Local => indoc!( - r#" + r" if [[ -f .node-version || -f .nvmrc ]]; then fnm use --silent-if-unchanged fi - "# + " ), - VersionFileStrategy::Recursive => r#"fnm use --silent-if-unchanged"#, + VersionFileStrategy::Recursive => r"fnm use --silent-if-unchanged", }; Ok(formatdoc!( r#" diff --git a/src/shell/fish.rs b/src/shell/fish.rs index 18f6b32..0b3db7f 100644 --- a/src/shell/fish.rs +++ b/src/shell/fish.rs @@ -28,13 +28,13 @@ impl Shell for Fish { fn use_on_cd(&self, config: &crate::config::FnmConfig) -> anyhow::Result { let autoload_hook = match config.version_file_strategy() { VersionFileStrategy::Local => indoc!( - r#" + r" if test -f .node-version -o -f .nvmrc fnm use --silent-if-unchanged end - "# + " ), - VersionFileStrategy::Recursive => r#"fnm use --silent-if-unchanged"#, + VersionFileStrategy::Recursive => r"fnm use --silent-if-unchanged", }; Ok(formatdoc!( r#" diff --git a/src/shell/infer/mod.rs b/src/shell/infer/mod.rs index e0c22c6..e7b1a1b 100644 --- a/src/shell/infer/mod.rs +++ b/src/shell/infer/mod.rs @@ -7,7 +7,7 @@ pub use self::unix::infer_shell; #[cfg(not(unix))] pub use self::windows::infer_shell; -pub(self) fn shell_from_string(shell: &str) -> Option> { +fn shell_from_string(shell: &str) -> Option> { use super::{Bash, Fish, PowerShell, WindowsCmd, Zsh}; match shell { "sh" | "bash" => return Some(Box::from(Bash)), diff --git a/src/shell/powershell.rs b/src/shell/powershell.rs index be77c9b..99f115f 100644 --- a/src/shell/powershell.rs +++ b/src/shell/powershell.rs @@ -28,11 +28,11 @@ impl Shell for PowerShell { fn use_on_cd(&self, config: &crate::config::FnmConfig) -> anyhow::Result { let autoload_hook = match config.version_file_strategy() { VersionFileStrategy::Local => indoc!( - r#" + r" If ((Test-Path .nvmrc) -Or (Test-Path .node-version)) { & fnm use --silent-if-unchanged } - "# + " ), - VersionFileStrategy::Recursive => r#"fnm use --silent-if-unchanged"#, + VersionFileStrategy::Recursive => r"fnm use --silent-if-unchanged", }; Ok(formatdoc!( r#" diff --git a/src/shell/zsh.rs b/src/shell/zsh.rs index 01e039c..f138f22 100644 --- a/src/shell/zsh.rs +++ b/src/shell/zsh.rs @@ -32,13 +32,13 @@ impl Shell for Zsh { fn use_on_cd(&self, config: &crate::config::FnmConfig) -> anyhow::Result { let autoload_hook = match config.version_file_strategy() { VersionFileStrategy::Local => indoc!( - r#" + r" if [[ -f .node-version || -f .nvmrc ]]; then fnm use --silent-if-unchanged fi - "# + " ), - VersionFileStrategy::Recursive => r#"fnm use --silent-if-unchanged"#, + VersionFileStrategy::Recursive => r"fnm use --silent-if-unchanged", }; Ok(formatdoc!( r#" From d9af62ff432e57398efdd812e218f3fb390c5243 Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Sun, 26 May 2024 08:26:23 +0300 Subject: [PATCH 09/10] improve progress bar in install (#1125) * update indicatif version * add a eprintln after it finishes * make it prettier * update progress bar docs * add changeset * s/no_progress: true/progress: ProgressConfig::Never/g * styling the progress bar * fix test --- .changeset/poor-poets-compete.md | 5 +++++ Cargo.lock | 4 ++-- Cargo.toml | 2 +- docs/commands.md | 7 +++++-- src/commands/install.rs | 23 ++++++++++++----------- src/progress.rs | 30 ++++++++++++++++++++++++------ 6 files changed, 49 insertions(+), 22 deletions(-) create mode 100644 .changeset/poor-poets-compete.md diff --git a/.changeset/poor-poets-compete.md b/.changeset/poor-poets-compete.md new file mode 100644 index 0000000..1d8287f --- /dev/null +++ b/.changeset/poor-poets-compete.md @@ -0,0 +1,5 @@ +--- +"fnm": patch +--- + +make nicer styling in progress bar (add newline, make it unicode) diff --git a/Cargo.lock b/Cargo.lock index fbc969f..49143d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -935,9 +935,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.6" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b297dc40733f23a0e52728a58fa9489a5b7638a324932de16b41adc3ef80730" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", diff --git a/Cargo.toml b/Cargo.toml index f6f9326..161b4ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ sysinfo = "0.29.3" thiserror = "1.0.44" clap_complete = "4.3.1" anyhow = "1.0.71" -indicatif = "0.17.6" +indicatif = "0.17.8" [dev-dependencies] pretty_assertions = "1.4.0" diff --git a/docs/commands.md b/docs/commands.md index 631f170..8468c86 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -216,8 +216,11 @@ Options: --latest Install latest version - --no-progress - Do not display a progress bar + --progress + Show an interactive progress bar for the download status + + [default: auto] + [possible values: auto, never, always] --log-level The log level of fnm commands diff --git a/src/commands/install.rs b/src/commands/install.rs index 4ca15a0..79013ad 100644 --- a/src/commands/install.rs +++ b/src/commands/install.rs @@ -3,9 +3,9 @@ use crate::alias::create_alias; use crate::arch::get_safe_arch; use crate::config::FnmConfig; use crate::downloader::{install_node_dist, Error as DownloaderError}; -use crate::log_level::LogLevel; use crate::lts::LtsType; use crate::outln; +use crate::progress::ProgressConfig; use crate::remote_node_index; use crate::user_version::UserVersion; use crate::version::Version; @@ -27,9 +27,11 @@ pub struct Install { #[clap(long, conflicts_with_all = &["version", "lts"])] pub latest: bool, - /// Do not display a progress bar - #[clap(long)] - pub no_progress: bool, + /// Show an interactive progress bar for the download + /// status. + #[clap(long, default_value_t)] + #[arg(value_enum)] + pub progress: ProgressConfig, } impl Install { @@ -39,19 +41,19 @@ impl Install { version: v, lts: false, latest: false, - no_progress: _, + .. } => Ok(v), Self { version: None, lts: true, latest: false, - no_progress: _, + .. } => Ok(Some(UserVersion::Full(Version::Lts(LtsType::Latest)))), Self { version: None, lts: false, latest: true, - no_progress: _, + .. } => Ok(Some(UserVersion::Full(Version::Latest))), _ => Err(Error::TooManyVersionsProvided), } @@ -63,8 +65,7 @@ impl Command for Install { fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { let current_dir = std::env::current_dir().unwrap(); - - let show_progress = !self.no_progress && config.log_level().is_writable(&LogLevel::Info); + let show_progress = self.progress.enabled(config); let current_version = self .version()? @@ -236,7 +237,7 @@ mod tests { version: UserVersion::from_str("12.0.0").ok(), lts: false, latest: false, - no_progress: true, + progress: ProgressConfig::Never, } .apply(&config) .expect("Can't install"); @@ -262,7 +263,7 @@ mod tests { version: None, lts: false, latest: true, - no_progress: true, + progress: ProgressConfig::Never, } .apply(&config) .expect("Can't install"); diff --git a/src/progress.rs b/src/progress.rs index ec8e301..aaaeea8 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -8,15 +8,35 @@ pub struct ResponseProgress { response: Response, } +#[derive(Default, Clone, Debug, clap::ValueEnum)] +pub enum ProgressConfig { + #[default] + Auto, + Never, + Always, +} + +impl ProgressConfig { + pub fn enabled(&self, config: &crate::config::FnmConfig) -> bool { + match self { + Self::Never => false, + Self::Always => true, + Self::Auto => config + .log_level() + .is_writable(&crate::log_level::LogLevel::Info), + } + } +} + fn make_progress_bar(size: u64, target: ProgressDrawTarget) -> ProgressBar { let bar = ProgressBar::with_draw_target(Some(size), target); bar.set_style( ProgressStyle::with_template( - "[{elapsed_precise}] [{bar:40}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})", + "{elapsed_precise:.white.dim} {wide_bar:.cyan} {bytes}/{total_bytes} ({bytes_per_sec}, {eta})", ) .unwrap() - .progress_chars("#>-"), + .progress_chars("█▉▊▋▌▍▎▏ "), ); bar @@ -54,6 +74,7 @@ impl Read for ResponseProgress { impl Drop for ResponseProgress { fn drop(&mut self) { self.finish(); + eprintln!(); } } @@ -139,9 +160,6 @@ mod tests { assert_eq!(size, CONTENT_LENGTH); assert_eq!(buf, "a".repeat(CONTENT_LENGTH).as_bytes()); - assert!(out_buf - .lock() - .unwrap() - .contains(&format!("[{}]", &"#".repeat(40)))); + assert!(out_buf.lock().unwrap().contains(&"█".repeat(40))); } } From 098bd52f4750d69d6afa468a928e34b04825bfec Mon Sep 17 00:00:00 2001 From: Gal Schlezinger Date: Mon, 27 May 2024 11:10:19 +0300 Subject: [PATCH 10/10] fix screen recording terminal sizing (#1130) --- .ci/record_screen.sh | 6 +++++- Cargo.lock | 7 +++++++ Cargo.toml | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.ci/record_screen.sh b/.ci/record_screen.sh index 8ee6f41..5d372fe 100755 --- a/.ci/record_screen.sh +++ b/.ci/record_screen.sh @@ -20,7 +20,11 @@ RECORDING_PATH=$DIRECTORY/screen_recording (rm -rf "$RECORDING_PATH" &> /dev/null || true) -asciinema rec -c "$DIRECTORY/recorded_screen_script.sh" "$RECORDING_PATH" +asciinema rec \ + --command "$DIRECTORY/recorded_screen_script.sh" \ + --cols 70 \ + --rows 17 \ + "$RECORDING_PATH" sed "s@$TEMP_DIR@~@g" "$RECORDING_PATH" | \ svg-term \ --window \ diff --git a/Cargo.lock b/Cargo.lock index 49143d4..25ffbd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -943,6 +943,7 @@ dependencies = [ "instant", "number_prefix", "portable-atomic", + "unicode-segmentation", "unicode-width", ] @@ -1950,6 +1951,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-width" version = "0.1.10" diff --git a/Cargo.toml b/Cargo.toml index 161b4ef..131ddfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ sysinfo = "0.29.3" thiserror = "1.0.44" clap_complete = "4.3.1" anyhow = "1.0.71" -indicatif = "0.17.8" +indicatif = { version = "0.17.8", features = ["improved_unicode"] } [dev-dependencies] pretty_assertions = "1.4.0"