Browse Source

feat: support `fnm install --latest` (#859)

* feat: support to install the latest version

* chore: test & fmt & clippy

* docs: update command docs

* docs: update command docs

* Create warm-rice-appear.md

* Update change set to be a minor release

As this is a new feature

* chore: update workflow to install pnpm

* Remove `feat:` from changeset

Because GitHub actions approval button is missing

* run pnpm like other tasks

* Revert "run pnpm like other tasks"

This reverts commit f5f2ca29f1.
we can revert the yarn changes and do it in a different PR

* revert the changes in .github/workflows

Co-authored-by: Gal Schlezinger <gal@spitfire.co.il>
remotes/origin/clean-multishell-on-shell-exit
nzhl 2 years ago committed by GitHub
parent
commit
ca71291020
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .changeset/warm-rice-appear.md
  2. 2
      .ci/prepare-version.js
  3. 4
      .ci/print-command-docs.js
  4. 3
      docs/commands.md
  5. 63
      src/commands/install.rs
  6. 2
      src/user_version.rs
  7. 6
      src/version.rs

5
.changeset/warm-rice-appear.md

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
---
"fnm": minor
---
support `fnm install --latest` to install the latest Node.js version

2
.ci/prepare-version.js

@ -17,7 +17,7 @@ const command = cmd.command({ @@ -17,7 +17,7 @@ const command = cmd.command({
async handler({}) {
updateCargoToml(await getPackageVersion())
exec("cargo build --release")
exec("yarn generate-command-docs --binary-path=./target/release/fnm")
exec("pnpm generate-command-docs --binary-path=./target/release/fnm")
exec("./.ci/record_screen.sh")
},
})

4
.ci/print-command-docs.js

@ -42,7 +42,7 @@ const command = cmd.command({ @@ -42,7 +42,7 @@ const command = cmd.command({
if (gitStatus.state === "dirty") {
process.exitCode = 1
console.error(
"The file has changed. Please re-run `yarn generate-command-docs`."
"The file has changed. Please re-run `pnpm generate-command-docs`."
)
console.error(`hint: The following diff was found:`)
console.error()
@ -76,7 +76,7 @@ async function main(targetFile, fnmPath) { @@ -76,7 +76,7 @@ async function main(targetFile, fnmPath) {
stream.close()
await execa(`yarn`, ["prettier", "--write", targetFile])
await execa(`pnpm`, ["prettier", "--write", targetFile])
}
/**

3
docs/commands.md

@ -458,6 +458,9 @@ OPTIONS: @@ -458,6 +458,9 @@ OPTIONS:
-h, --help
Print help information
--latest
Install latest version
--log-level <LOG_LEVEL>
The log level of fnm commands

63
src/commands/install.rs

@ -18,25 +18,33 @@ pub struct Install { @@ -18,25 +18,33 @@ pub struct Install {
pub version: Option<UserVersion>,
/// Install latest LTS
#[clap(long, conflicts_with = "version")]
#[clap(long, conflicts_with_all = &["version", "latest"])]
pub lts: bool,
/// Install latest version
#[clap(long, conflicts_with_all = &["version", "lts"])]
pub latest: bool,
}
impl Install {
fn version(self) -> Result<Option<UserVersion>, Error> {
match self {
Self {
version: Some(_),
lts: true,
} => Err(Error::TooManyVersionsProvided),
Self {
version: v,
lts: false,
latest: false,
} => Ok(v),
Self {
version: None,
lts: true,
latest: false,
} => Ok(Some(UserVersion::Full(Version::Lts(LtsType::Latest)))),
Self {
version: None,
lts: false,
latest: true,
} => Ok(Some(UserVersion::Full(Version::Latest))),
_ => Err(Error::TooManyVersionsProvided),
}
}
}
@ -74,6 +82,21 @@ impl super::command::Command for Install { @@ -74,6 +82,21 @@ impl super::command::Command for Install {
);
picked_version
}
UserVersion::Full(Version::Latest) => {
let available_versions: Vec<_> = remote_node_index::list(&config.node_dist_mirror)
.map_err(|source| Error::CantListRemoteVersions { source })?;
let picked_version = available_versions
.last()
.ok_or(Error::CantFindLatest)?
.version
.clone();
debug!(
"Resolved {} into Node version {}",
Version::Latest.v_str().cyan(),
picked_version.v_str().cyan()
);
picked_version
}
current_version => {
let available_versions: Vec<_> = remote_node_index::list(&config.node_dist_mirror)
.map_err(|source| Error::CantListRemoteVersions { source })?
@ -153,6 +176,8 @@ pub enum Error { @@ -153,6 +176,8 @@ pub enum Error {
CantFindNodeVersion { requested_version: UserVersion },
#[error("Can't find relevant LTS named {}", lts_type)]
CantFindRelevantLts { lts_type: crate::lts::LtsType },
#[error("Can't find any versions in the upstream version index.")]
CantFindLatest,
#[error("The requested version is not installable: {}", version.v_str())]
UninstallableVersion { version: Version },
#[error("Too many versions provided. Please don't use --lts with a version string.")]
@ -175,6 +200,7 @@ mod tests { @@ -175,6 +200,7 @@ mod tests {
Install {
version: UserVersion::from_str("12.0.0").ok(),
lts: false,
latest: false,
}
.apply(&config)
.expect("Can't install");
@ -190,4 +216,31 @@ mod tests { @@ -190,4 +216,31 @@ mod tests {
.ok()
);
}
#[test]
fn test_install_latest() {
let base_dir = tempfile::tempdir().unwrap();
let config = FnmConfig::default().with_base_dir(Some(base_dir.path().to_path_buf()));
Install {
version: None,
lts: false,
latest: true,
}
.apply(&config)
.expect("Can't install");
let available_versions: Vec<_> =
remote_node_index::list(&config.node_dist_mirror).expect("Can't get node version list");
let latest_version = available_versions.last().unwrap().version.clone();
assert!(config.installations_dir().exists());
assert!(config
.installations_dir()
.join(latest_version.to_string())
.join("installation")
.canonicalize()
.unwrap()
.exists());
}
}

2
src/user_version.rs

@ -41,7 +41,7 @@ impl UserVersion { @@ -41,7 +41,7 @@ impl UserVersion {
}
}
}
(_, Version::Bypassed | Version::Lts(_) | Version::Alias(_)) => false,
(_, Version::Bypassed | Version::Lts(_) | Version::Alias(_) | Version::Latest) => false,
(Self::OnlyMajor(major), Version::Semver(other)) => *major == other.major,
(Self::MajorMinor(major, minor), Version::Semver(other)) => {
*major == other.major && *minor == other.minor

6
src/version.rs

@ -9,6 +9,7 @@ pub enum Version { @@ -9,6 +9,7 @@ pub enum Version {
Semver(semver::Version),
Lts(LtsType),
Alias(String),
Latest,
Bypassed,
}
@ -58,7 +59,7 @@ impl Version { @@ -58,7 +59,7 @@ impl Version {
pub fn installation_path(&self, config: &config::FnmConfig) -> std::path::PathBuf {
match self {
Self::Bypassed => system_version::path(),
v @ (Self::Lts(_) | Self::Alias(_)) => {
v @ (Self::Lts(_) | Self::Alias(_) | Self::Latest) => {
config.aliases_dir().join(v.alias_name().unwrap())
}
v @ Self::Semver(_) => config
@ -93,6 +94,7 @@ impl std::fmt::Display for Version { @@ -93,6 +94,7 @@ impl std::fmt::Display for Version {
Self::Lts(lts) => write!(f, "lts-{}", lts),
Self::Semver(semver) => write!(f, "v{}", semver),
Self::Alias(alias) => write!(f, "{}", alias),
Self::Latest => write!(f, "latest"),
}
}
}
@ -107,7 +109,7 @@ impl FromStr for Version { @@ -107,7 +109,7 @@ impl FromStr for Version {
impl PartialEq<semver::Version> for Version {
fn eq(&self, other: &semver::Version) -> bool {
match self {
Self::Bypassed | Self::Lts(_) | Self::Alias(_) => false,
Self::Bypassed | Self::Lts(_) | Self::Alias(_) | Self::Latest => false,
Self::Semver(v) => v == other,
}
}

Loading…
Cancel
Save