Skip to content

Commit c3d6601

Browse files
borsweihanglo
authored andcommitted
Auto merge of rust-lang#11756 - arlosi:sparse-uninstall, r=ehuss
Fix Cargo removing the sparse+ prefix from sparse URLs in .crates.toml The URL associated with a `SourceId` for a sparse registry includes the `sparse+` prefix in the URL to differentiate from git (non-sparse) registries. `SourceId::from_url` was not including the `sparse+` prefix of sparse registry URLs on construction, which caused roundtrips of `as_url` and `from_url` to fail by losing the prefix. Fixes rust-lang#11751 by: * Including the prefix in the URL * Adding a test that verifies round-trip behavior for sparse `SourceId`s * Modifying `CanonicalUrl` so it no longer considers `sparse+` and non-`sparse+` URLs to be equivalent
1 parent 8d521c6 commit c3d6601

File tree

3 files changed

+86
-14
lines changed

3 files changed

+86
-14
lines changed

src/cargo/core/source/source_id.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ impl SourceId {
8181
///
8282
/// The canonical url will be calculated, but the precise field will not
8383
fn new(kind: SourceKind, url: Url, name: Option<&str>) -> CargoResult<SourceId> {
84+
if kind == SourceKind::SparseRegistry {
85+
// Sparse URLs are different because they store the kind prefix (sparse+)
86+
// in the URL. This is because the prefix is necessary to differentiate
87+
// from regular registries (git-based). The sparse+ prefix is included
88+
// everywhere, including user-facing locations such as the `config.toml`
89+
// file that defines the registry, or whenever Cargo displays it to the user.
90+
assert!(url.as_str().starts_with("sparse+"));
91+
}
8492
let source_id = SourceId::wrap(SourceIdInner {
8593
kind,
8694
canonical_url: CanonicalUrl::new(&url)?,
@@ -152,7 +160,7 @@ impl SourceId {
152160
.with_precise(Some("locked".to_string())))
153161
}
154162
"sparse" => {
155-
let url = url.into_url()?;
163+
let url = string.into_url()?;
156164
Ok(SourceId::new(SourceKind::SparseRegistry, url, None)?
157165
.with_precise(Some("locked".to_string())))
158166
}
@@ -721,6 +729,7 @@ impl<'a> fmt::Display for SourceIdAsUrl<'a> {
721729
ref url,
722730
..
723731
} => {
732+
// Sparse registry URL already includes the `sparse+` prefix
724733
write!(f, "{url}")
725734
}
726735
SourceIdInner {
@@ -864,4 +873,14 @@ mod tests {
864873
assert_eq!(gen_hash(source_id), 17459999773908528552);
865874
assert_eq!(crate::util::hex::short_hash(&source_id), "6568fe2c2fab5bfe");
866875
}
876+
877+
#[test]
878+
fn serde_roundtrip() {
879+
let url = "sparse+https://my-crates.io/".into_url().unwrap();
880+
let source_id = SourceId::for_registry(&url).unwrap();
881+
let formatted = format!("{}", source_id.as_url());
882+
let deserialized = SourceId::from_url(&formatted).unwrap();
883+
assert_eq!(formatted, "sparse+https://my-crates.io/");
884+
assert_eq!(source_id, deserialized);
885+
}
867886
}

src/cargo/util/canonical_url.rs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::util::{errors::CargoResult, IntoUrl};
1+
use crate::util::errors::CargoResult;
22
use std::hash::{self, Hash};
33
use url::Url;
44

@@ -56,17 +56,6 @@ impl CanonicalUrl {
5656
url.path_segments_mut().unwrap().pop().push(&last);
5757
}
5858

59-
// Ignore the protocol specifier (if any).
60-
if url.scheme().starts_with("sparse+") {
61-
// NOTE: it is illegal to use set_scheme to change sparse+http(s) to http(s).
62-
url = url
63-
.to_string()
64-
.strip_prefix("sparse+")
65-
.expect("we just found that prefix")
66-
.into_url()
67-
.expect("a valid url without a protocol specifier should still be valid");
68-
}
69-
7059
Ok(CanonicalUrl(url))
7160
}
7261

tests/testsuite/install.rs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::fs::{self, OpenOptions};
44
use std::io::prelude::*;
55

6-
use cargo_test_support::cross_compile;
6+
use cargo_test_support::{cross_compile, compare};
77
use cargo_test_support::git;
88
use cargo_test_support::registry::{self, registry_path, Package};
99
use cargo_test_support::{
@@ -2105,3 +2105,67 @@ fn no_auto_fix_note() {
21052105
.run();
21062106
assert_has_not_installed_exe(cargo_home(), "auto_fix");
21072107
}
2108+
2109+
#[cargo_test]
2110+
fn sparse_install() {
2111+
// Checks for an issue where uninstalling something corrupted
2112+
// the SourceIds of sparse registries.
2113+
// See https://github.com/rust-lang/cargo/issues/11751
2114+
let _registry = registry::RegistryBuilder::new().http_index().build();
2115+
2116+
pkg("foo", "0.0.1");
2117+
pkg("bar", "0.0.1");
2118+
2119+
cargo_process("install foo --registry dummy-registry")
2120+
.with_stderr(
2121+
"\
2122+
[UPDATING] `dummy-registry` index
2123+
[DOWNLOADING] crates ...
2124+
[DOWNLOADED] foo v0.0.1 (registry `dummy-registry`)
2125+
[INSTALLING] foo v0.0.1 (registry `dummy-registry`)
2126+
[UPDATING] `dummy-registry` index
2127+
[COMPILING] foo v0.0.1 (registry `dummy-registry`)
2128+
[FINISHED] release [optimized] target(s) in [..]
2129+
[INSTALLING] [ROOT]/home/.cargo/bin/foo[EXE]
2130+
[INSTALLED] package `foo v0.0.1 (registry `dummy-registry`)` (executable `foo[EXE]`)
2131+
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
2132+
",
2133+
)
2134+
.run();
2135+
assert_has_installed_exe(cargo_home(), "foo");
2136+
let assert_v1 = |expected| {
2137+
let v1 = fs::read_to_string(paths::home().join(".cargo/.crates.toml")).unwrap();
2138+
compare::assert_match_exact(expected, &v1);
2139+
};
2140+
assert_v1(
2141+
r#"[v1]
2142+
"foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"]
2143+
"#,
2144+
);
2145+
cargo_process("install bar").run();
2146+
assert_has_installed_exe(cargo_home(), "bar");
2147+
assert_v1(
2148+
r#"[v1]
2149+
"bar 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = ["bar[EXE]"]
2150+
"foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"]
2151+
"#,
2152+
);
2153+
2154+
cargo_process("uninstall bar")
2155+
.with_stderr("[REMOVING] [CWD]/home/.cargo/bin/bar[EXE]")
2156+
.run();
2157+
assert_has_not_installed_exe(cargo_home(), "bar");
2158+
assert_v1(
2159+
r#"[v1]
2160+
"foo 0.0.1 (sparse+http://127.0.0.1:[..]/index/)" = ["foo[EXE]"]
2161+
"#,
2162+
);
2163+
cargo_process("uninstall foo")
2164+
.with_stderr("[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]")
2165+
.run();
2166+
assert_has_not_installed_exe(cargo_home(), "foo");
2167+
assert_v1(
2168+
r#"[v1]
2169+
"#,
2170+
);
2171+
}

0 commit comments

Comments
 (0)