Skip to content

Commit 8bcf8fe

Browse files
committed
Auto merge of #12039 - epage:xtask, r=weihanglo
chore(xtask): Add `cargo xtask unpublished` ### What does this PR try to resolve? This tries to make it easy to see what existing versions have not been published. A future version of this could post to a PR what the current delta in version numbers for touched crates so reviewer have more context when deciding if they should ask for a crate version to be bumped ```console $ cargo unpublished Finished dev [unoptimized + debuginfo] target(s) in 0.12s Running `/home/epage/src/personal/cargo/target/debug/xtask unpublished` Updating crates.io index name published current ==== ========= ======= cargo-test-macro - 0.1.0 cargo-test-support - 0.1.0 cargo-util 0.2.3 0.2.4 mdman 0.0.0 0.1.0 resolver-tests - 0.1.0 cargo 0.70.0 0.71.0 cargo-credential 0.1.0 0.2.0 cargo-credential-1password 0.1.0 0.2.0 cargo-credential-gnome-secret 0.1.0 0.2.0 cargo-credential-macos-keychain 0.1.0 0.2.0 cargo-credential-wincred 0.1.0 0.2.0 benchsuite - 0.1.0 ``` Room for improvement - Aligning the start of each column - Filtering the list by a commit range - Adding this to an action to post to a review - Maybe sorting the output - Marking some our crates as `package.publish = false`, like benchsuite and resolver-tests ### How should we test and review this PR? This is broken down commit by commit for easier seeing of the building blocks for our first xtask
2 parents 284fd3f + 5b13963 commit 8bcf8fe

File tree

6 files changed

+197
-1
lines changed

6 files changed

+197
-1
lines changed

.cargo/config.toml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[alias]
2+
unpublished = "run --package xtask-unpublished --"

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
target
22
Cargo.lock
3-
.cargo
43
/config.stamp
54
/Makefile
65
/config.mk

Cargo.lock

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/xtask-unpublished/Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "xtask-unpublished"
3+
version = "0.0.0"
4+
edition = "2021"
5+
publish = false
6+
7+
[dependencies]
8+
anyhow = "1.0.47"
9+
cargo = { path = "../.." }
10+
clap = "4.2.0"
11+
env_logger = "0.10.0"
12+
log = "0.4.17"

crates/xtask-unpublished/src/main.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
mod xtask;
2+
3+
fn main() {
4+
env_logger::init_from_env("CARGO_LOG");
5+
let cli = xtask::cli();
6+
let matches = cli.get_matches();
7+
8+
let mut config = cargo::util::config::Config::default().unwrap_or_else(|e| {
9+
let mut eval = cargo::core::shell::Shell::new();
10+
cargo::exit_with_error(e.into(), &mut eval)
11+
});
12+
if let Err(e) = xtask::exec(&matches, &mut config) {
13+
cargo::exit_with_error(e, &mut config.shell())
14+
}
15+
}

crates/xtask-unpublished/src/xtask.rs

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
use cargo::core::registry::PackageRegistry;
2+
use cargo::core::QueryKind;
3+
use cargo::core::Registry;
4+
use cargo::core::SourceId;
5+
use cargo::util::command_prelude::*;
6+
7+
pub fn cli() -> clap::Command {
8+
clap::Command::new("xtask-unpublished")
9+
.arg(
10+
opt(
11+
"verbose",
12+
"Use verbose output (-vv very verbose/build.rs output)",
13+
)
14+
.short('v')
15+
.action(ArgAction::Count)
16+
.global(true),
17+
)
18+
.arg_quiet()
19+
.arg(
20+
opt("color", "Coloring: auto, always, never")
21+
.value_name("WHEN")
22+
.global(true),
23+
)
24+
.arg(flag("frozen", "Require Cargo.lock and cache are up to date").global(true))
25+
.arg(flag("locked", "Require Cargo.lock is up to date").global(true))
26+
.arg(flag("offline", "Run without accessing the network").global(true))
27+
.arg(multi_opt("config", "KEY=VALUE", "Override a configuration value").global(true))
28+
.arg(
29+
Arg::new("unstable-features")
30+
.help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
31+
.short('Z')
32+
.value_name("FLAG")
33+
.action(ArgAction::Append)
34+
.global(true),
35+
)
36+
}
37+
38+
pub fn exec(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult {
39+
config_configure(config, args)?;
40+
41+
unpublished(args, config)?;
42+
43+
Ok(())
44+
}
45+
46+
fn config_configure(config: &mut Config, args: &ArgMatches) -> CliResult {
47+
let verbose = args.verbose();
48+
// quiet is unusual because it is redefined in some subcommands in order
49+
// to provide custom help text.
50+
let quiet = args.flag("quiet");
51+
let color = args.get_one::<String>("color").map(String::as_str);
52+
let frozen = args.flag("frozen");
53+
let locked = args.flag("locked");
54+
let offline = args.flag("offline");
55+
let mut unstable_flags = vec![];
56+
if let Some(values) = args.get_many::<String>("unstable-features") {
57+
unstable_flags.extend(values.cloned());
58+
}
59+
let mut config_args = vec![];
60+
if let Some(values) = args.get_many::<String>("config") {
61+
config_args.extend(values.cloned());
62+
}
63+
config.configure(
64+
verbose,
65+
quiet,
66+
color,
67+
frozen,
68+
locked,
69+
offline,
70+
&None,
71+
&unstable_flags,
72+
&config_args,
73+
)?;
74+
Ok(())
75+
}
76+
77+
fn unpublished(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult {
78+
let ws = args.workspace(config)?;
79+
let mut results = Vec::new();
80+
{
81+
let mut registry = PackageRegistry::new(config)?;
82+
let _lock = config.acquire_package_cache_lock()?;
83+
registry.lock_patches();
84+
let source_id = SourceId::crates_io(config)?;
85+
86+
for member in ws.members() {
87+
let name = member.name();
88+
let current = member.version();
89+
if member.publish() == &Some(vec![]) {
90+
log::trace!("skipping {name}, `publish = false`");
91+
continue;
92+
}
93+
94+
let version_req = format!("<={current}");
95+
let query = cargo::core::dependency::Dependency::parse(
96+
name,
97+
Some(&version_req),
98+
source_id.clone(),
99+
)?;
100+
let possibilities = loop {
101+
// Exact to avoid returning all for path/git
102+
match registry.query_vec(&query, QueryKind::Exact) {
103+
std::task::Poll::Ready(res) => {
104+
break res?;
105+
}
106+
std::task::Poll::Pending => registry.block_until_ready()?,
107+
}
108+
};
109+
if let Some(last) = possibilities.iter().map(|s| s.version()).max() {
110+
if last != current {
111+
results.push((
112+
name.to_string(),
113+
Some(last.to_string()),
114+
current.to_string(),
115+
));
116+
} else {
117+
log::trace!("{name} {current} is published");
118+
}
119+
} else {
120+
results.push((name.to_string(), None, current.to_string()));
121+
}
122+
}
123+
}
124+
125+
if !results.is_empty() {
126+
results.insert(
127+
0,
128+
(
129+
"name".to_owned(),
130+
Some("published".to_owned()),
131+
"current".to_owned(),
132+
),
133+
);
134+
results.insert(
135+
1,
136+
(
137+
"====".to_owned(),
138+
Some("=========".to_owned()),
139+
"=======".to_owned(),
140+
),
141+
);
142+
}
143+
for (name, last, current) in results {
144+
if let Some(last) = last {
145+
println!("{name} {last} {current}");
146+
} else {
147+
println!("{name} - {current}");
148+
}
149+
}
150+
151+
Ok(())
152+
}
153+
154+
#[test]
155+
fn verify_cli() {
156+
cli().debug_assert();
157+
}

0 commit comments

Comments
 (0)