Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rust-version field to the database and index #6267

Merged
merged 8 commits into from
Apr 26, 2023
4 changes: 4 additions & 0 deletions cargo-registry-index/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ pub struct Crate {
pub yanked: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub links: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rust_version: Option<String>,
epage marked this conversation as resolved.
Show resolved Hide resolved
/// The schema version for this entry.
///
/// If this is None, it defaults to version 1. Entries with unknown
Expand Down Expand Up @@ -634,6 +636,7 @@ mod tests {
features2: None,
yanked: None,
links: None,
rust_version: None,
v: None,
};
let mut buffer = Vec::new();
Expand All @@ -657,6 +660,7 @@ mod tests {
features2: None,
yanked: None,
links: None,
rust_version: None,
v: None,
})
.collect::<Vec<_>>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE versions DROP COLUMN rust_version;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE versions ADD COLUMN rust_version VARCHAR;
14 changes: 2 additions & 12 deletions src/admin/render_readmes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{
models::Version,
schema::{crates, readme_renderings, versions},
uploaders::Uploader,
util::manifest::Manifest,
};
use anyhow::{anyhow, Context};
use std::{io::Read, path::Path, sync::Arc, thread};
Expand Down Expand Up @@ -200,18 +201,7 @@ fn render_pkg_readme<R: Read>(mut archive: Archive<R>, pkg_name: &str) -> anyhow
pkg_path_in_vcs,
)
};
return Ok(rendered);

#[derive(Debug, Deserialize)]
struct Package {
readme: Option<String>,
repository: Option<String>,
}

#[derive(Debug, Deserialize)]
struct Manifest {
package: Package,
}
Ok(rendered)
}

/// Search an entry by its path in a Tar archive.
Expand Down
73 changes: 62 additions & 11 deletions src/controllers/krate/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use hyper::body::Buf;
use sha2::{Digest, Sha256};
use std::collections::BTreeMap;
use std::io::Read;
use std::ops::Deref;
use std::path::Path;

use crate::controllers::cargo_prelude::*;
Expand All @@ -22,6 +23,7 @@ use crate::middleware::log_request::RequestLogExt;
use crate::models::token::EndpointScope;
use crate::schema::*;
use crate::util::errors::{cargo_err, AppResult};
use crate::util::manifest::Manifest;
use crate::util::{CargoVcsInfo, LimitErrorReader, Maximums};
use crate::views::{
EncodableCrate, EncodableCrateDependency, EncodableCrateUpload, GoodCrate, PublishWarnings,
Expand Down Expand Up @@ -188,6 +190,14 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
// Read tarball from request
let hex_cksum: String = Sha256::digest(&tarball_bytes).encode_hex();

let pkg_name = format!("{}-{}", krate.name, vers);
let tarball_info = verify_tarball(&pkg_name, &tarball_bytes, maximums.max_unpack_size)?;

let rust_version = tarball_info
.manifest
.and_then(|m| m.package.rust_version)
.map(|rv| rv.deref().to_string());

// Persist the new version of this crate
let version = NewVersion::new(
krate.id,
Expand All @@ -201,6 +211,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
user.id,
hex_cksum.clone(),
links.clone(),
rust_version.clone(),
)?
.save(conn, &verified_email_address)?;

Expand All @@ -224,10 +235,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra

let top_versions = krate.top_versions(conn)?;

let pkg_name = format!("{}-{}", krate.name, vers);
let cargo_vcs_info =
verify_tarball(&pkg_name, &tarball_bytes, maximums.max_unpack_size)?;
let pkg_path_in_vcs = cargo_vcs_info.map(|info| info.path_in_vcs);
let pkg_path_in_vcs = tarball_info.vcs_info.map(|info| info.path_in_vcs);

if let Some(readme) = new_crate.readme {
Job::render_and_upload_readme(
Expand Down Expand Up @@ -269,6 +277,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
deps: git_deps,
yanked: Some(false),
links,
rust_version,
v,
};

Expand Down Expand Up @@ -426,12 +435,14 @@ pub fn add_dependencies(
Ok(git_deps)
}

#[derive(Debug)]
struct TarballInfo {
manifest: Option<Manifest>,
vcs_info: Option<CargoVcsInfo>,
}

#[instrument(skip_all, fields(%pkg_name))]
fn verify_tarball(
pkg_name: &str,
tarball: &[u8],
max_unpack: u64,
) -> AppResult<Option<CargoVcsInfo>> {
fn verify_tarball(pkg_name: &str, tarball: &[u8], max_unpack: u64) -> AppResult<TarballInfo> {
// All our data is currently encoded with gzip
let decoder = GzDecoder::new(tarball);

Expand All @@ -445,6 +456,9 @@ fn verify_tarball(
let vcs_info_path = Path::new(&pkg_name).join(".cargo_vcs_info.json");
let mut vcs_info = None;

let manifest_path = Path::new(&pkg_name).join("Cargo.toml");
let mut manifest = None;

for entry in archive.entries()? {
let mut entry = entry.map_err(|err| {
err.chain(cargo_err(
Expand All @@ -465,6 +479,12 @@ fn verify_tarball(
let mut contents = String::new();
entry.read_to_string(&mut contents)?;
vcs_info = CargoVcsInfo::from_contents(&contents).ok();
} else if entry_path == manifest_path {
// Try to extract and read the Cargo.toml from the tarball, silently
// erroring if it cannot be read.
let mut contents = String::new();
entry.read_to_string(&mut contents)?;
manifest = toml::from_str(&contents).ok();
}

// Historical versions of the `tar` crate which Cargo uses internally
Expand All @@ -477,7 +497,8 @@ fn verify_tarball(
return Err(cargo_err("invalid tarball uploaded"));
}
}
Ok(vcs_info)

Ok(TarballInfo { manifest, vcs_info })
}

#[cfg(test)]
Expand Down Expand Up @@ -505,7 +526,9 @@ mod tests {

let limit = 512 * 1024 * 1024;
assert_eq!(
verify_tarball("foo-0.0.1", &serialized_archive, limit).unwrap(),
verify_tarball("foo-0.0.1", &serialized_archive, limit)
.unwrap()
.vcs_info,
None
);
assert_err!(verify_tarball("bar-0.0.1", &serialized_archive, limit));
Expand All @@ -527,6 +550,7 @@ mod tests {
let limit = 512 * 1024 * 1024;
let vcs_info = verify_tarball("foo-0.0.1", &serialized_archive, limit)
.unwrap()
.vcs_info
.unwrap();
assert_eq!(vcs_info.path_in_vcs, "");
}
Expand All @@ -547,7 +571,34 @@ mod tests {
let limit = 512 * 1024 * 1024;
let vcs_info = verify_tarball("foo-0.0.1", &serialized_archive, limit)
.unwrap()
.vcs_info
.unwrap();
assert_eq!(vcs_info.path_in_vcs, "path/in/vcs");
}

#[test]
fn verify_tarball_test_manifest() {
let mut pkg = tar::Builder::new(vec![]);
add_file(
&mut pkg,
"foo-0.0.1/Cargo.toml",
br#"
[package]
rust_version = "1.59"
readme = "README.md"
repository = "https://github.com/foo/bar"
"#,
);
let mut serialized_archive = vec![];
GzEncoder::new(pkg.into_inner().unwrap().as_slice(), Default::default())
.read_to_end(&mut serialized_archive)
.unwrap();

let limit = 512 * 1024 * 1024;
let tarball_info = assert_ok!(verify_tarball("foo-0.0.1", &serialized_archive, limit));
let manifest = assert_some!(tarball_info.manifest);
assert_some_eq!(manifest.package.readme, "README.md");
assert_some_eq!(manifest.package.repository, "https://github.com/foo/bar");
assert_some_eq!(manifest.package.rust_version, "1.59");
}
}
1 change: 1 addition & 0 deletions src/downloads_counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ mod tests {
self.user.id,
"0000000000000000000000000000000000000000000000000000000000000000".to_string(),
None,
None,
)
.expect("failed to create version")
.save(conn, "ghost@example.com")
Expand Down
1 change: 1 addition & 0 deletions src/models/krate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ impl Crate {
deps,
features,
links: version.links,
rust_version: version.rust_version,
features2,
v,
};
Expand Down
4 changes: 4 additions & 0 deletions src/models/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct Version {
pub published_by: Option<i32>,
pub checksum: String,
pub links: Option<String>,
pub rust_version: Option<String>,
}

#[derive(Insertable, Debug)]
Expand All @@ -38,6 +39,7 @@ pub struct NewVersion {
published_by: i32,
checksum: String,
links: Option<String>,
rust_version: Option<String>,
}

/// The highest version (semver order) and the most recently updated version.
Expand Down Expand Up @@ -139,6 +141,7 @@ impl NewVersion {
published_by: i32,
checksum: String,
links: Option<String>,
rust_version: Option<String>,
) -> AppResult<Self> {
let features = serde_json::to_value(features)?;

Expand All @@ -151,6 +154,7 @@ impl NewVersion {
published_by,
checksum,
links,
rust_version,
};

new_version.validate_license(license_file)?;
Expand Down
6 changes: 6 additions & 0 deletions src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,12 @@ diesel::table! {
///
/// (Automatically generated by Diesel.)
links -> Nullable<Varchar>,
/// The `rust_version` column of the `versions` table.
///
/// Its SQL type is `Nullable<Varchar>`.
///
/// (Automatically generated by Diesel.)
rust_version -> Nullable<Varchar>,
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/tests/builders/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct VersionBuilder<'a> {
yanked: bool,
checksum: String,
links: Option<String>,
rust_version: Option<String>,
}

impl<'a> VersionBuilder<'a> {
Expand All @@ -45,6 +46,7 @@ impl<'a> VersionBuilder<'a> {
yanked: false,
checksum: String::new(),
links: None,
rust_version: None,
}
}

Expand Down Expand Up @@ -83,6 +85,12 @@ impl<'a> VersionBuilder<'a> {
self
}

/// Sets the version's `rust_version` value.
pub fn rust_version(mut self, rust_version: &str) -> Self {
self.rust_version = Some(rust_version.to_owned());
self
}

pub fn build(
self,
crate_id: i32,
Expand All @@ -103,6 +111,7 @@ impl<'a> VersionBuilder<'a> {
published_by,
self.checksum,
self.links,
self.rust_version,
)?
.save(connection, "someone@example.com")?;

Expand Down
5 changes: 3 additions & 2 deletions src/tests/krate/versions.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::builders::CrateBuilder;
use crate::builders::{CrateBuilder, VersionBuilder};
use crate::util::{RequestHelper, TestApp};
use cargo_registry::schema::versions;
use cargo_registry::views::EncodableVersion;
Expand All @@ -16,7 +16,7 @@ fn versions() {
app.db(|conn| {
CrateBuilder::new("foo_versions", user.id)
.version("0.5.1")
.version("1.0.0")
.version(VersionBuilder::new("1.0.0").rust_version("1.64"))
.version("0.5.0")
.expect_build(conn);
// Make version 1.0.0 mimic a version published before we started recording who published
Expand All @@ -33,6 +33,7 @@ fn versions() {

assert_eq!(json.versions.len(), 3);
assert_eq!(json.versions[0].num, "1.0.0");
assert_some_eq!(&json.versions[0].rust_version, "1.64");
assert_eq!(json.versions[1].num, "0.5.1");
assert_eq!(json.versions[2].num, "0.5.0");
assert_none!(&json.versions[0].published_by);
Expand Down
1 change: 1 addition & 0 deletions src/tests/routes/crates/versions/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fn show_by_crate_name_and_version() {
VersionBuilder::new("2.0.0")
.size(1234)
.checksum("c241cd77c3723ccf1aa453f169ee60c0a888344da504bee0142adb859092acb4")
.rust_version("1.64")
.expect_build(krate.id, user.id, conn)
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ version:
num: 1.0.0
published_by: ~
readme_path: /api/v1/crates/foo_vers_show_no_pb/1.0.0/readme
rust_version: ~
updated_at: "[datetime]"
yanked: false

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
source: src/tests/routes/crates/versions/read.rs
assertion_line: 22
assertion_line: 23
expression: json
---
version:
Expand All @@ -26,6 +26,7 @@ version:
name: ~
url: "https://github.com/foo"
readme_path: /api/v1/crates/foo_vers_show/2.0.0/readme
rust_version: "1.64"
updated_at: "[datetime]"
yanked: false

Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ versions:
name: ~
url: "https://github.com/foo"
readme_path: /api/v1/crates/foo_vers_index/2.0.0/readme
rust_version: ~
updated_at: "[datetime]"
yanked: false
- audit_actions: []
Expand All @@ -50,6 +51,7 @@ versions:
name: ~
url: "https://github.com/foo"
readme_path: /api/v1/crates/foo_vers_index/2.0.1/readme
rust_version: ~
updated_at: "[datetime]"
yanked: false

Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ version:
name: ~
url: "https://github.com/foo"
readme_path: /api/v1/crates/foo_vers_show_id/2.0.0/readme
rust_version: ~
updated_at: "[datetime]"
yanked: false

Loading