Skip to content

Commit dd5134c

Browse files
committed
Implement RFC 3289: source replacement ambiguity
1 parent 8743325 commit dd5134c

32 files changed

+661
-330
lines changed

crates/cargo-test-support/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,17 @@ impl Execs {
818818
self
819819
}
820820

821+
/// Overrides the crates.io URL for testing.
822+
///
823+
/// Can be used for testing crates-io functionality where alt registries
824+
/// cannot be used.
825+
pub fn replace_crates_io(&mut self, url: &Url) -> &mut Self {
826+
if let Some(ref mut p) = self.process_builder {
827+
p.env("__CARGO_TEST_CRATES_IO_URL_DO_NOT_USE_THIS", url.as_str());
828+
}
829+
self
830+
}
831+
821832
pub fn enable_mac_dsym(&mut self) -> &mut Self {
822833
if cfg!(target_os = "macos") {
823834
self.env("CARGO_PROFILE_DEV_SPLIT_DEBUGINFO", "packed")

crates/cargo-test-support/src/registry.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ impl RegistryBuilder {
105105
pub fn new() -> RegistryBuilder {
106106
RegistryBuilder {
107107
alternative: None,
108-
token: Some("api-token".to_string()),
108+
token: None,
109109
http_api: false,
110110
http_index: false,
111111
api: true,
@@ -197,6 +197,7 @@ impl RegistryBuilder {
197197
let dl_url = generate_url(&format!("{prefix}dl"));
198198
let dl_path = generate_path(&format!("{prefix}dl"));
199199
let api_path = generate_path(&format!("{prefix}api"));
200+
let token = Some(self.token.unwrap_or_else(|| format!("{prefix}sekrit")));
200201

201202
let (server, index_url, api_url, dl_url) = if !self.http_index && !self.http_api {
202203
// No need to start the HTTP server.
@@ -205,7 +206,7 @@ impl RegistryBuilder {
205206
let server = HttpServer::new(
206207
registry_path.clone(),
207208
dl_path,
208-
self.token.clone(),
209+
token.clone(),
209210
self.custom_responders,
210211
);
211212
let index_url = if self.http_index {
@@ -228,7 +229,7 @@ impl RegistryBuilder {
228229
_server: server,
229230
dl_url,
230231
path: registry_path,
231-
token: self.token,
232+
token,
232233
};
233234

234235
if self.configure_registry {
@@ -252,8 +253,8 @@ impl RegistryBuilder {
252253
[source.crates-io]
253254
replace-with = 'dummy-registry'
254255
255-
[source.dummy-registry]
256-
registry = '{}'",
256+
[registries.dummy-registry]
257+
index = '{}'",
257258
registry.index_url
258259
)
259260
.as_bytes(),

src/bin/cargo/commands/add.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use cargo::sources::CRATES_IO_REGISTRY;
12
use indexmap::IndexMap;
23
use indexmap::IndexSet;
34

@@ -207,7 +208,10 @@ fn parse_dependencies(config: &Config, matches: &ArgMatches) -> CargoResult<Vec<
207208
let rev = matches.get_one::<String>("rev");
208209
let tag = matches.get_one::<String>("tag");
209210
let rename = matches.get_one::<String>("rename");
210-
let registry = matches.registry(config)?;
211+
let registry = match matches.registry(config)? {
212+
Some(reg) if reg == CRATES_IO_REGISTRY => None,
213+
reg => reg,
214+
};
211215
let default_features = default_features(matches);
212216
let optional = optional(matches);
213217

src/cargo/core/compiler/rustdoc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ pub fn add_root_urls(
128128
if !sid.is_registry() {
129129
return false;
130130
}
131-
if sid.is_default_registry() {
131+
if sid.is_crates_io() {
132132
return registry == CRATES_IO_REGISTRY;
133133
}
134134
if let Some(index_url) = name2url.get(registry) {

src/cargo/core/package_id.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ impl fmt::Display for PackageId {
211211
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
212212
write!(f, "{} v{}", self.inner.name, self.inner.version)?;
213213

214-
if !self.inner.source_id.is_default_registry() {
214+
if !self.inner.source_id.is_crates_io() {
215215
write!(f, " ({})", self.inner.source_id)?;
216216
}
217217

src/cargo/core/source/source_id.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ struct SourceIdInner {
3939
/// WARNING: this is not always set for alt-registries when the name is
4040
/// not known.
4141
name: Option<String>,
42+
/// Name of the alt registry in the `[registries]` table.
43+
/// WARNING: this is not always set for alt-registries when the name is
44+
/// not known.
45+
alt_registry_key: Option<String>,
4246
}
4347

4448
/// The possible kinds of code source. Along with `SourceIdInner`, this fully defines the
@@ -81,6 +85,7 @@ impl SourceId {
8185
url,
8286
precise: None,
8387
name: name.map(|n| n.into()),
88+
alt_registry_key: None,
8489
});
8590
Ok(source_id)
8691
}
@@ -221,13 +226,17 @@ impl SourceId {
221226

222227
/// Gets the `SourceId` associated with given name of the remote registry.
223228
pub fn alt_registry(config: &Config, key: &str) -> CargoResult<SourceId> {
229+
if key == CRATES_IO_REGISTRY {
230+
return Self::crates_io(config);
231+
}
224232
let url = config.get_registry_index(key)?;
225233
Ok(SourceId::wrap(SourceIdInner {
226234
kind: SourceKind::Registry,
227235
canonical_url: CanonicalUrl::new(&url)?,
228236
url,
229237
precise: None,
230238
name: Some(key.to_string()),
239+
alt_registry_key: Some(key.to_string()),
231240
}))
232241
}
233242

@@ -243,15 +252,15 @@ impl SourceId {
243252
}
244253

245254
pub fn display_index(self) -> String {
246-
if self.is_default_registry() {
255+
if self.is_crates_io() {
247256
format!("{} index", CRATES_IO_DOMAIN)
248257
} else {
249258
format!("`{}` index", self.display_registry_name())
250259
}
251260
}
252261

253262
pub fn display_registry_name(self) -> String {
254-
if self.is_default_registry() {
263+
if self.is_crates_io() {
255264
CRATES_IO_REGISTRY.to_string()
256265
} else if let Some(name) = &self.inner.name {
257266
name.clone()
@@ -264,6 +273,13 @@ impl SourceId {
264273
}
265274
}
266275

276+
/// Gets the name of the remote registry as defined in the `[registries]` table.
277+
/// WARNING: alt registries that come from Cargo.lock, or --index will
278+
/// not have a name.
279+
pub fn alt_registry_key(&self) -> Option<&str> {
280+
self.inner.alt_registry_key.as_deref()
281+
}
282+
267283
/// Returns `true` if this source is from a filesystem path.
268284
pub fn is_path(self) -> bool {
269285
self.inner.kind == SourceKind::Path
@@ -364,13 +380,15 @@ impl SourceId {
364380
}
365381

366382
/// Returns `true` if the remote registry is the standard <https://crates.io>.
367-
pub fn is_default_registry(self) -> bool {
383+
pub fn is_crates_io(self) -> bool {
368384
match self.inner.kind {
369385
SourceKind::Registry => {}
370386
_ => return false,
371387
}
372388
let url = self.inner.url.as_str();
373-
url == CRATES_IO_INDEX || url == CRATES_IO_HTTP_INDEX
389+
url == CRATES_IO_INDEX
390+
|| url == CRATES_IO_HTTP_INDEX
391+
|| std::env::var("__CARGO_TEST_CRATES_IO_URL_DO_NOT_USE_THIS").as_deref() == Ok(url)
374392
}
375393

376394
/// Hashes `self`.

src/cargo/ops/registry.rs

Lines changed: 66 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ fn verify_dependencies(
209209
// This extra hostname check is mostly to assist with testing,
210210
// but also prevents someone using `--index` to specify
211211
// something that points to crates.io.
212-
if registry_src.is_default_registry() || registry.host_is_crates_io() {
212+
if registry_src.is_crates_io() || registry.host_is_crates_io() {
213213
bail!("crates cannot be published to crates.io with dependencies sourced from other\n\
214214
registries. `{}` needs to be published to crates.io before publishing this crate.\n\
215215
(crate `{}` is pulled from {})",
@@ -391,6 +391,22 @@ pub fn registry_configuration(
391391
};
392392
// `registry.default` is handled in command-line parsing.
393393
let (token, process) = match registry {
394+
Some("crates-io") | None => {
395+
// Use crates.io default.
396+
config.check_registry_index_not_set()?;
397+
let token = config.get_string("registry.token")?.map(|p| p.val);
398+
let process = if config.cli_unstable().credential_process {
399+
let process =
400+
config.get::<Option<config::PathAndArgs>>("registry.credential-process")?;
401+
if token.is_some() && process.is_some() {
402+
return err_both("registry.token", "registry.credential-process");
403+
}
404+
process
405+
} else {
406+
None
407+
};
408+
(token, process)
409+
}
394410
Some(registry) => {
395411
let token_key = format!("registries.{registry}.token");
396412
let token = config.get_string(&token_key)?.map(|p| p.val);
@@ -411,22 +427,6 @@ pub fn registry_configuration(
411427
};
412428
(token, process)
413429
}
414-
None => {
415-
// Use crates.io default.
416-
config.check_registry_index_not_set()?;
417-
let token = config.get_string("registry.token")?.map(|p| p.val);
418-
let process = if config.cli_unstable().credential_process {
419-
let process =
420-
config.get::<Option<config::PathAndArgs>>("registry.credential-process")?;
421-
if token.is_some() && process.is_some() {
422-
return err_both("registry.token", "registry.credential-process");
423-
}
424-
process
425-
} else {
426-
None
427-
};
428-
(token, process)
429-
}
430430
};
431431

432432
let credential_process =
@@ -444,11 +444,9 @@ pub fn registry_configuration(
444444
///
445445
/// * `token`: The token from the command-line. If not set, uses the token
446446
/// from the config.
447-
/// * `index`: The index URL from the command-line. This is ignored if
448-
/// `registry` is set.
447+
/// * `index`: The index URL from the command-line.
449448
/// * `registry`: The registry name from the command-line. If neither
450-
/// `registry`, or `index` are set, then uses `crates-io`, honoring
451-
/// `[source]` replacement if defined.
449+
/// `registry`, or `index` are set, then uses `crates-io`.
452450
/// * `force_update`: If `true`, forces the index to be updated.
453451
/// * `validate_token`: If `true`, the token must be set.
454452
fn registry(
@@ -459,24 +457,8 @@ fn registry(
459457
force_update: bool,
460458
validate_token: bool,
461459
) -> CargoResult<(Registry, RegistryConfig, SourceId)> {
462-
if index.is_some() && registry.is_some() {
463-
// Otherwise we would silently ignore one or the other.
464-
bail!("both `--index` and `--registry` should not be set at the same time");
465-
}
466-
// Parse all configuration options
460+
let (sid, sid_no_replacement) = get_source_id(config, index, registry)?;
467461
let reg_cfg = registry_configuration(config, registry)?;
468-
let opt_index = registry
469-
.map(|r| config.get_registry_index(r))
470-
.transpose()?
471-
.map(|u| u.to_string());
472-
let sid = get_source_id(config, opt_index.as_deref().or(index), registry)?;
473-
if !sid.is_remote_registry() {
474-
bail!(
475-
"{} does not support API commands.\n\
476-
Check for a source-replacement in .cargo/config.",
477-
sid
478-
);
479-
}
480462
let api_host = {
481463
let _lock = config.acquire_package_cache_lock()?;
482464
let mut src = RegistrySource::remote(sid, &HashSet::new(), config)?;
@@ -503,42 +485,18 @@ fn registry(
503485
}
504486
token
505487
} else {
506-
// Check `is_default_registry` so that the crates.io index can
507-
// change config.json's "api" value, and this won't affect most
508-
// people. It will affect those using source replacement, but
509-
// hopefully that's a relatively small set of users.
510-
if token.is_none()
511-
&& reg_cfg.is_token()
512-
&& registry.is_none()
513-
&& !sid.is_default_registry()
514-
&& !crates_io::is_url_crates_io(&api_host)
515-
{
516-
config.shell().warn(
517-
"using `registry.token` config value with source \
518-
replacement is deprecated\n\
519-
This may become a hard error in the future; \
520-
see <https://github.com/rust-lang/cargo/issues/xxx>.\n\
521-
Use the --token command-line flag to remove this warning.",
522-
)?;
523-
reg_cfg.as_token().map(|t| t.to_owned())
524-
} else {
525-
let token =
526-
auth::auth_token(config, token.as_deref(), &reg_cfg, registry, &api_host)?;
527-
Some(token)
528-
}
488+
let token = auth::auth_token(config, token.as_deref(), &reg_cfg, registry, &api_host)?;
489+
Some(token)
529490
}
530491
} else {
531492
None
532493
};
533494
let handle = http_handle(config)?;
534-
// Workaround for the sparse+https://index.crates.io replacement index. Use the non-replaced
535-
// source_id so that the original (github) url is used when publishing a crate.
536-
let sid = if sid.is_default_registry() {
537-
SourceId::crates_io(config)?
538-
} else {
539-
sid
540-
};
541-
Ok((Registry::new_handle(api_host, token, handle), reg_cfg, sid))
495+
Ok((
496+
Registry::new_handle(api_host, token, handle),
497+
reg_cfg,
498+
sid_no_replacement,
499+
))
542500
}
543501

544502
/// Creates a new HTTP handle with appropriate global configuration for cargo.
@@ -947,16 +905,45 @@ pub fn yank(
947905
/// Gets the SourceId for an index or registry setting.
948906
///
949907
/// The `index` and `reg` values are from the command-line or config settings.
950-
/// If both are None, returns the source for crates.io.
951-
fn get_source_id(config: &Config, index: Option<&str>, reg: Option<&str>) -> CargoResult<SourceId> {
952-
match (reg, index) {
953-
(Some(r), _) => SourceId::alt_registry(config, r),
954-
(_, Some(i)) => SourceId::for_registry(&i.into_url()?),
955-
_ => {
956-
let map = SourceConfigMap::new(config)?;
957-
let src = map.load(SourceId::crates_io(config)?, &HashSet::new())?;
958-
Ok(src.replaced_source_id())
908+
/// If both are None, and no source-replacement is configured, returns the source for crates.io.
909+
/// If both are None, and source replacement is configured, returns an error.
910+
///
911+
/// The source for crates.io may be GitHub, index.crates.io, or a test-only registry depending
912+
/// on configuration.
913+
///
914+
/// If `reg` is set, source replacement is not followed.
915+
///
916+
/// The return value is a pair of `SourceId`s: The first may be a built-in replacement of
917+
/// crates.io (such as index.crates.io), while the second is always the original source.
918+
fn get_source_id(
919+
config: &Config,
920+
index: Option<&str>,
921+
reg: Option<&str>,
922+
) -> CargoResult<(SourceId, SourceId)> {
923+
let sid = match (reg, index) {
924+
(None, None) => SourceId::crates_io(config)?,
925+
(Some(r), None) => SourceId::alt_registry(config, r)?,
926+
(None, Some(i)) => SourceId::for_registry(&i.into_url()?)?,
927+
(Some(_), Some(_)) => {
928+
bail!("both `--index` and `--registry` should not be set at the same time")
929+
}
930+
};
931+
// Load source replacements that are built-in to Cargo.
932+
let builtin_replacement_sid = SourceConfigMap::empty(config)?
933+
.load(sid, &HashSet::new())?
934+
.replaced_source_id();
935+
let replacement_sid = SourceConfigMap::new(config)?
936+
.load(sid, &HashSet::new())?
937+
.replaced_source_id();
938+
if reg.is_none() && index.is_none() && replacement_sid != builtin_replacement_sid {
939+
// Neither --registry nor --index was passed and the user has configured source-replacement.
940+
if let Some(replacement_name) = replacement_sid.alt_registry_key() {
941+
bail!("crates-io is replaced with remote registry {replacement_name};\ninclude `--registry {replacement_name}` or `--registry crates-io`");
942+
} else {
943+
bail!("crates-io is replaced with non-remote-registry source {replacement_sid};\ninclude `--registry crates-io` to use crates.io");
959944
}
945+
} else {
946+
Ok((builtin_replacement_sid, sid))
960947
}
961948
}
962949

@@ -1024,7 +1011,7 @@ pub fn search(
10241011
&ColorSpec::new(),
10251012
);
10261013
} else if total_crates > limit && limit >= search_max_limit {
1027-
let extra = if source_id.is_default_registry() {
1014+
let extra = if source_id.is_crates_io() {
10281015
format!(
10291016
" (go to https://crates.io/search?q={} to see more)",
10301017
percent_encode(query.as_bytes(), NON_ALPHANUMERIC)

0 commit comments

Comments
 (0)