Skip to content

Commit

Permalink
Allow unpublishable crates to be packaged
Browse files Browse the repository at this point in the history
  • Loading branch information
jneem committed Aug 19, 2024
1 parent e7073ea commit 4ee3aef
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 37 deletions.
9 changes: 7 additions & 2 deletions src/cargo/ops/cargo_package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Vec<Fi
} else {
let tarball = create_package(ws, &pkg, ar_files, local_reg.as_ref())?;
if let Some(local_reg) = local_reg.as_mut() {
local_reg.add_package(ws, &pkg, &tarball)?;
if pkg.publish() != &Some(Vec::new()) {
local_reg.add_package(ws, &pkg, &tarball)?;
}
}
outputs.push((pkg, opts, tarball));
}
Expand Down Expand Up @@ -290,7 +292,10 @@ fn get_registry(
if let RegistryOrIndex::Registry(reg_name) = reg {
for pkg in pkgs {
if let Some(allowed) = pkg.publish().as_ref() {
if !allowed.iter().any(|a| a == &reg_name) {
// If allowed is empty (i.e. package.publish is false), we let it slide.
// This allows packaging unpublishable packages (although packaging might
// fail later if the unpublishable package is a dependency of something else).
if !allowed.is_empty() && !allowed.iter().any(|a| a == &reg_name) {
bail!(
"`{}` cannot be packaged.\n\
The registry `{}` is not listed in the `package.publish` value in Cargo.toml.",
Expand Down
68 changes: 39 additions & 29 deletions src/cargo/ops/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,39 +325,49 @@ pub(crate) struct RegistrySourceIds {

/// If this set of packages has an unambiguous publish registry, find it.
pub(crate) fn infer_registry(pkgs: &[&Package]) -> CargoResult<Option<RegistryOrIndex>> {
if pkgs[1..].iter().all(|p| p.publish() == pkgs[0].publish()) {
// Ignore "publish = false" packages while inferring the registry.
let publishable_pkgs: Vec<_> = pkgs
.iter()
.filter(|p| p.publish() != &Some(Vec::new()))
.collect();

if let Some((first, rest)) = publishable_pkgs.split_first() {
// If all packages have the same publish settings, we take that as the default.
match pkgs[0].publish().as_deref() {
Some([unique_pkg_reg]) => {
Ok(Some(RegistryOrIndex::Registry(unique_pkg_reg.to_owned())))
if rest.iter().all(|p| p.publish() == first.publish()) {
match publishable_pkgs[0].publish().as_deref() {
Some([unique_pkg_reg]) => {
Ok(Some(RegistryOrIndex::Registry(unique_pkg_reg.to_owned())))
}
None | Some([]) => Ok(None),
Some(regs) => {
let mut regs: Vec<_> = regs.iter().map(|s| format!("\"{}\"", s)).collect();
regs.sort();
regs.dedup();
// unwrap: the match block ensures that there's more than one reg.
let (last_reg, regs) = regs.split_last().unwrap();
bail!(
"--registry is required to disambiguate between {} or {} registries",
regs.join(", "),
last_reg
)
}
}
None | Some([]) => Ok(None),
Some(regs) => {
let mut regs: Vec<_> = regs.iter().map(|s| format!("\"{}\"", s)).collect();
regs.sort();
regs.dedup();
// unwrap: the match block ensures that there's more than one reg.
let (last_reg, regs) = regs.split_last().unwrap();
bail!(
"--registry is required to disambiguate between {} or {} registries",
regs.join(", "),
last_reg
)
} else {
let common_regs = publishable_pkgs
.iter()
// `None` means "all registries", so drop them instead of including them
// in the intersection.
.filter_map(|p| p.publish().as_deref())
.map(|p| p.iter().collect::<HashSet<_>>())
.reduce(|xs, ys| xs.intersection(&ys).cloned().collect())
.unwrap_or_default();
if common_regs.is_empty() {
bail!("conflicts between `package.publish` fields in the selected packages");
} else {
bail!("--registry is required because not all `package.publish` settings agree",);
}
}
} else {
let common_regs = pkgs
.iter()
// `None` means "all registries", so drop them instead of including them
// in the intersection.
.filter_map(|p| p.publish().as_deref())
.map(|p| p.iter().collect::<HashSet<_>>())
.reduce(|xs, ys| xs.intersection(&ys).cloned().collect())
.unwrap_or_default();
if common_regs.is_empty() {
bail!("conflicts between `package.publish` fields in the selected packages");
} else {
bail!("--registry is required because not all `package.publish` settings agree",);
}
Ok(None)
}
}
43 changes: 37 additions & 6 deletions tests/testsuite/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6227,19 +6227,41 @@ fn registry_inference_ignores_unpublishable() {

p.cargo("package -Zpackage-workspace")
.masquerade_as_nightly_cargo(&["package-workspace"])
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] conflicts between `package.publish` fields in the selected packages
[PACKAGING] dep v0.1.0 ([ROOT]/foo/dep)
[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
[UPDATING] `alternative` index
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
[VERIFYING] dep v0.1.0 ([ROOT]/foo/dep)
[COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0)
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
[VERIFYING] main v0.0.1 ([ROOT]/foo/main)
[UPDATING] `alternative` index
[UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`)
[COMPILING] dep v0.1.0 (registry `alternative`)
[COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1)
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
"#]])
.run();

p.cargo("package -Zpackage-workspace --registry=alternative")
.masquerade_as_nightly_cargo(&["package-workspace"])
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] `main` cannot be packaged.
The registry `alternative` is not listed in the `package.publish` value in Cargo.toml.
[PACKAGING] dep v0.1.0 ([ROOT]/foo/dep)
[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
[UPDATING] `alternative` index
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
[VERIFYING] dep v0.1.0 ([ROOT]/foo/dep)
[COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0)
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
[VERIFYING] main v0.0.1 ([ROOT]/foo/main)
[UPDATING] `alternative` index
[COMPILING] dep v0.1.0 (registry `alternative`)
[COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1)
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
"#]])
.run();
Expand Down Expand Up @@ -6464,7 +6486,16 @@ fn unpublishable_dependency() {
.masquerade_as_nightly_cargo(&["package-workspace"])
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] conflicts between `package.publish` fields in the selected packages
[PACKAGING] dep v0.1.0 ([ROOT]/foo/dep)
[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
[UPDATING] `alternative` index
[ERROR] failed to prepare local package for uploading
Caused by:
no matching package named `dep` found
location searched: registry `alternative`
required by package `main v0.0.1 ([ROOT]/foo/main)`
"#]])
.run();
Expand Down

0 comments on commit 4ee3aef

Please sign in to comment.