Skip to content

Commit

Permalink
Add a schema version to the index.
Browse files Browse the repository at this point in the history
  • Loading branch information
ehuss committed Feb 22, 2021
1 parent a9652c0 commit 19fd5cb
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 8 deletions.
19 changes: 16 additions & 3 deletions crates/cargo-test-support/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ pub struct Package {
links: Option<String>,
rust_version: Option<String>,
cargo_features: Vec<String>,
v: Option<u32>,
}

#[derive(Clone)]
Expand Down Expand Up @@ -401,6 +402,7 @@ impl Package {
links: None,
rust_version: None,
cargo_features: Vec::new(),
v: None,
}
}

Expand Down Expand Up @@ -554,6 +556,14 @@ impl Package {
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.
///
/// This does not actually use Cargo's publishing system, but instead
Expand Down Expand Up @@ -599,16 +609,19 @@ impl Package {
} else {
serde_json::json!(self.name)
};
let line = serde_json::json!({
let mut json = serde_json::json!({
"name": name,
"vers": self.vers,
"deps": deps,
"cksum": cksum,
"features": self.features,
"yanked": self.yanked,
"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() {
1 => format!("1/{}", self.name),
Expand Down
2 changes: 2 additions & 0 deletions crates/crates-io/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ pub struct NewCrate {
pub repository: Option<String>,
pub badges: BTreeMap<String, BTreeMap<String, String>>,
pub links: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub v: Option<u32>,
}

#[derive(Serialize)]
Expand Down
1 change: 1 addition & 0 deletions src/cargo/ops/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ fn transmit(
license_file: license_file.clone(),
badges: badges.clone(),
links: links.clone(),
v: None,
},
tarball,
);
Expand Down
37 changes: 32 additions & 5 deletions src/cargo/sources/registry/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ use crate::sources::registry::{RegistryData, RegistryPackage};
use crate::util::interning::InternedString;
use crate::util::paths;
use crate::util::{internal, CargoResult, Config, Filesystem, ToSemver};
use log::info;
use anyhow::bail;
use log::{debug, info};
use semver::{Version, VersionReq};
use std::collections::{HashMap, HashSet};
use std::fs;
Expand Down Expand Up @@ -233,6 +234,8 @@ enum MaybeIndexSummary {
pub struct IndexSummary {
pub summary: Summary,
pub yanked: bool,
/// Schema version, see [`RegistryPackage`].
v: u32,
}

/// A representation of the cache on disk that Cargo maintains of summaries.
Expand Down Expand Up @@ -305,6 +308,7 @@ impl<'cfg> RegistryIndex<'cfg> {
// minimize the amount of work being done here and parse as little as
// necessary.
let raw_data = &summaries.raw_data;
let max_version = 1;
Ok(summaries
.versions
.iter_mut()
Expand All @@ -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| {
is.summary
.unstable_gate(namespaced_features, weak_dep_features)
Expand Down Expand Up @@ -578,7 +595,14 @@ impl Summaries {
// actually happens to verify that our cache is indeed fresh and
// computes exactly the same value as before.
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
Expand Down Expand Up @@ -659,19 +683,19 @@ impl<'a> SummariesCache<'a> {
.split_first()
.ok_or_else(|| anyhow::format_err!("malformed cache"))?;
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);
if let Some(update) = iter.next() {
if update != last_index_update.as_bytes() {
anyhow::bail!(
bail!(
"cache out of date: current index ({}) != cache ({})",
last_index_update,
str::from_utf8(update)?,
)
}
} else {
anyhow::bail!("malformed file");
bail!("malformed file");
}
let mut ret = SummariesCache::default();
while let Some(version) = iter.next() {
Expand Down Expand Up @@ -749,7 +773,9 @@ impl IndexSummary {
features,
yanked,
links,
v,
} = serde_json::from_slice(line)?;
let v = v.unwrap_or(1);
log::trace!("json parsed registry {}/{}", name, vers);
let pkgid = PackageId::new(name, &vers, source_id)?;
let deps = deps
Expand All @@ -761,6 +787,7 @@ impl IndexSummary {
Ok(IndexSummary {
summary,
yanked: yanked.unwrap_or(false),
v,
})
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/cargo/sources/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,24 @@ pub struct RegistryPackage<'a> {
/// Added early 2018 (see <https://github.com/rust-lang/cargo/pull/4978>),
/// can be `None` if published before then.
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]
Expand Down
30 changes: 30 additions & 0 deletions tests/testsuite/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2170,3 +2170,33 @@ fn package_lock_inside_package_is_overwritten() {

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();
}

0 comments on commit 19fd5cb

Please sign in to comment.