Skip to content

Commit 0d08f19

Browse files
committed
Change registry inference rules when packaging multiple packages
Infer the package registry only if all packages have the same publish field. If there is confusion, bail out instead of defaulting to crates-io.
1 parent fff7dd5 commit 0d08f19

File tree

2 files changed

+98
-63
lines changed

2 files changed

+98
-63
lines changed

src/cargo/ops/cargo_package.rs

+52-20
Original file line numberDiff line numberDiff line change
@@ -185,44 +185,76 @@ fn infer_registry(
185185
pkgs: &[&Package],
186186
reg_or_index: Option<RegistryOrIndex>,
187187
) -> CargoResult<SourceId> {
188-
let publish_registry = if let Some(RegistryOrIndex::Registry(registry)) = reg_or_index.as_ref()
189-
{
190-
Some(registry.clone())
191-
} else if let Some([first_pkg_reg]) = pkgs[0].publish().as_deref() {
192-
// If no registry is specified in the command, but all of the packages
193-
// to publish have the same, unique allowed registry, push to that one.
194-
if pkgs[1..].iter().all(|p| p.publish() == pkgs[0].publish()) {
195-
Some(first_pkg_reg.clone())
196-
} else {
197-
None
188+
let reg_or_index = match reg_or_index {
189+
Some(r) => r,
190+
None => {
191+
if pkgs[1..].iter().all(|p| p.publish() == pkgs[0].publish()) {
192+
// If all packages have the same publish settings, we take that as the default.
193+
match pkgs[0].publish().as_deref() {
194+
Some([unique_pkg_reg]) => RegistryOrIndex::Registry(unique_pkg_reg.to_owned()),
195+
None | Some([]) => RegistryOrIndex::Registry(CRATES_IO_REGISTRY.to_owned()),
196+
Some([reg, ..]) if pkgs.len() == 1 => {
197+
// For backwards compatibility, avoid erroring if there's only one package.
198+
// The registry doesn't affect packaging in this case.
199+
RegistryOrIndex::Registry(reg.to_owned())
200+
}
201+
Some(regs) => {
202+
let mut regs: Vec<_> = regs.iter().map(|s| format!("\"{}\"", s)).collect();
203+
regs.sort();
204+
regs.dedup();
205+
// unwrap: the match block ensures that there's more than one reg.
206+
let (last_reg, regs) = regs.split_last().unwrap();
207+
bail!(
208+
"--registry is required to disambiguate between {} or {} registries",
209+
regs.join(", "),
210+
last_reg
211+
)
212+
}
213+
}
214+
} else {
215+
let common_regs = pkgs
216+
.iter()
217+
// `None` means "all registries", so drop them instead of including them
218+
// in the intersection.
219+
.filter_map(|p| p.publish().as_deref())
220+
.map(|p| p.iter().collect::<HashSet<_>>())
221+
.reduce(|xs, ys| xs.intersection(&ys).cloned().collect())
222+
.unwrap_or_default();
223+
if common_regs.is_empty() {
224+
bail!("conflicts between `package.publish` fields in the selected packages");
225+
} else {
226+
bail!(
227+
"--registry is required because not all `package.publish` settings agree",
228+
);
229+
}
230+
}
198231
}
199-
} else {
200-
None
201232
};
202233

203234
// Validate the registry against the packages' allow-lists. For backwards compatibility, we
204235
// skip this if only a single package is being published (because in that case the registry
205236
// doesn't affect the packaging step).
206237
if pkgs.len() > 1 {
207-
let reg_name = publish_registry.as_deref().unwrap_or(CRATES_IO_REGISTRY);
208-
for pkg in pkgs {
209-
if let Some(allowed) = pkg.publish().as_ref() {
210-
if !allowed.iter().any(|a| a == reg_name) {
211-
bail!(
238+
if let RegistryOrIndex::Registry(reg_name) = &reg_or_index {
239+
for pkg in pkgs {
240+
if let Some(allowed) = pkg.publish().as_ref() {
241+
if !allowed.iter().any(|a| a == reg_name) {
242+
bail!(
212243
"`{}` cannot be packaged.\n\
213244
The registry `{}` is not listed in the `package.publish` value in Cargo.toml.",
214245
pkg.name(),
215246
reg_name
216247
);
248+
}
217249
}
218250
}
219251
}
220252
}
221253

222254
let sid = match reg_or_index {
223-
None => SourceId::crates_io(gctx)?,
224-
Some(RegistryOrIndex::Registry(r)) => SourceId::alt_registry(gctx, &r)?,
225-
Some(RegistryOrIndex::Index(url)) => SourceId::for_registry(&url)?,
255+
RegistryOrIndex::Index(url) => SourceId::for_registry(&url)?,
256+
RegistryOrIndex::Registry(reg) if reg == CRATES_IO_REGISTRY => SourceId::crates_io(gctx)?,
257+
RegistryOrIndex::Registry(reg) => SourceId::alt_registry(gctx, &reg)?,
226258
};
227259

228260
// Load source replacements that are built-in to Cargo.

tests/testsuite/package.rs

+46-43
Original file line numberDiff line numberDiff line change
@@ -6028,25 +6028,28 @@ fn registry_inferred_from_unique_option() {
60286028

60296029
p.cargo("package -Zpackage-workspace")
60306030
.masquerade_as_nightly_cargo(&["package-workspace"])
6031-
.with_status(101)
60326031
.with_stderr_data(str![[r#"
60336032
[PACKAGING] dep v0.1.0 ([ROOT]/foo/dep)
60346033
[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
60356034
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
60366035
[UPDATING] `alternative` index
6037-
[ERROR] failed to prepare local package for uploading
6038-
6039-
Caused by:
6040-
no matching package named `dep` found
6041-
location searched: registry `alternative`
6042-
required by package `main v0.0.1 ([ROOT]/foo/main)`
6036+
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
6037+
[VERIFYING] dep v0.1.0 ([ROOT]/foo/dep)
6038+
[COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0)
6039+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
6040+
[VERIFYING] main v0.0.1 ([ROOT]/foo/main)
6041+
[UPDATING] `alternative` index
6042+
[UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`)
6043+
[COMPILING] dep v0.1.0 (registry `alternative`)
6044+
[COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1)
6045+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
60436046
60446047
"#]])
60456048
.run();
60466049
}
60476050

60486051
#[cargo_test]
6049-
fn registry_not_inferred_because_of_missing_option() {
6052+
fn registry_not_inferred_because_of_conflict() {
60506053
let alt_reg = registry::RegistryBuilder::new()
60516054
.http_api()
60526055
.http_index()
@@ -6100,8 +6103,7 @@ fn registry_not_inferred_because_of_missing_option() {
61006103
.masquerade_as_nightly_cargo(&["package-workspace"])
61016104
.with_status(101)
61026105
.with_stderr_data(str![[r#"
6103-
[ERROR] `dep` cannot be packaged.
6104-
The registry `crates-io` is not listed in the `package.publish` value in Cargo.toml.
6106+
[ERROR] conflicts between `package.publish` fields in the selected packages
61056107
61066108
"#]])
61076109
.run();
@@ -6121,10 +6123,21 @@ The registry `alternative` is not listed in the `package.publish` value in Cargo
61216123
alt_reg.index_url()
61226124
))
61236125
.masquerade_as_nightly_cargo(&["package-workspace"])
6124-
.with_status(101)
61256126
.with_stderr_data(str![[r#"
6126-
[ERROR] `dep` cannot be packaged.
6127-
The registry `crates-io` is not listed in the `package.publish` value in Cargo.toml.
6127+
[PACKAGING] dep v0.1.0 ([ROOT]/foo/dep)
6128+
[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
6129+
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
6130+
[UPDATING] `alternative` index
6131+
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
6132+
[VERIFYING] dep v0.1.0 ([ROOT]/foo/dep)
6133+
[COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0)
6134+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
6135+
[VERIFYING] main v0.0.1 ([ROOT]/foo/main)
6136+
[UPDATING] `alternative` index
6137+
[UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`)
6138+
[COMPILING] dep v0.1.0 (registry `alternative`)
6139+
[COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1)
6140+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
61286141
61296142
"#]])
61306143
.run();
@@ -6143,7 +6156,7 @@ fn registry_not_inferred_because_of_multiple_options() {
61436156
"Cargo.toml",
61446157
r#"
61456158
[workspace]
6146-
members = ["dep", "main", "other"]
6159+
members = ["dep", "main"]
61476160
"#,
61486161
)
61496162
.file(
@@ -6179,29 +6192,13 @@ fn registry_not_inferred_because_of_multiple_options() {
61796192
"#,
61806193
)
61816194
.file("dep/src/lib.rs", "")
6182-
// No publish field means "publish anywhere"
6183-
.file(
6184-
"other/Cargo.toml",
6185-
r#"
6186-
[package]
6187-
name = "other"
6188-
version = "0.1.0"
6189-
edition = "2015"
6190-
authors = []
6191-
license = "MIT"
6192-
description = "dep"
6193-
repository = "bar"
6194-
"#,
6195-
)
6196-
.file("other/src/lib.rs", "")
61976195
.build();
61986196

61996197
p.cargo("package -Zpackage-workspace")
62006198
.masquerade_as_nightly_cargo(&["package-workspace"])
62016199
.with_status(101)
62026200
.with_stderr_data(str![[r#"
6203-
[ERROR] `dep` cannot be packaged.
6204-
The registry `crates-io` is not listed in the `package.publish` value in Cargo.toml.
6201+
[ERROR] --registry is required to disambiguate between "alternative" or "alternative2" registries
62056202
62066203
"#]])
62076204
.run();
@@ -6214,8 +6211,6 @@ The registry `crates-io` is not listed in the `package.publish` value in Cargo.t
62146211
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
62156212
[UPDATING] `alternative` index
62166213
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
6217-
[PACKAGING] other v0.1.0 ([ROOT]/foo/other)
6218-
[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
62196214
[VERIFYING] dep v0.1.0 ([ROOT]/foo/dep)
62206215
[COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0)
62216216
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
@@ -6225,16 +6220,13 @@ The registry `crates-io` is not listed in the `package.publish` value in Cargo.t
62256220
[COMPILING] dep v0.1.0 (registry `alternative`)
62266221
[COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1)
62276222
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
6228-
[VERIFYING] other v0.1.0 ([ROOT]/foo/other)
6229-
[COMPILING] other v0.1.0 ([ROOT]/foo/target/package/other-0.1.0)
6230-
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
62316223
62326224
"#]])
62336225
.run();
62346226
}
62356227

62366228
#[cargo_test]
6237-
fn registry_not_inferred_because_of_conflict() {
6229+
fn registry_not_inferred_because_of_mismatch() {
62386230
let _alt_reg = registry::RegistryBuilder::new()
62396231
.http_api()
62406232
.http_index()
@@ -6267,6 +6259,8 @@ fn registry_not_inferred_because_of_conflict() {
62676259
"#,
62686260
)
62696261
.file("main/src/main.rs", "fn main() {}")
6262+
// No `publish` field means "any registry", but the presence of this package
6263+
// will stop us from inferring a registry.
62706264
.file(
62716265
"dep/Cargo.toml",
62726266
r#"
@@ -6278,7 +6272,6 @@ fn registry_not_inferred_because_of_conflict() {
62786272
license = "MIT"
62796273
description = "dep"
62806274
repository = "bar"
6281-
publish = ["alternative2"]
62826275
"#,
62836276
)
62846277
.file("dep/src/lib.rs", "")
@@ -6288,18 +6281,28 @@ fn registry_not_inferred_because_of_conflict() {
62886281
.masquerade_as_nightly_cargo(&["package-workspace"])
62896282
.with_status(101)
62906283
.with_stderr_data(str![[r#"
6291-
[ERROR] `dep` cannot be packaged.
6292-
The registry `crates-io` is not listed in the `package.publish` value in Cargo.toml.
6284+
[ERROR] --registry is required because not all `package.publish` settings agree
62936285
62946286
"#]])
62956287
.run();
62966288

62976289
p.cargo("package -Zpackage-workspace --registry=alternative")
62986290
.masquerade_as_nightly_cargo(&["package-workspace"])
6299-
.with_status(101)
63006291
.with_stderr_data(str![[r#"
6301-
[ERROR] `dep` cannot be packaged.
6302-
The registry `alternative` is not listed in the `package.publish` value in Cargo.toml.
6292+
[PACKAGING] dep v0.1.0 ([ROOT]/foo/dep)
6293+
[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
6294+
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
6295+
[UPDATING] `alternative` index
6296+
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
6297+
[VERIFYING] dep v0.1.0 ([ROOT]/foo/dep)
6298+
[COMPILING] dep v0.1.0 ([ROOT]/foo/target/package/dep-0.1.0)
6299+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
6300+
[VERIFYING] main v0.0.1 ([ROOT]/foo/main)
6301+
[UPDATING] `alternative` index
6302+
[UNPACKING] dep v0.1.0 (registry `[ROOT]/foo/target/package/tmp-registry`)
6303+
[COMPILING] dep v0.1.0 (registry `alternative`)
6304+
[COMPILING] main v0.0.1 ([ROOT]/foo/target/package/main-0.0.1)
6305+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
63036306
63046307
"#]])
63056308
.run();

0 commit comments

Comments
 (0)