diff --git a/Cargo.lock b/Cargo.lock index cc4f5af1dfb..2c7c5379385 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,6 +295,7 @@ dependencies = [ "reqwest", "scheduled-thread-pool", "semver 0.10.0", + "semver 0.11.0", "sentry", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 03b1550eb9c..f58110dd8a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,8 @@ parking_lot = "0.11" rand = "0.7" reqwest = { version = "0.11", features = ["blocking", "gzip", "json"] } scheduled-thread-pool = "0.2.0" -semver = { version = "0.10", features = ["diesel", "serde"] } +semver = { version = "0.10", features = ["diesel", "serde"] } # TODO: Bump to 0.11 after steveklabnik/semver#217 is resolved +semver_next = { version = "0.11", package = "semver" } sentry = "0.22" serde = { version = "1.0.0", features = ["derive"] } serde_json = "1.0.0" diff --git a/src/admin/mod.rs b/src/admin/mod.rs index fd08ad49d73..99ce3efbea4 100644 --- a/src/admin/mod.rs +++ b/src/admin/mod.rs @@ -4,6 +4,7 @@ pub mod dialoguer; pub mod on_call; pub mod populate; pub mod render_readmes; +pub mod test_new_semver_release; pub mod test_pagerduty; pub mod transfer_crates; pub mod verify_token; diff --git a/src/admin/test_new_semver_release.rs b/src/admin/test_new_semver_release.rs new file mode 100644 index 00000000000..b7923a19f8f --- /dev/null +++ b/src/admin/test_new_semver_release.rs @@ -0,0 +1,71 @@ +use crate::{ + db, + schema::{dependencies, versions}, +}; + +use clap::Clap; +use diesel::prelude::*; + +#[derive(Clap, Copy, Clone, Debug)] +#[clap( + name = "test-new-semver-release", + about = "Scan the database for all versions and version requirements to \ + ensure they can be parsed by the latest `semver` crate." +)] +pub struct Opts {} + +pub fn run(_opts: Opts) { + use self::dependencies::dsl::*; + use self::versions::dsl::*; + + let url = db::connection_url(&dotenv::var("READ_ONLY_REPLICA_URL").unwrap()); + let conn = PgConnection::establish(&url).unwrap(); + conn.transaction::<_, diesel::result::Error, _>(|| { + let vers = versions.select(num).load(&conn)?; + test_versions(&vers); + + let deps = dependencies.select(req).load(&conn)?; + test_dependency_predicates(&deps); + + Ok(()) + }) + .unwrap(); + + println!("Test finished."); +} + +fn test_versions(versions: &[String]) { + for version in versions { + if let Err(e) = semver::Version::parse(version) { + println!("Could not parse `{}` as a semver::Version: {}", version, e); + } + } + + for version in versions { + if let Err(e) = semver::Version::parse(version) { + println!("Could not parse `{}` as a semver::Version: {}", version, e); + } + + if let Err(e) = semver_next::Version::parse(version) { + println!( + "Could not parse `{}` as a semver_next::Version: {}", + version, e + ); + } + } +} + +fn test_dependency_predicates(versions: &[String]) { + for version in versions { + if let Err(e) = semver::VersionReq::parse(version) { + println!("Could not parse `{}` as a semver::Version: {}", version, e); + } + + if let Err(e) = semver_next::VersionReq::parse(version) { + println!( + "Could not parse `{}` as a semver_next::Version: {}", + version, e + ); + } + } +} diff --git a/src/bin/crates-admin.rs b/src/bin/crates-admin.rs index f8b70179f00..ea54b2d6ff2 100644 --- a/src/bin/crates-admin.rs +++ b/src/bin/crates-admin.rs @@ -1,8 +1,8 @@ #![warn(clippy::all, rust_2018_idioms)] use cargo_registry::admin::{ - delete_crate, delete_version, populate, render_readmes, test_pagerduty, transfer_crates, - verify_token, + delete_crate, delete_version, populate, render_readmes, test_new_semver_release, + test_pagerduty, transfer_crates, verify_token, }; use clap::Clap; @@ -20,6 +20,7 @@ enum SubCommand { DeleteVersion(delete_version::Opts), Populate(populate::Opts), RenderReadmes(render_readmes::Opts), + TestNewSemverRelease(test_new_semver_release::Opts), TestPagerduty(test_pagerduty::Opts), TransferCrates(transfer_crates::Opts), VerifyToken(verify_token::Opts), @@ -33,6 +34,7 @@ fn main() { SubCommand::DeleteVersion(opts) => delete_version::run(opts), SubCommand::Populate(opts) => populate::run(opts), SubCommand::RenderReadmes(opts) => render_readmes::run(opts), + SubCommand::TestNewSemverRelease(opts) => test_new_semver_release::run(opts), SubCommand::TestPagerduty(opts) => test_pagerduty::run(opts).unwrap(), SubCommand::TransferCrates(opts) => transfer_crates::run(opts), SubCommand::VerifyToken(opts) => verify_token::run(opts).unwrap(), diff --git a/src/controllers/krate/publish.rs b/src/controllers/krate/publish.rs index 1257faa2728..e07f77f863e 100644 --- a/src/controllers/krate/publish.rs +++ b/src/controllers/krate/publish.rs @@ -25,7 +25,8 @@ pub const MISSING_RIGHTS_ERROR_MESSAGE: &str = to accept an invitation to be an owner before \ publishing."; -pub const WILDCARD_ERROR_MESSAGE: &str = "wildcard (`*`) dependency constraints are not allowed \ +pub const WILDCARD_ERROR_MESSAGE: &str = + "wildcard (`*` or `>= 0`) dependency constraints are not allowed \ on crates.io. See https://doc.rust-lang.org/cargo/faq.html#can-\ libraries-use--as-a-version-for-their-dependencies for more \ information"; diff --git a/src/tests/all.rs b/src/tests/all.rs index ac54f3ea53e..1222f2fafc4 100644 --- a/src/tests/all.rs +++ b/src/tests/all.rs @@ -178,6 +178,7 @@ fn env(var: &str) -> String { } } +#[track_caller] fn json(r: &mut AppResponse) -> T where for<'de> T: serde::Deserialize<'de>, diff --git a/src/tests/version.rs b/src/tests/version.rs index 6f059f2e5e4..93c9e4ae099 100644 --- a/src/tests/version.rs +++ b/src/tests/version.rs @@ -162,3 +162,28 @@ fn version_size() { .expect("Could not find v2.0.0"); assert_eq!(version2.crate_size, Some(91)); } + +#[test] +fn accepts_complicated_semver_prerelease_and_build_identifiers() { + #[track_caller] + fn parse(s: &str) { + semver::Version::parse(s).expect("couldn't parse as Version"); + semver::VersionReq::parse(s).expect("couldn't parse as VersionReq"); + } + + // Must provide at least major, minor, and patch so that it can parse as a Version + parse("1.0.0"); + + parse("1.0.0-1a"); + parse("1.0.0-1.a"); + parse("1.0.0-a1"); + parse("1.0.0-a.1"); + + parse("1.0.0+01a"); + parse("1.0.0+1.a"); + parse("1.0.0+a01"); + parse("1.0.0+a.01"); + + parse("1.0.0-01+01"); + parse("1.0.0-01.-.a+01.a-01a-01a"); +}