mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-20 20:48:43 +00:00
fix version matching, now works with semver
This commit is contained in:
parent
a6c70b41e2
commit
ef6a3d0ef7
4 changed files with 210 additions and 45 deletions
127
Cargo.lock
generated
127
Cargo.lock
generated
|
@ -134,12 +134,14 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_metadata",
|
"cargo_metadata",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
|
"crates-index",
|
||||||
"crates_io_api",
|
"crates_io_api",
|
||||||
"dirs",
|
"dirs",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"flate2",
|
"flate2",
|
||||||
"log",
|
"log",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"simplelog",
|
"simplelog",
|
||||||
|
@ -180,6 +182,9 @@ name = "cc"
|
||||||
version = "1.0.66"
|
version = "1.0.66"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
|
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -244,6 +249,24 @@ version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crates-index"
|
||||||
|
version = "0.16.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24823d553339d125040d989d2a593a01b034fe5ac17714423bcd2c3d168878"
|
||||||
|
dependencies = [
|
||||||
|
"git2",
|
||||||
|
"glob",
|
||||||
|
"hex",
|
||||||
|
"home",
|
||||||
|
"memchr",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"smartstring",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crates_io_api"
|
name = "crates_io_api"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
|
@ -530,6 +553,27 @@ dependencies = [
|
||||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "git2"
|
||||||
|
version = "0.13.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44f267c9da8a4de3c615b59e23606c75f164f84896e97f4dd6c15a4294de4359"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
"libgit2-sys",
|
||||||
|
"log",
|
||||||
|
"openssl-probe",
|
||||||
|
"openssl-sys",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
|
@ -574,6 +618,24 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "home"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -692,6 +754,15 @@ version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.46"
|
version = "0.3.46"
|
||||||
|
@ -723,6 +794,46 @@ version = "0.2.81"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libgit2-sys"
|
||||||
|
version = "0.12.17+1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4ebdf65ca745126df8824688637aa0535a88900b83362d8ca63893bcf4e8841"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"libssh2-sys",
|
||||||
|
"libz-sys",
|
||||||
|
"openssl-sys",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libssh2-sys"
|
||||||
|
version = "0.2.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df40b13fe7ea1be9b9dffa365a51273816c345fc1811478b57ed7d964fbfc4ce"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"libz-sys",
|
||||||
|
"openssl-sys",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libz-sys"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.11"
|
version = "0.4.11"
|
||||||
|
@ -1384,6 +1495,16 @@ version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smartstring"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ada87540bf8ef4cf8a1789deb175626829bb59b1fefd816cf7f7f55efcdbae9"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.3.19"
|
version = "0.3.19"
|
||||||
|
@ -1395,6 +1516,12 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -7,10 +7,6 @@ authors = ["ryan <ryan@kurte.nz>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
|
|
||||||
[package.metadata.binstall]
|
|
||||||
pkg-url = "https://github.com/ryankurte/cargo-binstall/releases/download/v{ version }/cargo-binstall-{ target }.tgz"
|
|
||||||
pkg-fmt = "tgz"
|
|
||||||
|
|
||||||
[[pkg_bin]]
|
[[pkg_bin]]
|
||||||
name = "cargo-binstall"
|
name = "cargo-binstall"
|
||||||
path = "cargo-binstall-{ target }"
|
path = "cargo-binstall-{ target }"
|
||||||
|
@ -34,10 +30,8 @@ strum_macros = "0.20.1"
|
||||||
strum = "0.20.0"
|
strum = "0.20.0"
|
||||||
dirs = "3.0.1"
|
dirs = "3.0.1"
|
||||||
serde_derive = "1.0.118"
|
serde_derive = "1.0.118"
|
||||||
|
crates-index = "0.16.2"
|
||||||
|
semver = "0.11.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = "0.8.2"
|
env_logger = "0.8.2"
|
||||||
#github = "0.1.2"
|
|
||||||
|
|
||||||
[patch.crates-io]
|
|
||||||
#reqwest = { git = "https://github.com/seanmonstar/reqwest.git" }
|
|
||||||
|
|
|
@ -2,45 +2,90 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use log::{debug, error};
|
use log::{debug};
|
||||||
|
use anyhow::{Context, anyhow};
|
||||||
|
use semver::{Version, VersionReq};
|
||||||
|
|
||||||
use crates_io_api::AsyncClient;
|
use crates_io_api::AsyncClient;
|
||||||
|
|
||||||
use crate::PkgFmt;
|
use crate::PkgFmt;
|
||||||
use crate::helpers::*;
|
use crate::helpers::*;
|
||||||
|
|
||||||
|
fn find_version<'a, V: Iterator<Item=&'a str>>(requirement: &str, version_iter: V) -> Result<String, anyhow::Error> {
|
||||||
|
// Parse version requirement
|
||||||
|
let version_req = VersionReq::parse(requirement)?;
|
||||||
|
|
||||||
|
// Filter for matching versions
|
||||||
|
let mut filtered: Vec<_> = version_iter.filter(|v| {
|
||||||
|
// Remove leading `v` for git tags
|
||||||
|
let ver_str = match v.strip_prefix("s") {
|
||||||
|
Some(v) => v,
|
||||||
|
None => v,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse out version
|
||||||
|
let ver = match Version::parse(ver_str) {
|
||||||
|
Ok(sv) => sv,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("Version: {:?}", ver);
|
||||||
|
|
||||||
|
// Filter by version match
|
||||||
|
version_req.matches(&ver)
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
// Sort by highest matching version
|
||||||
|
filtered.sort_by(|a, b| {
|
||||||
|
let a = Version::parse(a).unwrap();
|
||||||
|
let b = Version::parse(b).unwrap();
|
||||||
|
|
||||||
|
b.partial_cmp(&a).unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
debug!("Filtered: {:?}", filtered);
|
||||||
|
|
||||||
|
// Return highest version
|
||||||
|
match filtered.get(0) {
|
||||||
|
Some(v) => Ok(v.to_string()),
|
||||||
|
None => Err(anyhow!("No matching version for requirement: '{}'", version_req))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch a crate by name and version from crates.io
|
/// Fetch a crate by name and version from crates.io
|
||||||
pub async fn fetch_crate_cratesio(name: &str, version: Option<&str>, temp_dir: &Path) -> Result<PathBuf, anyhow::Error> {
|
pub async fn fetch_crate_cratesio(name: &str, version_req: &str, temp_dir: &Path) -> Result<PathBuf, anyhow::Error> {
|
||||||
// Build crates.io api client and fetch info
|
|
||||||
let api_client = AsyncClient::new("cargo-binstall (https://github.com/ryankurte/cargo-binstall)", Duration::from_millis(100))?;
|
|
||||||
|
|
||||||
debug!("Fetching information for crate: '{}'", name);
|
// Fetch / update index
|
||||||
|
debug!("Updating crates.io index");
|
||||||
|
let index = crates_index::Index::new_cargo_default();
|
||||||
|
index.retrieve_or_update()?;
|
||||||
|
|
||||||
// Fetch overall crate info
|
// Lookup crate in index
|
||||||
let info = match api_client.get_crate(name.as_ref()).await {
|
debug!("Looking up crate information");
|
||||||
Ok(i) => i,
|
let base_info = match index.crate_(name) {
|
||||||
Err(e) => {
|
Some(i) => i,
|
||||||
error!("Error fetching information for crate {}: {}", name, e);
|
None => {
|
||||||
return Err(e.into())
|
return Err(anyhow::anyhow!("Error fetching information for crate {}", name));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use specified or latest version
|
// Locate matching version
|
||||||
let version_num = match version {
|
let version_iter = base_info.versions().iter().map(|v| v.version() );
|
||||||
Some(v) => v.to_string(),
|
let version_name = find_version(version_req, version_iter)?;
|
||||||
None => info.crate_data.max_version,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fetch crates.io information for the specified version
|
// Build crates.io api client
|
||||||
// Note it is not viable to use a semver match here as crates.io
|
let api_client = AsyncClient::new("cargo-binstall (https://github.com/ryankurte/cargo-binstall)", Duration::from_millis(100))?;
|
||||||
// appears to elide alpha and yanked versions in the generic response...
|
|
||||||
let versions = info.versions.clone();
|
// Fetch online crate information
|
||||||
let version = match versions.iter().find(|v| v.num == version_num) {
|
let crate_info = api_client.get_crate(name.as_ref()).await
|
||||||
|
.context("Error fetching crate information")?;
|
||||||
|
|
||||||
|
// Fetch information for the filtered version
|
||||||
|
let version = match crate_info.versions.iter().find(|v| v.num == version_name) {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => {
|
None => {
|
||||||
error!("No crates.io information found for crate: '{}' version: '{}'",
|
return Err(anyhow::anyhow!("No information found for crate: '{}' version: '{}'",
|
||||||
name, version_num);
|
name, version_name));
|
||||||
return Err(anyhow::anyhow!("No crate information found"));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,7 +103,7 @@ pub async fn fetch_crate_cratesio(name: &str, version: Option<&str>, temp_dir: &
|
||||||
// Decompress downloaded tgz
|
// Decompress downloaded tgz
|
||||||
debug!("Decompressing crate archive");
|
debug!("Decompressing crate archive");
|
||||||
extract(&tgz_path, PkgFmt::Tgz, &temp_dir)?;
|
extract(&tgz_path, PkgFmt::Tgz, &temp_dir)?;
|
||||||
let crate_path = temp_dir.join(format!("{}-{}", name, version_num));
|
let crate_path = temp_dir.join(format!("{}-{}", name, version_name));
|
||||||
|
|
||||||
// Return crate directory
|
// Return crate directory
|
||||||
Ok(crate_path)
|
Ok(crate_path)
|
||||||
|
|
23
src/main.rs
23
src/main.rs
|
@ -18,8 +18,8 @@ struct Options {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
||||||
/// Filter for package version to install
|
/// Filter for package version to install
|
||||||
#[structopt(long)]
|
#[structopt(long, default_value = "*")]
|
||||||
version: Option<String>,
|
version: String,
|
||||||
|
|
||||||
/// Override binary target, ignoring compiled version
|
/// Override binary target, ignoring compiled version
|
||||||
#[structopt(long, default_value = TARGET)]
|
#[structopt(long, default_value = TARGET)]
|
||||||
|
@ -30,14 +30,6 @@ struct Options {
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
install_path: Option<String>,
|
install_path: Option<String>,
|
||||||
|
|
||||||
/// Do not cleanup temporary files on success
|
|
||||||
#[structopt(long)]
|
|
||||||
no_cleanup: bool,
|
|
||||||
|
|
||||||
/// Disable interactive mode / confirmation
|
|
||||||
#[structopt(long)]
|
|
||||||
no_confirm: bool,
|
|
||||||
|
|
||||||
/// Disable symlinking / versioned updates
|
/// Disable symlinking / versioned updates
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
no_symlinks: bool,
|
no_symlinks: bool,
|
||||||
|
@ -46,6 +38,14 @@ struct Options {
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
|
|
||||||
|
/// Disable interactive mode / confirmation
|
||||||
|
#[structopt(long)]
|
||||||
|
no_confirm: bool,
|
||||||
|
|
||||||
|
/// Do not cleanup temporary files on success
|
||||||
|
#[structopt(long)]
|
||||||
|
no_cleanup: bool,
|
||||||
|
|
||||||
/// Override manifest source.
|
/// Override manifest source.
|
||||||
/// This skips searching crates.io for a manifest and uses
|
/// This skips searching crates.io for a manifest and uses
|
||||||
/// the specified path directly, useful for debugging and
|
/// the specified path directly, useful for debugging and
|
||||||
|
@ -59,7 +59,6 @@ struct Options {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), anyhow::Error> {
|
async fn main() -> Result<(), anyhow::Error> {
|
||||||
|
|
||||||
|
@ -91,7 +90,7 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||||
// TODO: support git-based fetches (whole repo name rather than just crate name)
|
// TODO: support git-based fetches (whole repo name rather than just crate name)
|
||||||
let manifest_path = match opts.manifest_path.clone() {
|
let manifest_path = match opts.manifest_path.clone() {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => fetch_crate_cratesio(&opts.name, opts.version.as_deref(), temp_dir.path()).await?,
|
None => fetch_crate_cratesio(&opts.name, &opts.version, temp_dir.path()).await?,
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Reading manifest: {}", manifest_path.display());
|
debug!("Reading manifest: {}", manifest_path.display());
|
||||||
|
|
Loading…
Add table
Reference in a new issue