Package lock files in published crates

Previously we had logic to explicitly skip lock files but there's actually a
good case to read these from crates.io (#2263) so let's do so!

Closes #2263
This commit is contained in:
Alex Crichton 2018-02-27 07:56:04 -08:00
parent 8b475c1085
commit a4a3302d46
6 changed files with 233 additions and 1 deletions

View File

@ -162,6 +162,9 @@ features! {
// Renaming a package in the manifest via the `package` key
[unstable] rename_dependency: bool,
// Whether a lock file is published with this crate
[unstable] publish_lockfile: bool,
}
}

View File

@ -31,6 +31,7 @@ pub struct Manifest {
metadata: ManifestMetadata,
profiles: Profiles,
publish: Option<Vec<String>>,
publish_lockfile: bool,
replace: Vec<(PackageIdSpec, Dependency)>,
patch: HashMap<Url, Vec<Dependency>>,
workspace: WorkspaceConfig,
@ -267,6 +268,7 @@ impl Manifest {
metadata: ManifestMetadata,
profiles: Profiles,
publish: Option<Vec<String>>,
publish_lockfile: bool,
replace: Vec<(PackageIdSpec, Dependency)>,
patch: HashMap<Url, Vec<Dependency>>,
workspace: WorkspaceConfig,
@ -291,6 +293,7 @@ impl Manifest {
epoch,
original,
im_a_teapot,
publish_lockfile,
}
}
@ -306,6 +309,7 @@ impl Manifest {
pub fn warnings(&self) -> &[DelayedWarning] { &self.warnings }
pub fn profiles(&self) -> &Profiles { &self.profiles }
pub fn publish(&self) -> &Option<Vec<String>> { &self.publish }
pub fn publish_lockfile(&self) -> bool { self.publish_lockfile }
pub fn replace(&self) -> &[(PackageIdSpec, Dependency)] { &self.replace }
pub fn original(&self) -> &TomlManifest { &self.original }
pub fn patch(&self) -> &HashMap<Url, Vec<Dependency>> { &self.patch }

View File

@ -12,6 +12,7 @@ use tar::{Archive, Builder, Header, EntryType};
use core::{Package, Workspace, Source, SourceId};
use sources::PathSource;
use util::{self, internal, Config, FileLock};
use util::paths;
use util::errors::{CargoResult, CargoResultExt};
use ops::{self, DefaultExecutor};
@ -28,6 +29,7 @@ pub struct PackageOpts<'cfg> {
pub fn package(ws: &Workspace,
opts: &PackageOpts) -> CargoResult<Option<FileLock>> {
ops::resolve_ws(ws)?;
let pkg = ws.current()?;
let config = ws.config();
@ -47,6 +49,9 @@ pub fn package(ws: &Workspace,
let mut list: Vec<_> = src.list_files(pkg)?.iter().map(|file| {
util::without_prefix(file, root).unwrap().to_path_buf()
}).collect();
if include_lockfile(&pkg) {
list.push("Cargo.lock".into());
}
list.sort();
for file in list.iter() {
println!("{}", file.display());
@ -91,6 +96,11 @@ pub fn package(ws: &Workspace,
Ok(Some(dst))
}
fn include_lockfile(pkg: &Package) -> bool {
pkg.manifest().publish_lockfile() &&
pkg.targets().iter().any(|t| t.is_example() || t.is_bin())
}
// check that the package has some piece of metadata that a human can
// use to tell what the package is about.
fn check_metadata(pkg: &Package, config: &Config) -> CargoResult<()> {
@ -265,6 +275,22 @@ fn tar(ws: &Workspace,
})?;
}
}
if include_lockfile(pkg) {
let toml = paths::read(&ws.root().join("Cargo.lock"))?;
let path = format!("{}-{}{}Cargo.lock", pkg.name(), pkg.version(),
path::MAIN_SEPARATOR);
let mut header = Header::new_ustar();
header.set_path(&path)?;
header.set_entry_type(EntryType::file());
header.set_mode(0o644);
header.set_size(toml.len() as u64);
header.set_cksum();
ar.append(&header, toml.as_bytes()).chain_err(|| {
internal("could not archive source file `Cargo.lock`")
})?;
}
let encoder = ar.into_inner()?;
encoder.finish()?;
Ok(())

View File

@ -426,6 +426,8 @@ pub struct TomlProject {
exclude: Option<Vec<String>>,
include: Option<Vec<String>>,
publish: Option<VecStringOrBool>,
#[serde(rename = "publish-lockfile")]
publish_lockfile: Option<bool>,
workspace: Option<String>,
#[serde(rename = "im-a-teapot")]
im_a_teapot: Option<bool>,
@ -719,6 +721,14 @@ impl TomlManifest {
None | Some(VecStringOrBool::Bool(true)) => None,
};
let publish_lockfile = match project.publish_lockfile {
Some(b) => {
features.require(Feature::publish_lockfile())?;
b
}
None => false,
};
let epoch = if let Some(ref epoch) = project.rust {
features.require(Feature::epoch()).chain_err(|| {
"epoches are unstable"
@ -739,6 +749,7 @@ impl TomlManifest {
metadata,
profiles,
publish,
publish_lockfile,
replace,
patch,
workspace_config,

View File

@ -1044,3 +1044,33 @@ dependencies = [
assert_that(cargo_process("install").arg("foo"),
execs().with_status(0));
}
#[test]
fn lock_file_path_deps_ok() {
Package::new("bar", "0.1.0").publish();
Package::new("foo", "0.1.0")
.dep("bar", "0.1")
.file("src/lib.rs", "")
.file("src/main.rs", "
extern crate foo;
extern crate bar;
fn main() {}
")
.file("Cargo.lock", r#"
[[package]]
name = "bar"
version = "0.1.0"
[[package]]
name = "foo"
version = "0.1.0"
dependencies = [
"bar 0.1.0",
]
"#)
.publish();
assert_that(cargo_process("install").arg("foo"),
execs().with_status(0));
}

View File

@ -702,8 +702,9 @@ to proceed despite this, pass the `--allow-dirty` flag
#[test]
fn generated_manifest() {
Package::new("abc", "1.0.0").publish();
Package::new("def", "1.0.0").publish();
Package::new("def", "1.0.0").alternative(true).publish();
Package::new("ghi", "1.0.0").publish();
let p = project("foo")
.file("Cargo.toml", r#"
cargo-features = ["alternative-registries"]
@ -995,3 +996,160 @@ Caused by:
consider adding `cargo-features = [\"epoch\"]` to the manifest
")));
}
#[test]
fn package_lockfile() {
let p = project("foo")
.file("Cargo.toml", r#"
cargo-features = ["publish-lockfile"]
[project]
name = "foo"
version = "0.0.1"
authors = []
license = "MIT"
description = "foo"
publish-lockfile = true
"#)
.file("src/main.rs", "fn main() {}")
.build();
assert_that(p.cargo("package").masquerade_as_nightly_cargo(),
execs().with_status(0).with_stderr(&format!("\
[WARNING] manifest has no documentation[..]
See [..]
[PACKAGING] foo v0.0.1 ({dir})
[VERIFYING] foo v0.0.1 ({dir})
[COMPILING] foo v0.0.1 ({dir}[..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
dir = p.url())));
assert_that(&p.root().join("target/package/foo-0.0.1.crate"), existing_file());
assert_that(p.cargo("package").arg("-l").masquerade_as_nightly_cargo(),
execs().with_status(0).with_stdout("\
Cargo.lock
Cargo.toml
src[/]main.rs
"));
assert_that(p.cargo("package").masquerade_as_nightly_cargo(),
execs().with_status(0).with_stdout(""));
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
let mut rdr = GzDecoder::new(f);
let mut contents = Vec::new();
rdr.read_to_end(&mut contents).unwrap();
let mut ar = Archive::new(&contents[..]);
for f in ar.entries().unwrap() {
let f = f.unwrap();
let fname = f.header().path_bytes();
let fname = &*fname;
assert!(fname == b"foo-0.0.1/Cargo.toml" ||
fname == b"foo-0.0.1/Cargo.toml.orig" ||
fname == b"foo-0.0.1/Cargo.lock" ||
fname == b"foo-0.0.1/src/main.rs",
"unexpected filename: {:?}", f.header().path())
}
}
#[test]
fn package_lockfile_git_repo() {
let p = project("foo").build();
// Create a Git repository containing a minimal Rust project.
let _ = git::repo(&paths::root().join("foo"))
.file("Cargo.toml", r#"
cargo-features = ["publish-lockfile"]
[project]
name = "foo"
version = "0.0.1"
license = "MIT"
description = "foo"
documentation = "foo"
homepage = "foo"
repository = "foo"
publish-lockfile = true
"#)
.file("src/main.rs", "fn main() {}")
.build();
assert_that(p.cargo("package").arg("-l").masquerade_as_nightly_cargo(),
execs().with_status(0).with_stdout("\
Cargo.lock
Cargo.toml
src/main.rs
"));
}
#[test]
fn no_lock_file_with_library() {
let p = project("foo")
.file("Cargo.toml", r#"
cargo-features = ["publish-lockfile"]
[project]
name = "foo"
version = "0.0.1"
authors = []
license = "MIT"
description = "foo"
publish-lockfile = true
"#)
.file("src/lib.rs", "")
.build();
assert_that(p.cargo("package").masquerade_as_nightly_cargo(),
execs().with_status(0));
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
let mut rdr = GzDecoder::new(f);
let mut contents = Vec::new();
rdr.read_to_end(&mut contents).unwrap();
let mut ar = Archive::new(&contents[..]);
for f in ar.entries().unwrap() {
let f = f.unwrap();
let fname = f.header().path().unwrap();
assert!(!fname.ends_with("Cargo.lock"));
}
}
#[test]
fn lock_file_and_workspace() {
let p = project("foo")
.file("Cargo.toml", r#"
[workspace]
members = ["foo"]
"#)
.file("foo/Cargo.toml", r#"
cargo-features = ["publish-lockfile"]
[package]
name = "foo"
version = "0.0.1"
authors = []
license = "MIT"
description = "foo"
publish-lockfile = true
"#)
.file("foo/src/main.rs", "fn main() {}")
.build();
assert_that(p.cargo("package")
.cwd(p.root().join("foo"))
.masquerade_as_nightly_cargo(),
execs().with_status(0));
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
let mut rdr = GzDecoder::new(f);
let mut contents = Vec::new();
rdr.read_to_end(&mut contents).unwrap();
let mut ar = Archive::new(&contents[..]);
assert!(
ar.entries().unwrap()
.into_iter()
.any(|f|{
let f = f.unwrap();
let fname = f.header().path().unwrap();
fname.ends_with("Cargo.lock")
})
);
}