summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo/crates
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
commitd1b2d29528b7794b41e66fc2136e395a02f8529b (patch)
treea4a17504b260206dec3cf55b2dca82929a348ac2 /src/tools/cargo/crates
parentReleasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz
rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/cargo/crates')
-rw-r--r--src/tools/cargo/crates/cargo-platform/Cargo.toml6
-rw-r--r--src/tools/cargo/crates/cargo-test-macro/Cargo.toml4
-rw-r--r--src/tools/cargo/crates/cargo-test-support/Cargo.toml4
-rw-r--r--src/tools/cargo/crates/cargo-test-support/containers/sshd/Dockerfile2
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/compare.rs1
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/registry.rs36
-rw-r--r--src/tools/cargo/crates/cargo-util/Cargo.toml8
-rw-r--r--src/tools/cargo/crates/cargo-util/src/paths.rs20
-rw-r--r--src/tools/cargo/crates/cargo-util/src/process_builder.rs4
-rw-r--r--src/tools/cargo/crates/crates-io/Cargo.toml8
-rw-r--r--src/tools/cargo/crates/crates-io/lib.rs167
-rw-r--r--src/tools/cargo/crates/home/Cargo.toml7
-rw-r--r--src/tools/cargo/crates/mdman/Cargo.toml4
-rw-r--r--src/tools/cargo/crates/resolver-tests/Cargo.toml2
-rw-r--r--src/tools/cargo/crates/semver-check/Cargo.toml2
-rw-r--r--src/tools/cargo/crates/semver-check/src/main.rs13
-rw-r--r--src/tools/cargo/crates/xtask-build-man/Cargo.toml2
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/Cargo.toml14
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/src/main.rs (renamed from src/tools/cargo/crates/xtask-unpublished/src/main.rs)14
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/src/xtask.rs423
-rw-r--r--src/tools/cargo/crates/xtask-stale-label/Cargo.toml2
-rw-r--r--src/tools/cargo/crates/xtask-stale-label/src/main.rs2
-rw-r--r--src/tools/cargo/crates/xtask-unpublished/Cargo.toml12
-rw-r--r--src/tools/cargo/crates/xtask-unpublished/src/xtask.rs200
24 files changed, 616 insertions, 341 deletions
diff --git a/src/tools/cargo/crates/cargo-platform/Cargo.toml b/src/tools/cargo/crates/cargo-platform/Cargo.toml
index 423cf491d..e7f22cf87 100644
--- a/src/tools/cargo/crates/cargo-platform/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-platform/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "cargo-platform"
-version = "0.1.3"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+version = "0.1.4"
+edition.workspace = true
+license.workspace = true
homepage = "https://github.com/rust-lang/cargo"
repository = "https://github.com/rust-lang/cargo"
documentation = "https://docs.rs/cargo-platform"
diff --git a/src/tools/cargo/crates/cargo-test-macro/Cargo.toml b/src/tools/cargo/crates/cargo-test-macro/Cargo.toml
index e40602ae3..b5da0522f 100644
--- a/src/tools/cargo/crates/cargo-test-macro/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-test-macro/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "cargo-test-macro"
version = "0.1.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+edition.workspace = true
+license.workspace = true
homepage = "https://github.com/rust-lang/cargo"
repository = "https://github.com/rust-lang/cargo"
documentation = "https://github.com/rust-lang/cargo"
diff --git a/src/tools/cargo/crates/cargo-test-support/Cargo.toml b/src/tools/cargo/crates/cargo-test-support/Cargo.toml
index 305c809a8..085041aff 100644
--- a/src/tools/cargo/crates/cargo-test-support/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-test-support/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "cargo-test-support"
version = "0.1.0"
-license = "MIT OR Apache-2.0"
-edition = "2021"
+license.workspace = true
+edition.workspace = true
publish = false
[lib]
diff --git a/src/tools/cargo/crates/cargo-test-support/containers/sshd/Dockerfile b/src/tools/cargo/crates/cargo-test-support/containers/sshd/Dockerfile
index b52eefbad..f25212770 100644
--- a/src/tools/cargo/crates/cargo-test-support/containers/sshd/Dockerfile
+++ b/src/tools/cargo/crates/cargo-test-support/containers/sshd/Dockerfile
@@ -1,4 +1,4 @@
-FROM alpine:3.17
+FROM alpine:3.18
RUN apk add --no-cache openssh git
RUN ssh-keygen -A
diff --git a/src/tools/cargo/crates/cargo-test-support/src/compare.rs b/src/tools/cargo/crates/cargo-test-support/src/compare.rs
index 96ce52afc..21eb64d28 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/compare.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/compare.rs
@@ -192,6 +192,7 @@ fn substitute_macros(input: &str) -> String {
("[CHECKING]", " Checking"),
("[COMPLETED]", " Completed"),
("[CREATED]", " Created"),
+ ("[CREDENTIAL]", " Credential"),
("[DOWNGRADING]", " Downgrading"),
("[FINISHED]", " Finished"),
("[ERROR]", "error:"),
diff --git a/src/tools/cargo/crates/cargo-test-support/src/registry.rs b/src/tools/cargo/crates/cargo-test-support/src/registry.rs
index 910f95bfa..27c319656 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/registry.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/registry.rs
@@ -104,6 +104,8 @@ pub struct RegistryBuilder {
not_found_handler: RequestCallback,
/// If nonzero, the git index update to be delayed by the given number of seconds.
delayed_index_update: usize,
+ /// Credential provider in configuration
+ credential_provider: Option<String>,
}
pub struct TestRegistry {
@@ -172,6 +174,7 @@ impl RegistryBuilder {
custom_responders: HashMap::new(),
not_found_handler: Box::new(not_found),
delayed_index_update: 0,
+ credential_provider: None,
}
}
@@ -266,6 +269,13 @@ impl RegistryBuilder {
self
}
+ /// The credential provider to configure for this registry.
+ #[must_use]
+ pub fn credential_provider(mut self, provider: &[&str]) -> Self {
+ self.credential_provider = Some(format!("['{}']", provider.join("','")));
+ self
+ }
+
/// Initializes the registry.
#[must_use]
pub fn build(self) -> TestRegistry {
@@ -336,6 +346,18 @@ impl RegistryBuilder {
.as_bytes(),
)
.unwrap();
+ if let Some(p) = &self.credential_provider {
+ append(
+ &config_path,
+ &format!(
+ "
+ credential-provider = {p}
+ "
+ )
+ .as_bytes(),
+ )
+ .unwrap()
+ }
} else {
append(
&config_path,
@@ -351,6 +373,20 @@ impl RegistryBuilder {
.as_bytes(),
)
.unwrap();
+
+ if let Some(p) = &self.credential_provider {
+ append(
+ &config_path,
+ &format!(
+ "
+ [registry]
+ credential-provider = {p}
+ "
+ )
+ .as_bytes(),
+ )
+ .unwrap()
+ }
}
}
diff --git a/src/tools/cargo/crates/cargo-util/Cargo.toml b/src/tools/cargo/crates/cargo-util/Cargo.toml
index 614581037..99a59422d 100644
--- a/src/tools/cargo/crates/cargo-util/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-util/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "cargo-util"
-version = "0.2.5"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+version = "0.2.6"
+edition.workspace = true
+license.workspace = true
homepage = "https://github.com/rust-lang/cargo"
repository = "https://github.com/rust-lang/cargo"
description = "Miscellaneous support code used by Cargo."
@@ -14,10 +14,10 @@ filetime.workspace = true
hex.workspace = true
jobserver.workspace = true
libc.workspace = true
-log.workspace = true
same-file.workspace = true
shell-escape.workspace = true
tempfile.workspace = true
+tracing.workspace = true
walkdir.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
diff --git a/src/tools/cargo/crates/cargo-util/src/paths.rs b/src/tools/cargo/crates/cargo-util/src/paths.rs
index 4a917821b..ce6755859 100644
--- a/src/tools/cargo/crates/cargo-util/src/paths.rs
+++ b/src/tools/cargo/crates/cargo-util/src/paths.rs
@@ -237,7 +237,7 @@ pub fn mtime_recursive(path: &Path) -> Result<FileTime> {
Err(e) => {
// Ignore errors while walking. If Cargo can't access it, the
// build script probably can't access it, either.
- log::debug!("failed to determine mtime while walking directory: {}", e);
+ tracing::debug!("failed to determine mtime while walking directory: {}", e);
None
}
})
@@ -252,7 +252,7 @@ pub fn mtime_recursive(path: &Path) -> Result<FileTime> {
// I'm not sure when this is really possible (maybe a
// race with unlinking?). Regardless, if Cargo can't
// read it, the build script probably can't either.
- log::debug!(
+ tracing::debug!(
"failed to determine mtime while fetching symlink metadata of {}: {}",
e.path().display(),
err
@@ -271,7 +271,7 @@ pub fn mtime_recursive(path: &Path) -> Result<FileTime> {
// Can't access the symlink target. If Cargo can't
// access it, the build script probably can't access
// it either.
- log::debug!(
+ tracing::debug!(
"failed to determine mtime of symlink target for {}: {}",
e.path().display(),
err
@@ -286,7 +286,7 @@ pub fn mtime_recursive(path: &Path) -> Result<FileTime> {
// I'm not sure when this is really possible (maybe a
// race with unlinking?). Regardless, if Cargo can't
// read it, the build script probably can't either.
- log::debug!(
+ tracing::debug!(
"failed to determine mtime while fetching metadata of {}: {}",
e.path().display(),
err
@@ -314,7 +314,7 @@ pub fn set_invocation_time(path: &Path) -> Result<FileTime> {
"This file has an mtime of when this was started.",
)?;
let ft = mtime(&timestamp)?;
- log::debug!("invocation time for {:?} is {}", path, ft);
+ tracing::debug!("invocation time for {:?} is {}", path, ft);
Ok(ft)
}
@@ -508,7 +508,7 @@ pub fn link_or_copy(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()>
}
fn _link_or_copy(src: &Path, dst: &Path) -> Result<()> {
- log::debug!("linking {} to {}", src.display(), dst.display());
+ tracing::debug!("linking {} to {}", src.display(), dst.display());
if same_file::is_same_file(src, dst).unwrap_or(false) {
return Ok(());
}
@@ -567,7 +567,7 @@ fn _link_or_copy(src: &Path, dst: &Path) -> Result<()> {
};
link_result
.or_else(|err| {
- log::debug!("link failed {}. falling back to fs::copy", err);
+ tracing::debug!("link failed {}. falling back to fs::copy", err);
fs::copy(src, dst).map(|_| ())
})
.with_context(|| {
@@ -598,8 +598,8 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<u64> {
pub fn set_file_time_no_err<P: AsRef<Path>>(path: P, time: FileTime) {
let path = path.as_ref();
match filetime::set_file_times(path, time, time) {
- Ok(()) => log::debug!("set file mtime {} to {}", path.display(), time),
- Err(e) => log::warn!(
+ Ok(()) => tracing::debug!("set file mtime {} to {}", path.display(), time),
+ Err(e) => tracing::warn!(
"could not set mtime of {} to {}: {:?}",
path.display(),
time,
@@ -621,7 +621,7 @@ pub fn strip_prefix_canonical<P: AsRef<Path>>(
let safe_canonicalize = |path: &Path| match path.canonicalize() {
Ok(p) => p,
Err(e) => {
- log::warn!("cannot canonicalize {:?}: {:?}", path, e);
+ tracing::warn!("cannot canonicalize {:?}: {:?}", path, e);
path.to_path_buf()
}
};
diff --git a/src/tools/cargo/crates/cargo-util/src/process_builder.rs b/src/tools/cargo/crates/cargo-util/src/process_builder.rs
index 76392f256..b197b95b1 100644
--- a/src/tools/cargo/crates/cargo-util/src/process_builder.rs
+++ b/src/tools/cargo/crates/cargo-util/src/process_builder.rs
@@ -449,7 +449,7 @@ impl ProcessBuilder {
arg.push(tmp.path());
let mut cmd = self.build_command_without_args();
cmd.arg(arg);
- log::debug!("created argfile at {} for {self}", tmp.path().display());
+ tracing::debug!("created argfile at {} for {self}", tmp.path().display());
let cap = self.get_args().map(|arg| arg.len() + 1).sum::<usize>();
let mut buf = Vec::with_capacity(cap);
@@ -558,7 +558,7 @@ fn piped(cmd: &mut Command, pipe_stdin: bool) -> &mut Command {
fn close_tempfile_and_log_error(file: NamedTempFile) {
file.close().unwrap_or_else(|e| {
- log::warn!("failed to close temporary file: {e}");
+ tracing::warn!("failed to close temporary file: {e}");
});
}
diff --git a/src/tools/cargo/crates/crates-io/Cargo.toml b/src/tools/cargo/crates/crates-io/Cargo.toml
index 034c2fca5..139b8aa97 100644
--- a/src/tools/cargo/crates/crates-io/Cargo.toml
+++ b/src/tools/cargo/crates/crates-io/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "crates-io"
-version = "0.37.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+version = "0.38.0"
+edition.workspace = true
+license.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = """
Helpers for interacting with crates.io
@@ -13,9 +13,9 @@ name = "crates_io"
path = "lib.rs"
[dependencies]
-anyhow.workspace = true
curl.workspace = true
percent-encoding.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
+thiserror.workspace = true
url.workspace = true
diff --git a/src/tools/cargo/crates/crates-io/lib.rs b/src/tools/cargo/crates/crates-io/lib.rs
index 243808098..6ce39cefd 100644
--- a/src/tools/cargo/crates/crates-io/lib.rs
+++ b/src/tools/cargo/crates/crates-io/lib.rs
@@ -1,18 +1,18 @@
#![allow(clippy::all)]
use std::collections::BTreeMap;
-use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::io::{Cursor, SeekFrom};
use std::time::Instant;
-use anyhow::{bail, format_err, Context, Result};
use curl::easy::{Easy, List};
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
use serde::{Deserialize, Serialize};
use url::Url;
+pub type Result<T> = std::result::Result<T, Error>;
+
pub struct Registry {
/// The base URL for issuing API requests.
host: String,
@@ -125,67 +125,62 @@ struct Crates {
meta: TotalCrates,
}
-#[derive(Debug)]
-pub enum ResponseError {
- Curl(curl::Error),
+/// Error returned when interacting with a registry.
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ /// Error from libcurl.
+ #[error(transparent)]
+ Curl(#[from] curl::Error),
+
+ /// Error from seriailzing the request payload and deserialzing the
+ /// response body (like response body didn't match expected structure).
+ #[error(transparent)]
+ Json(#[from] serde_json::Error),
+
+ /// Error from IO. Mostly from reading the tarball to upload.
+ #[error("failed to seek tarball")]
+ Io(#[from] std::io::Error),
+
+ /// Response body was not valid utf8.
+ #[error("invalid response body from server")]
+ Utf8(#[from] std::string::FromUtf8Error),
+
+ /// Error from API response containing JSON field `errors.details`.
+ #[error(
+ "the remote server responded with an error{}: {}",
+ status(*code),
+ errors.join(", "),
+ )]
Api {
code: u32,
+ headers: Vec<String>,
errors: Vec<String>,
},
+
+ /// Error from API response which didn't have pre-programmed `errors.details`.
+ #[error(
+ "failed to get a 200 OK response, got {code}\nheaders:\n\t{}\nbody:\n{body}",
+ headers.join("\n\t"),
+ )]
Code {
code: u32,
headers: Vec<String>,
body: String,
},
- Other(anyhow::Error),
-}
-
-impl std::error::Error for ResponseError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- ResponseError::Curl(..) => None,
- ResponseError::Api { .. } => None,
- ResponseError::Code { .. } => None,
- ResponseError::Other(e) => Some(e.as_ref()),
- }
- }
-}
-impl fmt::Display for ResponseError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- ResponseError::Curl(e) => write!(f, "{}", e),
- ResponseError::Api { code, errors } => {
- f.write_str("the remote server responded with an error")?;
- if *code != 200 {
- write!(f, " (status {} {})", code, reason(*code))?;
- };
- write!(f, ": {}", errors.join(", "))
- }
- ResponseError::Code {
- code,
- headers,
- body,
- } => write!(
- f,
- "failed to get a 200 OK response, got {}\n\
- headers:\n\
- \t{}\n\
- body:\n\
- {}",
- code,
- headers.join("\n\t"),
- body
- ),
- ResponseError::Other(..) => write!(f, "invalid response from server"),
- }
- }
-}
-
-impl From<curl::Error> for ResponseError {
- fn from(error: curl::Error) -> Self {
- ResponseError::Curl(error)
- }
+ /// Reason why the token was invalid.
+ #[error("{0}")]
+ InvalidToken(&'static str),
+
+ /// Server was unavailable and timeouted. Happened when uploading a way
+ /// too large tarball to crates.io.
+ #[error(
+ "Request timed out after 30 seconds. If you're trying to \
+ upload a crate it may be too large. If the crate is under \
+ 10MB in size, you can email help@crates.io for assistance.\n\
+ Total size was {0}."
+ )]
+ Timeout(u64),
}
impl Registry {
@@ -221,10 +216,9 @@ impl Registry {
}
fn token(&self) -> Result<&str> {
- let token = match self.token.as_ref() {
- Some(s) => s,
- None => bail!("no upload token found, please run `cargo login`"),
- };
+ let token = self.token.as_ref().ok_or_else(|| {
+ Error::InvalidToken("no upload token found, please run `cargo login`")
+ })?;
check_token(token)?;
Ok(token)
}
@@ -270,12 +264,8 @@ impl Registry {
// This checks the length using seeking instead of metadata, because
// on some filesystems, getting the metadata will fail because
// the file was renamed in ops::package.
- let tarball_len = tarball
- .seek(SeekFrom::End(0))
- .with_context(|| "failed to seek tarball")?;
- tarball
- .seek(SeekFrom::Start(0))
- .with_context(|| "failed to seek tarball")?;
+ let tarball_len = tarball.seek(SeekFrom::End(0))?;
+ tarball.seek(SeekFrom::Start(0))?;
let header = {
let mut w = Vec::new();
w.extend(&(json.len() as u32).to_le_bytes());
@@ -300,18 +290,12 @@ impl Registry {
let body = self
.handle(&mut |buf| body.read(buf).unwrap_or(0))
.map_err(|e| match e {
- ResponseError::Code { code, .. }
+ Error::Code { code, .. }
if code == 503
&& started.elapsed().as_secs() >= 29
&& self.host_is_crates_io() =>
{
- format_err!(
- "Request timed out after 30 seconds. If you're trying to \
- upload a crate it may be too large. If the crate is under \
- 10MB in size, you can email help@crates.io for assistance.\n\
- Total size was {}.",
- tarball_len
- )
+ Error::Timeout(tarball_len)
}
_ => e.into(),
})?;
@@ -410,10 +394,7 @@ impl Registry {
}
}
- fn handle(
- &mut self,
- read: &mut dyn FnMut(&mut [u8]) -> usize,
- ) -> std::result::Result<String, ResponseError> {
+ fn handle(&mut self, read: &mut dyn FnMut(&mut [u8]) -> usize) -> Result<String> {
let mut headers = Vec::new();
let mut body = Vec::new();
{
@@ -427,28 +408,29 @@ impl Registry {
// Headers contain trailing \r\n, trim them to make it easier
// to work with.
let s = String::from_utf8_lossy(data).trim().to_string();
+ // Don't let server sneak extra lines anywhere.
+ if s.contains('\n') {
+ return true;
+ }
headers.push(s);
true
})?;
handle.perform()?;
}
- let body = match String::from_utf8(body) {
- Ok(body) => body,
- Err(..) => {
- return Err(ResponseError::Other(format_err!(
- "response body was not valid utf-8"
- )))
- }
- };
+ let body = String::from_utf8(body)?;
let errors = serde_json::from_str::<ApiErrorList>(&body)
.ok()
.map(|s| s.errors.into_iter().map(|s| s.detail).collect::<Vec<_>>());
match (self.handle.response_code()?, errors) {
(0, None) | (200, None) => Ok(body),
- (code, Some(errors)) => Err(ResponseError::Api { code, errors }),
- (code, None) => Err(ResponseError::Code {
+ (code, Some(errors)) => Err(Error::Api {
+ code,
+ headers,
+ errors,
+ }),
+ (code, None) => Err(Error::Code {
code,
headers,
body,
@@ -457,6 +439,15 @@ impl Registry {
}
}
+fn status(code: u32) -> String {
+ if code == 200 {
+ String::new()
+ } else {
+ let reason = reason(code);
+ format!(" (status {code} {reason})")
+ }
+}
+
fn reason(code: u32) -> &'static str {
// Taken from https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
match code {
@@ -520,7 +511,7 @@ pub fn is_url_crates_io(url: &str) -> bool {
/// registries only create tokens in that format so that is as less restricted as possible.
pub fn check_token(token: &str) -> Result<()> {
if token.is_empty() {
- bail!("please provide a non-empty token");
+ return Err(Error::InvalidToken("please provide a non-empty token"));
}
if token.bytes().all(|b| {
// This is essentially the US-ASCII limitation of
@@ -531,9 +522,9 @@ pub fn check_token(token: &str) -> Result<()> {
}) {
Ok(())
} else {
- Err(anyhow::anyhow!(
+ Err(Error::InvalidToken(
"token contains invalid characters.\nOnly printable ISO-8859-1 characters \
- are allowed as it is sent in a HTTPS header."
+ are allowed as it is sent in a HTTPS header.",
))
}
}
diff --git a/src/tools/cargo/crates/home/Cargo.toml b/src/tools/cargo/crates/home/Cargo.toml
index 6c65ecc18..03bd555a2 100644
--- a/src/tools/cargo/crates/home/Cargo.toml
+++ b/src/tools/cargo/crates/home/Cargo.toml
@@ -1,9 +1,9 @@
[package]
name = "home"
-version = "0.5.6" # also update `html_root_url` in `src/lib.rs`
+version = "0.5.7" # also update `html_root_url` in `src/lib.rs`
authors = ["Brian Anderson <andersrb@gmail.com>"]
documentation = "https://docs.rs/home"
-edition = "2018"
+edition.workspace = true
include = [
"/src",
"/Cargo.toml",
@@ -11,8 +11,7 @@ include = [
"/LICENSE-*",
"/README.md",
]
-license = "MIT OR Apache-2.0"
-readme = "README.md"
+license.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = "Shared definitions of home directories."
diff --git a/src/tools/cargo/crates/mdman/Cargo.toml b/src/tools/cargo/crates/mdman/Cargo.toml
index 812f1393a..ba1d4b462 100644
--- a/src/tools/cargo/crates/mdman/Cargo.toml
+++ b/src/tools/cargo/crates/mdman/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "mdman"
version = "0.0.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+edition.workspace = true
+license.workspace = true
description = "Creates a man page page from markdown."
publish = false
diff --git a/src/tools/cargo/crates/resolver-tests/Cargo.toml b/src/tools/cargo/crates/resolver-tests/Cargo.toml
index e0efb9b6d..5e69d7367 100644
--- a/src/tools/cargo/crates/resolver-tests/Cargo.toml
+++ b/src/tools/cargo/crates/resolver-tests/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "resolver-tests"
version = "0.0.0"
-edition = "2018"
+edition.workspace = true
publish = false
[dependencies]
diff --git a/src/tools/cargo/crates/semver-check/Cargo.toml b/src/tools/cargo/crates/semver-check/Cargo.toml
index f7b8c7d48..17e696566 100644
--- a/src/tools/cargo/crates/semver-check/Cargo.toml
+++ b/src/tools/cargo/crates/semver-check/Cargo.toml
@@ -2,7 +2,7 @@
name = "semver-check"
version = "0.0.0"
authors = ["Eric Huss"]
-edition = "2021"
+edition.workspace = true
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/src/tools/cargo/crates/semver-check/src/main.rs b/src/tools/cargo/crates/semver-check/src/main.rs
index fa4639eb7..1ba405f57 100644
--- a/src/tools/cargo/crates/semver-check/src/main.rs
+++ b/src/tools/cargo/crates/semver-check/src/main.rs
@@ -7,6 +7,11 @@
//! An example with the word "MINOR" at the top is expected to successfully
//! build against the before and after. Otherwise it should fail. A comment of
//! "// Error:" will check that the given message appears in the error output.
+//!
+//! The code block can also include the annotations:
+//! - `run-fail`: The test should fail at runtime, not compiletime.
+//! - `dont-deny`: By default tests have a `#![deny(warnings)]`. This option
+//! avoids this attribute. Note that `#![allow(unused)]` is always added.
use std::error::Error;
use std::fs;
@@ -57,7 +62,13 @@ fn doit() -> Result<(), Box<dyn Error>> {
if line.trim() == "```" {
break;
}
- block.push(line);
+ // Support rustdoc/mdbook hidden lines.
+ let line = line.strip_prefix("# ").unwrap_or(line);
+ if line == "#" {
+ block.push("");
+ } else {
+ block.push(line);
+ }
}
None => {
return Err(format!(
diff --git a/src/tools/cargo/crates/xtask-build-man/Cargo.toml b/src/tools/cargo/crates/xtask-build-man/Cargo.toml
index 6d02aa2c3..bec10c48c 100644
--- a/src/tools/cargo/crates/xtask-build-man/Cargo.toml
+++ b/src/tools/cargo/crates/xtask-build-man/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "xtask-build-man"
version = "0.0.0"
-edition = "2021"
+edition.workspace = true
publish = false
[dependencies]
diff --git a/src/tools/cargo/crates/xtask-bump-check/Cargo.toml b/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
new file mode 100644
index 000000000..e965ad09e
--- /dev/null
+++ b/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "xtask-bump-check"
+version = "0.0.0"
+edition.workspace = true
+publish = false
+
+[dependencies]
+anyhow.workspace = true
+cargo.workspace = true
+cargo-util.workspace = true
+clap.workspace = true
+git2.workspace = true
+tracing.workspace = true
+tracing-subscriber.workspace = true
diff --git a/src/tools/cargo/crates/xtask-unpublished/src/main.rs b/src/tools/cargo/crates/xtask-bump-check/src/main.rs
index 1942a3621..0461ab91a 100644
--- a/src/tools/cargo/crates/xtask-unpublished/src/main.rs
+++ b/src/tools/cargo/crates/xtask-bump-check/src/main.rs
@@ -1,7 +1,8 @@
mod xtask;
fn main() {
- env_logger::init_from_env("CARGO_LOG");
+ setup_logger();
+
let cli = xtask::cli();
let matches = cli.get_matches();
@@ -13,3 +14,14 @@ fn main() {
cargo::exit_with_error(e, &mut config.shell())
}
}
+
+// In sync with `src/bin/cargo/main.rs@setup_logger`.
+fn setup_logger() {
+ let env = tracing_subscriber::EnvFilter::from_env("CARGO_LOG");
+
+ tracing_subscriber::fmt()
+ .with_ansi(std::io::IsTerminal::is_terminal(&std::io::stderr()))
+ .with_writer(std::io::stderr)
+ .with_env_filter(env)
+ .init();
+}
diff --git a/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs b/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
new file mode 100644
index 000000000..f89152331
--- /dev/null
+++ b/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
@@ -0,0 +1,423 @@
+//! ```text
+//! NAME
+//! xtask-bump-check
+//!
+//! SYNOPSIS
+//! xtask-bump-check --base-rev <REV> --head-rev <REV>
+//!
+//! DESCRIPTION
+//! Checks if there is any member got changed since a base commit
+//! but forgot to bump its version.
+//! ```
+
+use std::collections::HashMap;
+use std::fmt::Write;
+use std::fs;
+use std::task;
+
+use cargo::core::dependency::Dependency;
+use cargo::core::registry::PackageRegistry;
+use cargo::core::Package;
+use cargo::core::QueryKind;
+use cargo::core::Registry;
+use cargo::core::SourceId;
+use cargo::core::Workspace;
+use cargo::util::command_prelude::*;
+use cargo::util::ToSemver;
+use cargo::CargoResult;
+use cargo_util::ProcessBuilder;
+
+const UPSTREAM_BRANCH: &str = "master";
+const STATUS: &str = "BumpCheck";
+
+pub fn cli() -> clap::Command {
+ clap::Command::new("xtask-bump-check")
+ .arg(
+ opt(
+ "verbose",
+ "Use verbose output (-vv very verbose/build.rs output)",
+ )
+ .short('v')
+ .action(ArgAction::Count)
+ .global(true),
+ )
+ .arg_quiet()
+ .arg(
+ opt("color", "Coloring: auto, always, never")
+ .value_name("WHEN")
+ .global(true),
+ )
+ .arg(opt("base-rev", "Git revision to lookup for a baseline"))
+ .arg(opt("head-rev", "Git revision with changes"))
+ .arg(flag("frozen", "Require Cargo.lock and cache are up to date").global(true))
+ .arg(flag("locked", "Require Cargo.lock is up to date").global(true))
+ .arg(flag("offline", "Run without accessing the network").global(true))
+ .arg(multi_opt("config", "KEY=VALUE", "Override a configuration value").global(true))
+ .arg(
+ Arg::new("unstable-features")
+ .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
+ .short('Z')
+ .value_name("FLAG")
+ .action(ArgAction::Append)
+ .global(true),
+ )
+}
+
+pub fn exec(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult {
+ config_configure(config, args)?;
+
+ bump_check(args, config)?;
+
+ Ok(())
+}
+
+fn config_configure(config: &mut Config, args: &ArgMatches) -> CliResult {
+ let verbose = args.verbose();
+ // quiet is unusual because it is redefined in some subcommands in order
+ // to provide custom help text.
+ let quiet = args.flag("quiet");
+ let color = args.get_one::<String>("color").map(String::as_str);
+ let frozen = args.flag("frozen");
+ let locked = args.flag("locked");
+ let offline = args.flag("offline");
+ let mut unstable_flags = vec![];
+ if let Some(values) = args.get_many::<String>("unstable-features") {
+ unstable_flags.extend(values.cloned());
+ }
+ let mut config_args = vec![];
+ if let Some(values) = args.get_many::<String>("config") {
+ config_args.extend(values.cloned());
+ }
+ config.configure(
+ verbose,
+ quiet,
+ color,
+ frozen,
+ locked,
+ offline,
+ &None,
+ &unstable_flags,
+ &config_args,
+ )?;
+ Ok(())
+}
+
+/// Main entry of `xtask-bump-check`.
+///
+/// Assumption: version number are incremental. We never have point release for old versions.
+fn bump_check(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> CargoResult<()> {
+ let ws = args.workspace(config)?;
+ let repo = git2::Repository::open(ws.root())?;
+ let base_commit = get_base_commit(config, args, &repo)?;
+ let head_commit = get_head_commit(args, &repo)?;
+ let referenced_commit = get_referenced_commit(&repo, &base_commit)?;
+ let changed_members = changed(&ws, &repo, &base_commit, &head_commit)?;
+ let status = |msg: &str| config.shell().status(STATUS, msg);
+
+ status(&format!("base commit `{}`", base_commit.id()))?;
+ status(&format!("head commit `{}`", head_commit.id()))?;
+
+ let mut needs_bump = Vec::new();
+
+ check_crates_io(config, &changed_members, &mut needs_bump)?;
+
+ if let Some(referenced_commit) = referenced_commit.as_ref() {
+ status(&format!("compare against `{}`", referenced_commit.id()))?;
+ for referenced_member in checkout_ws(&ws, &repo, referenced_commit)?.members() {
+ let pkg_name = referenced_member.name().as_str();
+ let Some(changed_member) = changed_members.get(pkg_name) else {
+ tracing::trace!("skipping {pkg_name}, may be removed or not published");
+ continue;
+ };
+
+ if changed_member.version() <= referenced_member.version() {
+ needs_bump.push(*changed_member);
+ }
+ }
+ }
+
+ if !needs_bump.is_empty() {
+ needs_bump.sort();
+ needs_bump.dedup();
+ let mut msg = String::new();
+ msg.push_str("Detected changes in these crates but no version bump found:\n");
+ for pkg in needs_bump {
+ writeln!(&mut msg, " {}@{}", pkg.name(), pkg.version())?;
+ }
+ msg.push_str("\nPlease bump at least one patch version in each corresponding Cargo.toml.");
+ anyhow::bail!(msg)
+ }
+
+ // Tracked by https://github.com/obi1kenobi/cargo-semver-checks/issues/511
+ let exclude_args = [
+ "--exclude",
+ "cargo-credential-1password",
+ "--exclude",
+ "cargo-credential-libsecret",
+ "--exclude",
+ "cargo-credential-macos-keychain",
+ "--exclude",
+ "cargo-credential-wincred",
+ ];
+
+ // Even when we test against baseline-rev, we still need to make sure a
+ // change doesn't violate SemVer rules aginst crates.io releases. The
+ // possibility of this happening is nearly zero but no harm to check twice.
+ let mut cmd = ProcessBuilder::new("cargo");
+ cmd.arg("semver-checks")
+ .arg("check-release")
+ .arg("--workspace")
+ .args(&exclude_args);
+ config.shell().status("Running", &cmd)?;
+ cmd.exec()?;
+
+ if let Some(referenced_commit) = referenced_commit.as_ref() {
+ let mut cmd = ProcessBuilder::new("cargo");
+ cmd.arg("semver-checks")
+ .arg("--workspace")
+ .arg("--baseline-rev")
+ .arg(referenced_commit.id().to_string())
+ .args(&exclude_args);
+ config.shell().status("Running", &cmd)?;
+ cmd.exec()?;
+ }
+
+ status("no version bump needed for member crates.")?;
+
+ return Ok(());
+}
+
+/// Returns the commit of upstream `master` branch if `base-rev` is missing.
+fn get_base_commit<'a>(
+ config: &Config,
+ args: &clap::ArgMatches,
+ repo: &'a git2::Repository,
+) -> CargoResult<git2::Commit<'a>> {
+ let base_commit = match args.get_one::<String>("base-rev") {
+ Some(sha) => {
+ let obj = repo.revparse_single(sha)?;
+ obj.peel_to_commit()?
+ }
+ None => {
+ let upstream_branches = repo
+ .branches(Some(git2::BranchType::Remote))?
+ .filter_map(|r| r.ok())
+ .filter(|(b, _)| {
+ b.name()
+ .ok()
+ .flatten()
+ .unwrap_or_default()
+ .ends_with(&format!("/{UPSTREAM_BRANCH}"))
+ })
+ .map(|(b, _)| b)
+ .collect::<Vec<_>>();
+ if upstream_branches.is_empty() {
+ anyhow::bail!(
+ "could not find `base-sha` for `{UPSTREAM_BRANCH}`, pass it in directly"
+ );
+ }
+ let upstream_ref = upstream_branches[0].get();
+ if upstream_branches.len() > 1 {
+ let name = upstream_ref.name().expect("name is valid UTF-8");
+ let _ = config.shell().warn(format!(
+ "multiple `{UPSTREAM_BRANCH}` found, picking {name}"
+ ));
+ }
+ upstream_ref.peel_to_commit()?
+ }
+ };
+ Ok(base_commit)
+}
+
+/// Returns `HEAD` of the Git repository if `head-rev` is missing.
+fn get_head_commit<'a>(
+ args: &clap::ArgMatches,
+ repo: &'a git2::Repository,
+) -> CargoResult<git2::Commit<'a>> {
+ let head_commit = match args.get_one::<String>("head-rev") {
+ Some(sha) => {
+ let head_obj = repo.revparse_single(sha)?;
+ head_obj.peel_to_commit()?
+ }
+ None => {
+ let head_ref = repo.head()?;
+ head_ref.peel_to_commit()?
+ }
+ };
+ Ok(head_commit)
+}
+
+/// Gets the referenced commit to compare if version bump needed.
+///
+/// * When merging into nightly, check the version with beta branch
+/// * When merging into beta, check the version with stable branch
+/// * When merging into stable, check against crates.io registry directly
+fn get_referenced_commit<'a>(
+ repo: &'a git2::Repository,
+ base: &git2::Commit<'a>,
+) -> CargoResult<Option<git2::Commit<'a>>> {
+ let [beta, stable] = beta_and_stable_branch(&repo)?;
+ let rev_id = base.id();
+ let stable_commit = stable.get().peel_to_commit()?;
+ let beta_commit = beta.get().peel_to_commit()?;
+
+ let referenced_commit = if rev_id == stable_commit.id() {
+ None
+ } else if rev_id == beta_commit.id() {
+ tracing::trace!("stable branch from `{}`", stable.name().unwrap().unwrap());
+ Some(stable_commit)
+ } else {
+ tracing::trace!("beta branch from `{}`", beta.name().unwrap().unwrap());
+ Some(beta_commit)
+ };
+
+ Ok(referenced_commit)
+}
+
+/// Get the current beta and stable branch in cargo repository.
+///
+/// Assumptions:
+///
+/// * The repository contains the full history of `<remote>/rust-1.*.0` branches.
+/// * The version part of `<remote>/rust-1.*.0` always ends with a zero.
+/// * The maximum version is for beta channel, and the second one is for stable.
+fn beta_and_stable_branch(repo: &git2::Repository) -> CargoResult<[git2::Branch<'_>; 2]> {
+ let mut release_branches = Vec::new();
+ for branch in repo.branches(Some(git2::BranchType::Remote))? {
+ let (branch, _) = branch?;
+ let name = branch.name()?.unwrap();
+ let Some((_, version)) = name.split_once("/rust-") else {
+ tracing::trace!("branch `{name}` is not in the format of `<remote>/rust-<semver>`");
+ continue;
+ };
+ let Ok(version) = version.to_semver() else {
+ tracing::trace!("branch `{name}` is not a valid semver: `{version}`");
+ continue;
+ };
+ release_branches.push((version, branch));
+ }
+ release_branches.sort_unstable_by(|a, b| a.0.cmp(&b.0));
+ release_branches.dedup_by(|a, b| a.0 == b.0);
+
+ let beta = release_branches.pop().unwrap();
+ let stable = release_branches.pop().unwrap();
+
+ assert_eq!(beta.0.major, 1);
+ assert_eq!(beta.0.patch, 0);
+ assert_eq!(stable.0.major, 1);
+ assert_eq!(stable.0.patch, 0);
+ assert_ne!(beta.0.minor, stable.0.minor);
+
+ Ok([beta.1, stable.1])
+}
+
+/// Lists all changed workspace members between two commits.
+fn changed<'r, 'ws>(
+ ws: &'ws Workspace<'_>,
+ repo: &'r git2::Repository,
+ base_commit: &git2::Commit<'r>,
+ head: &git2::Commit<'r>,
+) -> CargoResult<HashMap<&'ws str, &'ws Package>> {
+ let root_pkg_name = ws.current()?.name(); // `cargo` crate.
+ let ws_members = ws
+ .members()
+ .filter(|pkg| pkg.name() != root_pkg_name) // Only take care of sub crates here.
+ .filter(|pkg| pkg.publish() != &Some(vec![])) // filter out `publish = false`
+ .map(|pkg| {
+ // Having relative package root path so that we can compare with
+ // paths of changed files to determine which package has changed.
+ let relative_pkg_root = pkg.root().strip_prefix(ws.root()).unwrap();
+ (relative_pkg_root, pkg)
+ })
+ .collect::<Vec<_>>();
+ let base_tree = base_commit.as_object().peel_to_tree()?;
+ let head_tree = head.as_object().peel_to_tree()?;
+ let diff = repo.diff_tree_to_tree(Some(&base_tree), Some(&head_tree), Default::default())?;
+
+ let mut changed_members = HashMap::new();
+
+ for delta in diff.deltas() {
+ let old = delta.old_file().path().unwrap();
+ let new = delta.new_file().path().unwrap();
+ for (ref pkg_root, pkg) in ws_members.iter() {
+ if old.starts_with(pkg_root) || new.starts_with(pkg_root) {
+ changed_members.insert(pkg.name().as_str(), *pkg);
+ break;
+ }
+ }
+ }
+
+ tracing::trace!("changed_members: {:?}", changed_members.keys());
+ Ok(changed_members)
+}
+
+/// Compares version against published crates on crates.io.
+///
+/// Assumption: We always release a version larger than all existing versions.
+fn check_crates_io<'a>(
+ config: &Config,
+ changed_members: &HashMap<&'a str, &'a Package>,
+ needs_bump: &mut Vec<&'a Package>,
+) -> CargoResult<()> {
+ let source_id = SourceId::crates_io(config)?;
+ let mut registry = PackageRegistry::new(config)?;
+ let _lock = config.acquire_package_cache_lock()?;
+ registry.lock_patches();
+ config.shell().status(
+ STATUS,
+ format_args!("compare against `{}`", source_id.display_registry_name()),
+ )?;
+ for (name, member) in changed_members {
+ let current = member.version();
+ let version_req = format!(">={current}");
+ let query = Dependency::parse(*name, Some(&version_req), source_id)?;
+ let possibilities = loop {
+ // Exact to avoid returning all for path/git
+ match registry.query_vec(&query, QueryKind::Exact) {
+ task::Poll::Ready(res) => {
+ break res?;
+ }
+ task::Poll::Pending => registry.block_until_ready()?,
+ }
+ };
+ if possibilities.is_empty() {
+ tracing::trace!("dep `{name}` has no version greater than or equal to `{current}`");
+ } else {
+ tracing::trace!(
+ "`{name}@{current}` needs a bump because its should have a version newer than crates.io: {:?}`",
+ possibilities
+ .iter()
+ .map(|s| format!("{}@{}", s.name(), s.version()))
+ .collect::<Vec<_>>(),
+ );
+ needs_bump.push(member);
+ }
+ }
+
+ Ok(())
+}
+
+/// Checkouts a temporary workspace to do further version comparsions.
+fn checkout_ws<'cfg, 'a>(
+ ws: &Workspace<'cfg>,
+ repo: &'a git2::Repository,
+ referenced_commit: &git2::Commit<'a>,
+) -> CargoResult<Workspace<'cfg>> {
+ let repo_path = repo.path().as_os_str().to_str().unwrap();
+ // Put it under `target/cargo-<short-id>`
+ let short_id = &referenced_commit.id().to_string()[..7];
+ let checkout_path = ws.target_dir().join(format!("cargo-{short_id}"));
+ let checkout_path = checkout_path.as_path_unlocked();
+ let _ = fs::remove_dir_all(checkout_path);
+ let new_repo = git2::build::RepoBuilder::new()
+ .clone_local(git2::build::CloneLocal::Local)
+ .clone(repo_path, checkout_path)?;
+ let obj = new_repo.find_object(referenced_commit.id(), None)?;
+ new_repo.reset(&obj, git2::ResetType::Hard, None)?;
+ Workspace::new(&checkout_path.join("Cargo.toml"), ws.config())
+}
+
+#[test]
+fn verify_cli() {
+ cli().debug_assert();
+}
diff --git a/src/tools/cargo/crates/xtask-stale-label/Cargo.toml b/src/tools/cargo/crates/xtask-stale-label/Cargo.toml
index af3218e96..b1f54a2f1 100644
--- a/src/tools/cargo/crates/xtask-stale-label/Cargo.toml
+++ b/src/tools/cargo/crates/xtask-stale-label/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "xtask-stale-label"
version = "0.0.0"
-edition = "2021"
+edition.workspace = true
publish = false
[dependencies]
diff --git a/src/tools/cargo/crates/xtask-stale-label/src/main.rs b/src/tools/cargo/crates/xtask-stale-label/src/main.rs
index 37675979c..88c044b5b 100644
--- a/src/tools/cargo/crates/xtask-stale-label/src/main.rs
+++ b/src/tools/cargo/crates/xtask-stale-label/src/main.rs
@@ -34,7 +34,7 @@ fn main() {
for (label, value) in autolabel.iter() {
let Some(trigger_files) = value.get("trigger_files") else {
- continue
+ continue;
};
let trigger_files = trigger_files.as_array().expect("an array");
let missing_files: Vec<_> = trigger_files
diff --git a/src/tools/cargo/crates/xtask-unpublished/Cargo.toml b/src/tools/cargo/crates/xtask-unpublished/Cargo.toml
deleted file mode 100644
index 541a34dea..000000000
--- a/src/tools/cargo/crates/xtask-unpublished/Cargo.toml
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name = "xtask-unpublished"
-version = "0.0.0"
-edition = "2021"
-publish = false
-
-[dependencies]
-anyhow.workspace = true
-cargo.workspace = true
-clap.workspace = true
-env_logger.workspace = true
-log.workspace = true
diff --git a/src/tools/cargo/crates/xtask-unpublished/src/xtask.rs b/src/tools/cargo/crates/xtask-unpublished/src/xtask.rs
deleted file mode 100644
index f1086951f..000000000
--- a/src/tools/cargo/crates/xtask-unpublished/src/xtask.rs
+++ /dev/null
@@ -1,200 +0,0 @@
-//! `xtask-unpublished` outputs a table with publish status --- a local version
-//! and a version on crates.io for comparisons.
-//!
-//! This aims to help developers check if there is any crate required a new
-//! publish, as well as detect if a version bump is needed in CI pipeline.
-
-use std::collections::HashSet;
-
-use cargo::core::registry::PackageRegistry;
-use cargo::core::QueryKind;
-use cargo::core::Registry;
-use cargo::core::SourceId;
-use cargo::ops::Packages;
-use cargo::util::command_prelude::*;
-
-pub fn cli() -> clap::Command {
- clap::Command::new("xtask-unpublished")
- .arg_package_spec_simple("Package to inspect the published status")
- .arg(
- opt(
- "verbose",
- "Use verbose output (-vv very verbose/build.rs output)",
- )
- .short('v')
- .action(ArgAction::Count)
- .global(true),
- )
- .arg_quiet()
- .arg(
- opt("color", "Coloring: auto, always, never")
- .value_name("WHEN")
- .global(true),
- )
- .arg(flag("frozen", "Require Cargo.lock and cache are up to date").global(true))
- .arg(flag("locked", "Require Cargo.lock is up to date").global(true))
- .arg(flag("offline", "Run without accessing the network").global(true))
- .arg(multi_opt("config", "KEY=VALUE", "Override a configuration value").global(true))
- .arg(
- Arg::new("unstable-features")
- .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
- .short('Z')
- .value_name("FLAG")
- .action(ArgAction::Append)
- .global(true),
- )
-}
-
-pub fn exec(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult {
- config_configure(config, args)?;
-
- unpublished(args, config)?;
-
- Ok(())
-}
-
-fn config_configure(config: &mut Config, args: &ArgMatches) -> CliResult {
- let verbose = args.verbose();
- // quiet is unusual because it is redefined in some subcommands in order
- // to provide custom help text.
- let quiet = args.flag("quiet");
- let color = args.get_one::<String>("color").map(String::as_str);
- let frozen = args.flag("frozen");
- let locked = args.flag("locked");
- let offline = args.flag("offline");
- let mut unstable_flags = vec![];
- if let Some(values) = args.get_many::<String>("unstable-features") {
- unstable_flags.extend(values.cloned());
- }
- let mut config_args = vec![];
- if let Some(values) = args.get_many::<String>("config") {
- config_args.extend(values.cloned());
- }
- config.configure(
- verbose,
- quiet,
- color,
- frozen,
- locked,
- offline,
- &None,
- &unstable_flags,
- &config_args,
- )?;
- Ok(())
-}
-
-fn unpublished(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult {
- let ws = args.workspace(config)?;
-
- let members_to_inspect: HashSet<_> = {
- let pkgs = args.packages_from_flags()?;
- if let Packages::Packages(_) = pkgs {
- HashSet::from_iter(pkgs.get_packages(&ws)?)
- } else {
- HashSet::from_iter(ws.members())
- }
- };
-
- 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 members_to_inspect {
- 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)?;
- 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()?,
- }
- };
- let (last, published) = possibilities
- .iter()
- .map(|s| s.version())
- .max()
- .map(|last| (last.to_string(), last == current))
- .unwrap_or(("-".to_string(), false));
-
- results.push(vec![
- name.to_string(),
- last,
- current.to_string(),
- if published { "yes" } else { "no" }.to_string(),
- ]);
- }
- }
- results.sort();
-
- if results.is_empty() {
- return Ok(());
- }
-
- results.insert(
- 0,
- vec![
- "name".to_owned(),
- "crates.io".to_owned(),
- "local".to_owned(),
- "published?".to_owned(),
- ],
- );
-
- output_table(results);
-
- Ok(())
-}
-
-/// Outputs a markdown table like this.
-///
-/// ```text
-/// | name | crates.io | local | published? |
-/// |------------------|-----------|--------|------------|
-/// | cargo | 0.70.1 | 0.72.0 | no |
-/// | cargo-platform | 0.1.2 | 0.1.2 | yes |
-/// | cargo-util | - | 0.2.4 | no |
-/// | crates-io | 0.36.0 | 0.36.0 | yes |
-/// | home | - | 0.5.6 | no |
-/// ```
-fn output_table(table: Vec<Vec<String>>) {
- let header = table.first().unwrap();
- let paddings = table.iter().fold(vec![0; header.len()], |mut widths, row| {
- for (width, field) in widths.iter_mut().zip(row) {
- *width = usize::max(*width, field.len());
- }
- widths
- });
-
- let print = |row: &[_]| {
- for (field, pad) in row.iter().zip(&paddings) {
- print!("| {field:pad$} ");
- }
- println!("|");
- };
-
- print(header);
-
- paddings.iter().for_each(|fill| print!("|-{:-<fill$}-", ""));
- println!("|");
-
- table.iter().skip(1).for_each(|r| print(r));
-}
-
-#[test]
-fn verify_cli() {
- cli().debug_assert();
-}