From 223c6ef43a43c669aecb6caf106fc2e7b4e697f7 Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 30 Dec 2020 18:13:38 +1300 Subject: [PATCH] playing with version matching Can't use semver because crates.io hides alpha versions? not sure how this works in cargo --- src/drivers.rs | 17 +++---- src/lib.rs | 8 +++- src/main.rs | 118 ++++++++++++++++++++----------------------------- 3 files changed, 61 insertions(+), 82 deletions(-) diff --git a/src/drivers.rs b/src/drivers.rs index 41349fc8..cffe9ad0 100644 --- a/src/drivers.rs +++ b/src/drivers.rs @@ -5,7 +5,6 @@ use std::path::{Path, PathBuf}; use log::{debug, error}; use crates_io_api::AsyncClient; -use semver::Version; use crate::PkgFmt; use crate::helpers::*; @@ -13,7 +12,6 @@ use crate::helpers::*; /// 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 { // Build crates.io api client and fetch info - // TODO: support git-based fetches (whole repo name rather than just crate name) let api_client = AsyncClient::new("cargo-binstall (https://github.com/ryankurte/cargo-binstall)", Duration::from_millis(100))?; debug!("Fetching information for crate: '{}'", name); @@ -34,15 +32,9 @@ pub async fn fetch_crate_cratesio(name: &str, version: Option<&str>, temp_dir: & }; // Fetch crates.io information for the specified version - // TODO: Filter by semver matches instead of literal match - let mut versions = info.versions.clone(); - versions.sort_by(|a, b| { - let ver_a = Version::parse(&a.num).unwrap(); - let ver_b = Version::parse(&b.num).unwrap(); - - ver_a.partial_cmp(&ver_b).unwrap() - } ); - + // Note it is not viable to use a semver match here as crates.io + // appears to elide alpha and yanked versions in the generic response... + let versions = info.versions.clone(); let version = match versions.iter().find(|v| v.num == version_num) { Some(v) => v, None => { @@ -77,4 +69,5 @@ pub async fn fetch_crate_cratesio(name: &str, version: Option<&str>, temp_dir: & pub async fn fetch_crate_gh_releases(_name: &str, _version: Option<&str>, _temp_dir: &Path) -> Result { unimplemented!(); -} \ No newline at end of file +} + diff --git a/src/lib.rs b/src/lib.rs index 6459fd64..30b63cbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,11 @@ pub enum PkgFmt { Bin, } +impl Default for PkgFmt { + fn default() -> Self { + Self::Tgz + } +} /// Metadata for binary installation use. /// @@ -49,6 +54,7 @@ pub struct Meta { pub pkg_name: Option, /// Format override for package downloads + #[serde(default)] pub pkg_fmt: Option, #[serde(default)] @@ -56,7 +62,7 @@ pub struct Meta { pub pkg_bins: Vec, /// Public key for package verification (base64 encoded) - pub pkg_pub_key: Option, + pub pub_key: Option, } /// Template for constructing download paths diff --git a/src/main.rs b/src/main.rs index 598bdc1d..e57938f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ struct Options { #[structopt()] name: String, - /// Package version to instal + /// Package version to install #[structopt(long)] version: Option, @@ -32,9 +32,6 @@ struct Options { #[structopt(long)] install_path: Option, - #[structopt(flatten)] - overrides: Overrides, - /// Do not cleanup temporary files on success #[structopt(long)] no_cleanup: bool, @@ -47,41 +44,18 @@ struct Options { #[structopt(long)] no_symlinks: bool, + /// Override manifest source. + /// This skips searching crates.io for a manifest and uses + /// the specified path directly, useful for debugging and + /// when adding `binstall` support. + #[structopt(long)] + manifest_path: Option, + /// Utility log level #[structopt(long, default_value = "info")] log_level: LevelFilter, } -#[derive(Debug, StructOpt)] -pub struct Overrides { - - /// Override the package name. - /// This is only useful for diagnostics when using the default `pkg_url` - /// as you can otherwise customise this in the path. - /// Defaults to the crate name. - #[structopt(long)] - pkg_name: Option, - - /// Override the package path template. - /// If no `metadata.pkg_url` key is set or `--pkg-url` argument provided, this - /// defaults to `{ repo }/releases/download/v{ version }/{ name }-{ target }-v{ version }.tgz` - #[structopt(long)] - pkg_url: Option, - - /// Override format for binary file download. - /// Defaults to `tgz` - #[structopt(long)] - pkg_fmt: Option, - - /// Override manifest source. - /// This skips searching crates.io for a manifest and uses - /// the specified path directly, useful for debugging - #[structopt(long)] - manifest_path: Option, -} - - - #[tokio::main] async fn main() -> Result<(), anyhow::Error> { @@ -106,11 +80,18 @@ async fn main() -> Result<(), anyhow::Error> { // Create a temporary directory for downloads etc. let temp_dir = TempDir::new("cargo-binstall")?; + info!("Installing package: '{}'", opts.name); + // Fetch crate via crates.io, git, or use a local manifest path // TODO: work out which of these to do based on `opts.name` - let crate_path = match opts.overrides.manifest_path { - Some(p) => p, - None => fetch_crate_cratesio(&opts.name, opts.version.as_deref(), temp_dir.path()).await?, + // TODO: support git-based fetches (whole repo name rather than just crate name) + let crate_path = match opts.manifest_path { + Some(p) => { + p + }, + None => { + fetch_crate_cratesio(&opts.name, opts.version.as_deref(), temp_dir.path()).await? + }, }; // Read cargo manifest @@ -129,37 +110,31 @@ async fn main() -> Result<(), anyhow::Error> { debug!("Retrieved metadata: {:?}", meta); // Select which package path to use - let pkg_url = match (opts.overrides.pkg_url, meta.as_ref().map(|m| m.pkg_url.clone() ).flatten()) { - (Some(p), _) => { - info!("Using package url override: '{}'", p); - p - }, - (_, Some(m)) => { - info!("Using package url: '{}'", &m); + let pkg_url = match meta.as_ref().map(|m| m.pkg_url.clone() ).flatten() { + Some(m) => { + debug!("Using package url: '{}'", &m); m }, _ => { - info!("No `pkg-url` key found in Cargo.toml or `--pkg-url` argument provided"); - info!("Using default url: {}", DEFAULT_PKG_PATH); + debug!("No `pkg-url` key found in Cargo.toml or `--pkg-url` argument provided"); + debug!("Using default url: {}", DEFAULT_PKG_PATH); DEFAULT_PKG_PATH.to_string() }, }; // Select bin format to use - let pkg_fmt = match (opts.overrides.pkg_fmt, meta.as_ref().map(|m| m.pkg_fmt.clone() ).flatten()) { - (Some(o), _) => o, - (_, Some(m)) => m.clone(), + let pkg_fmt = match meta.as_ref().map(|m| m.pkg_fmt.clone() ).flatten() { + Some(m) => m.clone(), _ => PkgFmt::Tgz, }; // Override package name if required - let pkg_name = match (&opts.overrides.pkg_name, meta.as_ref().map(|m| m.pkg_name.clone() ).flatten()) { - (Some(o), _) => o.clone(), - (_, Some(m)) => m, + let pkg_name = match meta.as_ref().map(|m| m.pkg_name.clone() ).flatten() { + Some(m) => m, _ => opts.name.clone(), }; - // Generate context for interpolation + // Generate context for URL interpolation let ctx = Context { name: pkg_name.to_string(), repo: package.repository, @@ -190,26 +165,29 @@ async fn main() -> Result<(), anyhow::Error> { let pkg_path = temp_dir.path().join(format!("pkg-{}.{}", pkg_name, pkg_fmt)); download(&rendered, pkg_path.to_str().unwrap()).await?; - // Fetch and check package signature if available - if let Some(pub_key) = meta.as_ref().map(|m| m.pkg_pub_key.clone() ).flatten() { - debug!("Found public key: {}", pub_key); + #[cfg(incomplete)] + { + // Fetch and check package signature if available + if let Some(pub_key) = meta.as_ref().map(|m| m.pub_key.clone() ).flatten() { + debug!("Found public key: {}", pub_key); - // Generate signature file URL - let mut sig_ctx = ctx.clone(); - sig_ctx.format = "sig".to_string(); - let sig_url = sig_ctx.render(&pkg_url)?; + // Generate signature file URL + let mut sig_ctx = ctx.clone(); + sig_ctx.format = "sig".to_string(); + let sig_url = sig_ctx.render(&pkg_url)?; - debug!("Fetching signature file: {}", sig_url); + debug!("Fetching signature file: {}", sig_url); - // Download signature file - let sig_path = temp_dir.path().join(format!("{}.sig", pkg_name)); - download(&sig_url, &sig_path).await?; + // Download signature file + let sig_path = temp_dir.path().join(format!("{}.sig", pkg_name)); + download(&sig_url, &sig_path).await?; - // TODO: do the signature check - unimplemented!() + // TODO: do the signature check + unimplemented!() - } else { - warn!("No public key found, package signature could not be validated"); + } else { + warn!("No public key found, package signature could not be validated"); + } } // Extract files @@ -230,7 +208,7 @@ async fn main() -> Result<(), anyhow::Error> { let name = source.file_name().map(|v| v.to_str()).flatten().unwrap().to_string(); // Trim target and version from name if included in binary file name - let base_name = name.replace(&format!("-{}", ctx.target), "") + let base_name = name.replace(&format!("-{}", TARGET), "") .replace(&format!("-v{}", ctx.version), "") .replace(&format!("-{}", ctx.version), ""); @@ -262,6 +240,8 @@ async fn main() -> Result<(), anyhow::Error> { return Ok(()) } + info!("Installing binaries..."); + // Install binaries for (_name, source, dest, _link) in &bin_files { // TODO: check if file already exists