feat: parse Cargo manifest and cmp version against Crates.io index
This commit is contained in:
parent
b225153dad
commit
befc7ba705
3 changed files with 1620 additions and 0 deletions
1484
Cargo.lock
generated
Normal file
1484
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
18
Cargo.toml
Normal file
18
Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "deps-manager"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.4.6", features = ["derive"] }
|
||||||
|
log = "0.4.20"
|
||||||
|
pretty_env_logger = "0.5.0"
|
||||||
|
reqwest = { version = "0.11.22", features = ["json", "gzip"] }
|
||||||
|
semver = { version = "1.0.20", features = ["serde"] }
|
||||||
|
serde = { version = "1.0.189", features = ["derive"] }
|
||||||
|
serde_json = "1.0.107"
|
||||||
|
tokio = { version = "1.33.0", features = ["rt", "rt-multi-thread", "macros"] }
|
||||||
|
tokio-rustls = "0.24.1"
|
||||||
|
toml = "0.8.2"
|
118
src/main.rs
Normal file
118
src/main.rs
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use std::{fs, path::PathBuf};
|
||||||
|
|
||||||
|
use clap::*;
|
||||||
|
use log::*;
|
||||||
|
use semver::Version;
|
||||||
|
use semver::VersionReq;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[clap(author, version, about, long_about = None)]
|
||||||
|
struct CLI {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_logger() {
|
||||||
|
// if std::env::var("RUST_LOG").is_err() {
|
||||||
|
// std::env::set_var("RUST_LOG", "info");
|
||||||
|
// }
|
||||||
|
pretty_env_logger::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip_entry(name: &str, v: &toml::Value) -> bool {
|
||||||
|
if v.is_table() {
|
||||||
|
if v.get("git").is_some() {
|
||||||
|
info!("[{name}] Skipping, Found Git src: {:?}", v);
|
||||||
|
return true;
|
||||||
|
} else if v.get("path").is_some() {
|
||||||
|
info!("[{name}] Skipping, Found local dep: {:?}", v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn find_latest_version(c: &reqwest::Client, name: &str) -> Version {
|
||||||
|
let url = if name.len() == 1 {
|
||||||
|
format!("https://index.crates.io/1/{}", name)
|
||||||
|
} else if name.len() == 2 {
|
||||||
|
format!("https://index.crates.io/2/{}", name)
|
||||||
|
} else if name.len() == 3 {
|
||||||
|
format!("https://index.crates.io/3/{}/{}", &name[0..1], name)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"https://index.crates.io/{}/{}/{}",
|
||||||
|
&name[0..2].to_lowercase(),
|
||||||
|
&name[2..4].to_lowercase(),
|
||||||
|
name
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let resp = c.get(&url).send().await.unwrap();
|
||||||
|
let text = resp.text().await.unwrap();
|
||||||
|
|
||||||
|
let mut latest = Version::parse("0.0.0").unwrap();
|
||||||
|
for line in text.lines() {
|
||||||
|
if line.trim().is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let this: serde_json::Value =
|
||||||
|
serde_json::from_str(&line).unwrap_or_else(|_| panic!("{}", line));
|
||||||
|
let vers = this.get("vers").unwrap();
|
||||||
|
let vers = Version::parse(vers.as_str().unwrap()).unwrap();
|
||||||
|
if vers > latest {
|
||||||
|
latest = vers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
latest
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
init_logger();
|
||||||
|
|
||||||
|
let opts = CLI::parse();
|
||||||
|
let contents = fs::read_to_string(opts.path).unwrap();
|
||||||
|
let manifest: toml::Value = toml::from_str(&contents).unwrap();
|
||||||
|
let c =
|
||||||
|
reqwest::Client::builder();
|
||||||
|
let c = c.build().unwrap();
|
||||||
|
|
||||||
|
let mut updates = Vec::default();
|
||||||
|
|
||||||
|
if let Some(toml::Value::Table(deps)) = manifest.get("dependencies") {
|
||||||
|
for d in deps.iter() {
|
||||||
|
let name = d.0.as_str();
|
||||||
|
|
||||||
|
if skip_entry(name, d.1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let using = if d.1.is_str() {
|
||||||
|
d.1.as_str().unwrap()
|
||||||
|
} else {
|
||||||
|
d.1.get("version")
|
||||||
|
.unwrap_or_else(|| panic!("version not found: {:?}", d))
|
||||||
|
.as_str()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
let using = VersionReq::parse(using).unwrap();
|
||||||
|
debug!("name: {name} version: {using}");
|
||||||
|
|
||||||
|
let latest = find_latest_version(&c, name).await;
|
||||||
|
|
||||||
|
info!("{name} using: {using} latest {latest}");
|
||||||
|
// since remove version can only increase, failing to match can be assumed to be the
|
||||||
|
// same as "new version available"
|
||||||
|
if !using.matches(&latest) {
|
||||||
|
updates.push((name, (using, latest)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (name, (using, latest)) in updates.iter() {
|
||||||
|
println!("[{name}] {using} < {latest}")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue