mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-20 12:38:43 +00:00
playing with version matching
Can't use semver because crates.io hides alpha versions? not sure how this works in cargo
This commit is contained in:
parent
8777c355c5
commit
223c6ef43a
3 changed files with 61 additions and 82 deletions
|
@ -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<PathBuf, anyhow::Error> {
|
||||
// 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 => {
|
||||
|
@ -78,3 +70,4 @@ pub async fn fetch_crate_gh_releases(_name: &str, _version: Option<&str>, _temp_
|
|||
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String>,
|
||||
|
||||
/// Format override for package downloads
|
||||
#[serde(default)]
|
||||
pub pkg_fmt: Option<PkgFmt>,
|
||||
|
||||
#[serde(default)]
|
||||
|
@ -56,7 +62,7 @@ pub struct Meta {
|
|||
pub pkg_bins: Vec<String>,
|
||||
|
||||
/// Public key for package verification (base64 encoded)
|
||||
pub pkg_pub_key: Option<String>,
|
||||
pub pub_key: Option<String>,
|
||||
}
|
||||
|
||||
/// Template for constructing download paths
|
||||
|
|
90
src/main.rs
90
src/main.rs
|
@ -19,7 +19,7 @@ struct Options {
|
|||
#[structopt()]
|
||||
name: String,
|
||||
|
||||
/// Package version to instal
|
||||
/// Package version to install
|
||||
#[structopt(long)]
|
||||
version: Option<String>,
|
||||
|
||||
|
@ -32,9 +32,6 @@ struct Options {
|
|||
#[structopt(long)]
|
||||
install_path: Option<String>,
|
||||
|
||||
#[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<PathBuf>,
|
||||
|
||||
/// 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<String>,
|
||||
|
||||
/// 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<String>,
|
||||
|
||||
/// Override format for binary file download.
|
||||
/// Defaults to `tgz`
|
||||
#[structopt(long)]
|
||||
pkg_fmt: Option<PkgFmt>,
|
||||
|
||||
/// 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<PathBuf>,
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[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,8 +165,10 @@ 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?;
|
||||
|
||||
#[cfg(incomplete)]
|
||||
{
|
||||
// Fetch and check package signature if available
|
||||
if let Some(pub_key) = meta.as_ref().map(|m| m.pkg_pub_key.clone() ).flatten() {
|
||||
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
|
||||
|
@ -211,6 +188,7 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||
} else {
|
||||
warn!("No public key found, package signature could not be validated");
|
||||
}
|
||||
}
|
||||
|
||||
// Extract files
|
||||
let bin_path = temp_dir.path().join(format!("bin-{}", pkg_name));
|
||||
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue