Add a schema version to the index.

This commit is contained in:
Eric Huss 2021-02-10 10:58:07 -08:00
parent ab64d1393b
commit 196673bb1e
7 changed files with 100 additions and 8 deletions

View File

@ -327,6 +327,7 @@ pub struct Package {
links: Option<String>, links: Option<String>,
rust_version: Option<String>, rust_version: Option<String>,
cargo_features: Vec<String>, cargo_features: Vec<String>,
v: Option<u32>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -401,6 +402,7 @@ impl Package {
links: None, links: None,
rust_version: None, rust_version: None,
cargo_features: Vec::new(), cargo_features: Vec::new(),
v: None,
} }
} }
@ -554,6 +556,14 @@ impl Package {
self self
} }
/// Sets the index schema version for this package.
///
/// See [`cargo::sources::registry::RegistryPackage`] for more information.
pub fn schema_version(&mut self, version: u32) -> &mut Package {
self.v = Some(version);
self
}
/// Creates the package and place it in the registry. /// Creates the package and place it in the registry.
/// ///
/// This does not actually use Cargo's publishing system, but instead /// This does not actually use Cargo's publishing system, but instead
@ -599,7 +609,7 @@ impl Package {
} else { } else {
serde_json::json!(self.name) serde_json::json!(self.name)
}; };
let line = serde_json::json!({ let mut json = serde_json::json!({
"name": name, "name": name,
"vers": self.vers, "vers": self.vers,
"deps": deps, "deps": deps,
@ -607,8 +617,11 @@ impl Package {
"features": self.features, "features": self.features,
"yanked": self.yanked, "yanked": self.yanked,
"links": self.links, "links": self.links,
}) });
.to_string(); if let Some(v) = self.v {
json["v"] = serde_json::json!(v);
}
let line = json.to_string();
let file = match self.name.len() { let file = match self.name.len() {
1 => format!("1/{}", self.name), 1 => format!("1/{}", self.name),

View File

@ -56,6 +56,8 @@ pub struct NewCrate {
pub repository: Option<String>, pub repository: Option<String>,
pub badges: BTreeMap<String, BTreeMap<String, String>>, pub badges: BTreeMap<String, BTreeMap<String, String>>,
pub links: Option<String>, pub links: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub v: Option<u32>,
} }
#[derive(Serialize)] #[derive(Serialize)]

View File

@ -305,6 +305,7 @@ fn transmit(
license_file: license_file.clone(), license_file: license_file.clone(),
badges: badges.clone(), badges: badges.clone(),
links: links.clone(), links: links.clone(),
v: None,
}, },
tarball, tarball,
); );

View File

@ -72,7 +72,8 @@ use crate::sources::registry::{RegistryData, RegistryPackage};
use crate::util::interning::InternedString; use crate::util::interning::InternedString;
use crate::util::paths; use crate::util::paths;
use crate::util::{internal, CargoResult, Config, Filesystem, ToSemver}; use crate::util::{internal, CargoResult, Config, Filesystem, ToSemver};
use log::info; use anyhow::bail;
use log::{debug, info};
use semver::{Version, VersionReq}; use semver::{Version, VersionReq};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fs; use std::fs;
@ -233,6 +234,8 @@ enum MaybeIndexSummary {
pub struct IndexSummary { pub struct IndexSummary {
pub summary: Summary, pub summary: Summary,
pub yanked: bool, pub yanked: bool,
/// Schema version, see [`RegistryPackage`].
v: u32,
} }
/// A representation of the cache on disk that Cargo maintains of summaries. /// A representation of the cache on disk that Cargo maintains of summaries.
@ -305,6 +308,7 @@ impl<'cfg> RegistryIndex<'cfg> {
// minimize the amount of work being done here and parse as little as // minimize the amount of work being done here and parse as little as
// necessary. // necessary.
let raw_data = &summaries.raw_data; let raw_data = &summaries.raw_data;
let max_version = 1;
Ok(summaries Ok(summaries
.versions .versions
.iter_mut() .iter_mut()
@ -318,6 +322,19 @@ impl<'cfg> RegistryIndex<'cfg> {
} }
}, },
) )
.filter(move |is| {
if is.v > max_version {
debug!(
"unsupported schema version {} ({} {})",
is.v,
is.summary.name(),
is.summary.version()
);
false
} else {
true
}
})
.filter(move |is| { .filter(move |is| {
is.summary is.summary
.unstable_gate(namespaced_features, weak_dep_features) .unstable_gate(namespaced_features, weak_dep_features)
@ -578,7 +595,14 @@ impl Summaries {
// actually happens to verify that our cache is indeed fresh and // actually happens to verify that our cache is indeed fresh and
// computes exactly the same value as before. // computes exactly the same value as before.
if cfg!(debug_assertions) && cache_contents.is_some() { if cfg!(debug_assertions) && cache_contents.is_some() {
assert_eq!(cache_bytes, cache_contents); if cache_bytes != cache_contents {
panic!(
"original cache contents:\n{:?}\n\
does not equal new cache contents:\n{:?}\n",
cache_contents.as_ref().map(|s| String::from_utf8_lossy(s)),
cache_bytes.as_ref().map(|s| String::from_utf8_lossy(s)),
);
}
} }
// Once we have our `cache_bytes` which represents the `Summaries` we're // Once we have our `cache_bytes` which represents the `Summaries` we're
@ -659,19 +683,19 @@ impl<'a> SummariesCache<'a> {
.split_first() .split_first()
.ok_or_else(|| anyhow::format_err!("malformed cache"))?; .ok_or_else(|| anyhow::format_err!("malformed cache"))?;
if *first_byte != CURRENT_CACHE_VERSION { if *first_byte != CURRENT_CACHE_VERSION {
anyhow::bail!("looks like a different Cargo's cache, bailing out"); bail!("looks like a different Cargo's cache, bailing out");
} }
let mut iter = split(rest, 0); let mut iter = split(rest, 0);
if let Some(update) = iter.next() { if let Some(update) = iter.next() {
if update != last_index_update.as_bytes() { if update != last_index_update.as_bytes() {
anyhow::bail!( bail!(
"cache out of date: current index ({}) != cache ({})", "cache out of date: current index ({}) != cache ({})",
last_index_update, last_index_update,
str::from_utf8(update)?, str::from_utf8(update)?,
) )
} }
} else { } else {
anyhow::bail!("malformed file"); bail!("malformed file");
} }
let mut ret = SummariesCache::default(); let mut ret = SummariesCache::default();
while let Some(version) = iter.next() { while let Some(version) = iter.next() {
@ -749,7 +773,9 @@ impl IndexSummary {
features, features,
yanked, yanked,
links, links,
v,
} = serde_json::from_slice(line)?; } = serde_json::from_slice(line)?;
let v = v.unwrap_or(1);
log::trace!("json parsed registry {}/{}", name, vers); log::trace!("json parsed registry {}/{}", name, vers);
let pkgid = PackageId::new(name, &vers, source_id)?; let pkgid = PackageId::new(name, &vers, source_id)?;
let deps = deps let deps = deps
@ -761,6 +787,7 @@ impl IndexSummary {
Ok(IndexSummary { Ok(IndexSummary {
summary, summary,
yanked: yanked.unwrap_or(false), yanked: yanked.unwrap_or(false),
v,
}) })
} }
} }

View File

@ -269,6 +269,24 @@ pub struct RegistryPackage<'a> {
/// Added early 2018 (see <https://github.com/rust-lang/cargo/pull/4978>), /// Added early 2018 (see <https://github.com/rust-lang/cargo/pull/4978>),
/// can be `None` if published before then. /// can be `None` if published before then.
links: Option<InternedString>, links: Option<InternedString>,
/// The schema version for this entry.
///
/// If this is None, it defaults to version 1. Entries with unknown
/// versions are ignored.
///
/// This provides a method to safely introduce changes to index entries
/// and allow older versions of cargo to ignore newer entries it doesn't
/// understand. This is honored as of 1.51, so unfortunately older
/// versions will ignore it, and potentially misinterpret version 1 and
/// newer entries.
///
/// The intent is that versions older than 1.51 will work with a
/// pre-existing `Cargo.lock`, but they may not correctly process `cargo
/// update` or build a lock from scratch. In that case, cargo may
/// incorrectly select a new package that uses a new index format. A
/// workaround is to downgrade any packages that are incompatible with the
/// `--precise` flag of `cargo update`.
v: Option<u32>,
} }
#[test] #[test]

View File

@ -78,6 +78,7 @@ mod multitarget;
mod net_config; mod net_config;
mod new; mod new;
mod offline; mod offline;
mod old_cargos;
mod out_dir; mod out_dir;
mod owner; mod owner;
mod package; mod package;

View File

@ -2170,3 +2170,33 @@ fn package_lock_inside_package_is_overwritten() {
assert_eq!(ok.metadata().unwrap().len(), 2); assert_eq!(ok.metadata().unwrap().len(), 2);
} }
#[cargo_test]
fn ignores_unknown_index_version() {
// If the version field is not understood, it is ignored.
Package::new("bar", "1.0.0").publish();
Package::new("bar", "1.0.1").schema_version(9999).publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = "1.0"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("tree")
.with_stdout(
"foo v0.1.0 [..]\n\
bar v1.0.0\n\
",
)
.run();
}