//! Tests for including `Cargo.lock` when publishing/packaging. use std::fs::File; use cargo_test_support::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::{ basic_manifest, cargo_process, git, paths, project, publish::validate_crate_contents, str, }; fn pl_manifest(name: &str, version: &str, extra: &str) -> String { format!( r#" [package] name = "{}" version = "{}" edition = "2015" authors = [] license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" {} "#, name, version, extra ) } #[cargo_test] fn removed() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["publish-lockfile"] [package] name = "foo" version = "0.1.0" edition = "2015" publish-lockfile = true license = "MIT" description = "foo" documentation = "foo" homepage = "foo" repository = "foo" "#, ) .file("src/lib.rs", "") .build(); p.cargo("package") .masquerade_as_nightly_cargo(&["publish-lockfile"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: the cargo feature `publish-lockfile` has been removed in the 1.37 release Remove the feature from Cargo.toml to remove this error. See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#publish-lockfile for more information about using this feature. "#]]) .run(); } #[cargo_test] fn package_lockfile() { let p = project() .file("Cargo.toml", &pl_manifest("foo", "0.0.1", "")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); p.cargo("package -l") .with_stdout_data(str![[r#" Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("package") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "src/main.rs"], &[], ); } #[cargo_test] fn package_lockfile_git_repo() { // Create a Git repository containing a minimal Rust project. let g = git::repo(&paths::root().join("foo")) .file("Cargo.toml", &pl_manifest("foo", "0.0.1", "")) .file("src/main.rs", "fn main() {}") .build(); cargo_process("package -l") .cwd(g.root()) .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); cargo_process("package -v") .cwd(g.root()) .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [ARCHIVING] .cargo_vcs_info.json [ARCHIVING] Cargo.lock [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn no_lock_file_with_library() { let p = project() .file("Cargo.toml", &pl_manifest("foo", "0.0.1", "")) .file("src/lib.rs", "") .build(); p.cargo("package").run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], &[], ); } #[cargo_test] fn lock_file_and_workspace() { let p = project() .file( "Cargo.toml", r#" [workspace] members = ["foo"] "#, ) .file("foo/Cargo.toml", &pl_manifest("foo", "0.0.1", "")) .file("foo/src/main.rs", "fn main() {}") .build(); p.cargo("package").cwd("foo").run(); let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.toml", "Cargo.toml.orig", "src/main.rs", "Cargo.lock"], &[], ); } #[cargo_test] fn note_resolve_changes() { // `multi` has multiple sources (path and registry). Package::new("multi", "0.1.0").publish(); // `updated` is always from registry, but should not change. Package::new("updated", "1.0.0").publish(); // `patched` is [patch]ed. Package::new("patched", "1.0.0").publish(); let p = project() .file( "Cargo.toml", &pl_manifest( "foo", "0.0.1", r#" [dependencies] multi = { path = "multi", version = "0.1" } updated = "1.0" patched = "1.0" [patch.crates-io] patched = { path = "patched" } "#, ), ) .file("src/main.rs", "fn main() {}") .file("multi/Cargo.toml", &basic_manifest("multi", "0.1.0")) .file("multi/src/lib.rs", "") .file("patched/Cargo.toml", &basic_manifest("patched", "1.0.0")) .file("patched/src/lib.rs", "") .build(); p.cargo("generate-lockfile").run(); // Make sure this does not change or warn. Package::new("updated", "1.0.1").publish(); p.cargo("package --no-verify -v --allow-dirty") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [ARCHIVING] Cargo.lock [UPDATING] `dummy-registry` index [NOTE] package `multi v0.1.0` added to the packaged Cargo.lock file, was originally sourced from `[ROOT]/foo/multi` [NOTE] package `patched v1.0.0` added to the packaged Cargo.lock file, was originally sourced from `[ROOT]/foo/patched` [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [WARNING] no (git) Cargo.toml found at `[..]/foo/Cargo.toml` in workdir `[..]` "#]].unordered()) .run(); } #[cargo_test] fn outdated_lock_version_change_does_not_warn() { // If the version of the package being packaged changes, but Cargo.lock is // not updated, don't bother warning about it. let p = project() .file("Cargo.toml", &pl_manifest("foo", "0.1.0", "")) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile").run(); p.change_file("Cargo.toml", &pl_manifest("foo", "0.2.0", "")); p.cargo("package --no-verify") .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [UPDATING] foo v0.1.0 ([ROOT]/foo) -> v0.2.0 [PACKAGING] foo v0.2.0 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); } #[cargo_test] fn no_warn_workspace_extras() { // Other entries in workspace lock file should be ignored. Package::new("dep1", "1.0.0").publish(); Package::new("dep2", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", &pl_manifest( "a", "0.1.0", r#" [dependencies] dep1 = "1.0" "#, ), ) .file("a/src/main.rs", "fn main() {}") .file( "b/Cargo.toml", &pl_manifest( "b", "0.1.0", r#" [dependencies] dep2 = "1.0" "#, ), ) .file("b/src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile").run(); p.cargo("package --no-verify") .cwd("a") .with_stderr_data(str![[r#" [PACKAGING] a v0.1.0 ([ROOT]/foo/a) [UPDATING] `dummy-registry` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); } #[cargo_test] fn warn_package_with_yanked() { Package::new("bar", "0.1.0").publish(); let p = project() .file( "Cargo.toml", &pl_manifest( "foo", "0.0.1", r#" [dependencies] bar = "0.1" "#, ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("generate-lockfile").run(); Package::new("bar", "0.1.0").yanked(true).publish(); // Make sure it sticks with the locked (yanked) version. Package::new("bar", "0.1.1").publish(); p.cargo("package --no-verify") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [UPDATING] `dummy-registry` index [WARNING] package `bar v0.1.0` in Cargo.lock is yanked in registry `crates-io`, consider updating to a version that is not yanked [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); } #[cargo_test] fn warn_install_with_yanked() { Package::new("bar", "0.1.0").yanked(true).publish(); Package::new("bar", "0.1.1").publish(); Package::new("foo", "0.1.0") .dep("bar", "0.1") .file("src/main.rs", "fn main() {}") .file( "Cargo.lock", r#" [[package]] name = "bar" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "foo" version = "0.1.0" dependencies = [ "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] "#, ) .publish(); cargo_process("install --locked foo") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... [DOWNLOADED] foo v0.1.0 (registry `dummy-registry`) [INSTALLING] foo v0.1.0 [WARNING] package `bar v0.1.0` in Cargo.lock is yanked in registry `crates-io`, consider running without --locked [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.0 (registry `dummy-registry`) [COMPILING] bar v0.1.0 [COMPILING] foo v0.1.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE] [INSTALLED] package `foo v0.1.0` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); // Try again without --locked, make sure it uses 0.1.1 and does not warn. cargo_process("install --force foo") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [INSTALLING] foo v0.1.0 [LOCKING] 2 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] bar v0.1.1 (registry `dummy-registry`) [COMPILING] bar v0.1.1 [COMPILING] foo v0.1.0 [FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s [REPLACING] [ROOT]/home/.cargo/bin/foo[EXE] [REPLACED] package `foo v0.1.0` with `foo v0.1.0` (executable `foo[EXE]`) [WARNING] be sure to add `[ROOT]/home/.cargo/bin` to your PATH to be able to run the installed binaries "#]]) .run(); } #[cargo_test] fn ignore_lockfile() { // With an explicit `include` list, but Cargo.lock in .gitignore, don't // complain about `Cargo.lock` being ignored. Note that it is still // included in the packaged regardless. let p = git::new("foo", |p| { p.file( "Cargo.toml", &pl_manifest( "foo", "0.0.1", r#" include = [ "src/main.rs" ] "#, ), ) .file("src/main.rs", "fn main() {}") .file(".gitignore", "Cargo.lock") }); p.cargo("package -l") .with_stdout_data(str![[r#" .cargo_vcs_info.json Cargo.lock Cargo.toml Cargo.toml.orig src/main.rs "#]]) .run(); p.cargo("generate-lockfile").run(); p.cargo("package -v") .with_stderr_data(str![[r#" [PACKAGING] foo v0.0.1 ([ROOT]/foo) [ARCHIVING] .cargo_vcs_info.json [ARCHIVING] Cargo.lock [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn ignore_lockfile_inner() { // Ignore `Cargo.lock` if in .gitignore in a git subdirectory. let p = git::new("foo", |p| { p.no_manifest() .file("bar/Cargo.toml", &pl_manifest("bar", "0.0.1", "")) .file("bar/src/main.rs", "fn main() {}") .file("bar/.gitignore", "Cargo.lock") }); p.cargo("generate-lockfile").cwd("bar").run(); p.cargo("package -v --no-verify") .cwd("bar") .with_stderr_data(str![[r#" [PACKAGING] bar v0.0.1 ([ROOT]/foo/bar) [ARCHIVING] .cargo_vcs_info.json [ARCHIVING] .gitignore [ARCHIVING] Cargo.lock [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs [PACKAGED] 6 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); } #[cargo_test] fn use_workspace_root_lockfile() { // Issue #11148 // Workspace members should use `Cargo.lock` at workspace root Package::new("serde", "0.2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "foo" [dependencies] serde = "0.2" [workspace] members = ["bar"] "#, ) .file("src/main.rs", "fn main() {}") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] license = "MIT" description = "bar" workspace = ".." [dependencies] serde = "0.2" "#, ) .file("bar/src/main.rs", "fn main() {}") .build(); // Create `Cargo.lock` in the workspace root. p.cargo("generate-lockfile").run(); // Now, add a newer version of `serde`. Package::new("serde", "0.2.1").publish(); // Expect: package `bar` uses `serde v0.2.0` as required by workspace `Cargo.lock`. p.cargo("package --workspace") .with_stderr_data(str![[r#" [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] bar v0.0.1 ([ROOT]/foo/bar) [UPDATING] `dummy-registry` index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] bar v0.0.1 ([ROOT]/foo/bar) [DOWNLOADING] crates ... [DOWNLOADED] serde v0.2.0 (registry `dummy-registry`) [COMPILING] serde v0.2.0 [COMPILING] bar v0.0.1 ([ROOT]/foo/target/package/bar-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) [COMPILING] serde v0.2.0 [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let package_path = p.root().join("target/package/foo-0.0.1.crate"); assert!(package_path.is_file()); let f = File::open(&package_path).unwrap(); validate_crate_contents( f, "foo-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], &[], ); let package_path = p.root().join("target/package/bar-0.0.1.crate"); assert!(package_path.is_file()); let f = File::open(&package_path).unwrap(); validate_crate_contents( f, "bar-0.0.1.crate", &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], &[], ); }