diff --git a/Cargo.lock b/Cargo.lock index ab3fbb7580c..8e32c3d0736 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3702,6 +3702,7 @@ dependencies = [ "cargo", "clap 4.2.1", "env_logger 0.10.0", + "log", ] [[package]] diff --git a/crates/xtask/Cargo.toml b/crates/xtask/Cargo.toml index 8af48c88cfa..ffe4cafbe37 100644 --- a/crates/xtask/Cargo.toml +++ b/crates/xtask/Cargo.toml @@ -6,6 +6,7 @@ publish = false [dependencies] anyhow = "1.0.47" -cargo = { version = "0.71.0", path = "../.." } +cargo = { path = "../.." } clap = "4.2.0" env_logger = "0.10.0" +log = "0.4.17" diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs index 1942a3621cf..9edb3c3d72a 100644 --- a/crates/xtask/src/main.rs +++ b/crates/xtask/src/main.rs @@ -1,3 +1,4 @@ +mod unpublished; mod xtask; fn main() { diff --git a/crates/xtask/src/unpublished.rs b/crates/xtask/src/unpublished.rs new file mode 100644 index 00000000000..7c1f44af41d --- /dev/null +++ b/crates/xtask/src/unpublished.rs @@ -0,0 +1,86 @@ +use cargo::core::registry::PackageRegistry; +use cargo::core::QueryKind; +use cargo::core::Registry; +use cargo::core::SourceId; +use cargo::util::command_prelude::*; + +pub fn cli() -> clap::Command { + clap::Command::new("unpublished") +} + +pub fn exec(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult { + let ws = args.workspace(config)?; + let mut results = Vec::new(); + { + let mut registry = PackageRegistry::new(config)?; + let _lock = config.acquire_package_cache_lock()?; + registry.lock_patches(); + let source_id = SourceId::crates_io(config)?; + + for member in ws.members() { + let name = member.name(); + let current = member.version(); + if member.publish() == &Some(vec![]) { + log::trace!("skipping {name}, `publish = false`"); + continue; + } + + let version_req = format!("<={current}"); + let query = cargo::core::dependency::Dependency::parse( + name, + Some(&version_req), + source_id.clone(), + )?; + let possibilities = loop { + // Exact to avoid returning all for path/git + match registry.query_vec(&query, QueryKind::Exact) { + std::task::Poll::Ready(res) => { + break res?; + } + std::task::Poll::Pending => registry.block_until_ready()?, + } + }; + if let Some(last) = possibilities.iter().map(|s| s.version()).max() { + if last != current { + results.push(( + name.to_string(), + Some(last.to_string()), + current.to_string(), + )); + } else { + log::trace!("{name} {current} is published"); + } + } else { + results.push((name.to_string(), None, current.to_string())); + } + } + } + + if !results.is_empty() { + results.insert( + 0, + ( + "name".to_owned(), + Some("published".to_owned()), + "current".to_owned(), + ), + ); + results.insert( + 1, + ( + "====".to_owned(), + Some("=========".to_owned()), + "=======".to_owned(), + ), + ); + } + for (name, last, current) in results { + if let Some(last) = last { + println!("{name} {last} {current}"); + } else { + println!("{name} - {current}"); + } + } + + Ok(()) +} diff --git a/crates/xtask/src/xtask.rs b/crates/xtask/src/xtask.rs index 00541358b17..1d6948fc7d7 100644 --- a/crates/xtask/src/xtask.rs +++ b/crates/xtask/src/xtask.rs @@ -2,6 +2,8 @@ use cargo::util::command_prelude::*; pub fn cli() -> clap::Command { clap::Command::new("xtask") + .subcommand_required(true) + .arg_required_else_help(true) .arg( opt( "verbose", @@ -29,11 +31,18 @@ pub fn cli() -> clap::Command { .action(ArgAction::Append) .global(true), ) + .subcommands([crate::unpublished::cli()]) } pub fn exec(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult { config_configure(config, args)?; + match args.subcommand() { + Some(("unpublished", args)) => crate::unpublished::exec(args, config)?, + Some((name, _)) => unreachable!("clap enforces {name} to not exist"), + None => unreachable!("clap enforces a subcommand is present"), + } + Ok(()) } @@ -67,3 +76,8 @@ fn config_configure(config: &mut Config, args: &ArgMatches) -> CliResult { )?; Ok(()) } + +#[test] +fn verify_cli() { + cli().debug_assert(); +}