Skip to content

Commit 6a1aee0

Browse files
committed
Auto merge of #4568 - Metaswitch:alt-registry-publish, r=withoutboats
Add support for publish to optionally take the index that can be used This form part of alternative-registries RFC-2141, it allows crates to optionally specify which registries the crate can be be published to. @carols10cents, one thing that I am unsure about is if there is a plan for publish to still provide index, or for registry to be provided instead. I thought that your general view was that we should move away from the index file. If we do need to map allowed registries to the index then there will be a small amount of extra work required once #4506 is merged. @withoutboats, happy for this to be merged into your branch if you want, the main reason I did not base it on your branch was due to tests not working on there yet.
2 parents abd137a + cb37d33 commit 6a1aee0

File tree

11 files changed

+372
-33
lines changed

11 files changed

+372
-33
lines changed

src/bin/package.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub fn execute(options: Options, config: &mut Config) -> CliResult {
6161
allow_dirty: options.flag_allow_dirty,
6262
target: options.flag_target.as_ref().map(|t| &t[..]),
6363
jobs: options.flag_jobs,
64+
registry: None,
6465
})?;
6566
Ok(())
6667
}

src/cargo/core/manifest.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ pub struct Manifest {
2929
include: Vec<String>,
3030
metadata: ManifestMetadata,
3131
profiles: Profiles,
32-
publish: bool,
32+
publish: Option<Vec<String>>,
3333
replace: Vec<(PackageIdSpec, Dependency)>,
3434
patch: HashMap<Url, Vec<Dependency>>,
3535
workspace: WorkspaceConfig,
@@ -240,7 +240,7 @@ impl Manifest {
240240
links: Option<String>,
241241
metadata: ManifestMetadata,
242242
profiles: Profiles,
243-
publish: bool,
243+
publish: Option<Vec<String>>,
244244
replace: Vec<(PackageIdSpec, Dependency)>,
245245
patch: HashMap<Url, Vec<Dependency>>,
246246
workspace: WorkspaceConfig,
@@ -277,7 +277,7 @@ impl Manifest {
277277
pub fn version(&self) -> &Version { self.package_id().version() }
278278
pub fn warnings(&self) -> &[DelayedWarning] { &self.warnings }
279279
pub fn profiles(&self) -> &Profiles { &self.profiles }
280-
pub fn publish(&self) -> bool { self.publish }
280+
pub fn publish(&self) -> &Option<Vec<String>> { &self.publish }
281281
pub fn replace(&self) -> &[(PackageIdSpec, Dependency)] { &self.replace }
282282
pub fn original(&self) -> &TomlManifest { &self.original }
283283
pub fn patch(&self) -> &HashMap<Url, Vec<Dependency>> { &self.patch }

src/cargo/core/package.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl Package {
108108
/// Get the package authors
109109
pub fn authors(&self) -> &Vec<String> { &self.manifest.metadata().authors }
110110
/// Whether the package is set to publish
111-
pub fn publish(&self) -> bool { self.manifest.publish() }
111+
pub fn publish(&self) -> &Option<Vec<String>> { self.manifest.publish() }
112112

113113
/// Whether the package uses a custom build script for any target
114114
pub fn has_custom_build(&self) -> bool {
@@ -134,10 +134,10 @@ impl Package {
134134
}
135135
}
136136

137-
pub fn to_registry_toml(&self) -> String {
137+
pub fn to_registry_toml(&self) -> CargoResult<String> {
138138
let manifest = self.manifest().original().prepare_for_publish();
139-
let toml = toml::to_string(&manifest).unwrap();
140-
format!("\
139+
let toml = toml::to_string(&manifest)?;
140+
Ok(format!("\
141141
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO\n\
142142
#\n\
143143
# When uploading crates to the registry Cargo will automatically\n\
@@ -151,7 +151,7 @@ impl Package {
151151
# will likely look very different (and much more reasonable)\n\
152152
\n\
153153
{}\
154-
", toml)
154+
", toml))
155155
}
156156
}
157157

src/cargo/core/source/source_id.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ struct SourceIdInner {
3232
kind: Kind,
3333
// e.g. the exact git revision of the specified branch for a Git Source
3434
precise: Option<String>,
35+
/// Name of the registry source for alternative registries
36+
name: Option<String>,
3537
}
3638

3739
/// The possible kinds of code source. Along with a URL, this fully defines the
@@ -72,6 +74,7 @@ impl SourceId {
7274
canonical_url: git::canonicalize_url(&url)?,
7375
url: url,
7476
precise: None,
77+
name: None,
7578
}),
7679
};
7780
Ok(source_id)
@@ -190,6 +193,7 @@ impl SourceId {
190193
canonical_url: git::canonicalize_url(&url)?,
191194
url: url,
192195
precise: None,
196+
name: Some(key.to_string()),
193197
}),
194198
})
195199
}
@@ -211,11 +215,16 @@ impl SourceId {
211215
/// Is this source from a registry (either local or not)
212216
pub fn is_registry(&self) -> bool {
213217
match self.inner.kind {
214-
Kind::Registry | Kind::LocalRegistry => true,
215-
_ => false,
218+
Kind::Registry | Kind::LocalRegistry => true,
219+
_ => false,
216220
}
217221
}
218222

223+
/// Is this source from an alternative registry
224+
pub fn is_alt_registry(&self) -> bool {
225+
self.is_registry() && self.inner.name.is_some()
226+
}
227+
219228
/// Is this source from a git repository
220229
pub fn is_git(&self) -> bool {
221230
match self.inner.kind {

src/cargo/ops/cargo_package.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@ pub struct PackageOpts<'cfg> {
2323
pub verify: bool,
2424
pub jobs: Option<u32>,
2525
pub target: Option<&'cfg str>,
26+
pub registry: Option<String>,
2627
}
2728

2829
pub fn package(ws: &Workspace,
2930
opts: &PackageOpts) -> CargoResult<Option<FileLock>> {
3031
let pkg = ws.current()?;
3132
let config = ws.config();
32-
if !pkg.manifest().features().activated().is_empty() {
33+
34+
// Allow packaging if a registry has been provided, or if there are no nightly
35+
// features enabled.
36+
if opts.registry.is_none() && !pkg.manifest().features().activated().is_empty() {
3337
bail!("cannot package or publish crates which activate nightly-only \
3438
cargo features")
3539
}
@@ -251,7 +255,7 @@ fn tar(ws: &Workspace,
251255
})?;
252256

253257
let mut header = Header::new_ustar();
254-
let toml = pkg.to_registry_toml();
258+
let toml = pkg.to_registry_toml()?;
255259
header.set_path(&path)?;
256260
header.set_entry_type(EntryType::file());
257261
header.set_mode(0o644);

src/cargo/ops/registry.rs

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,16 @@ pub struct PublishOpts<'cfg> {
4242
pub fn publish(ws: &Workspace, opts: &PublishOpts) -> CargoResult<()> {
4343
let pkg = ws.current()?;
4444

45-
if !pkg.publish() {
46-
bail!("some crates cannot be published.\n\
47-
`{}` is marked as unpublishable", pkg.name());
45+
if let &Some(ref allowed_registries) = pkg.publish() {
46+
if !match opts.registry {
47+
Some(ref registry) => allowed_registries.contains(registry),
48+
None => false,
49+
} {
50+
bail!("some crates cannot be published.\n\
51+
`{}` is marked as unpublishable", pkg.name());
52+
}
4853
}
54+
4955
if !pkg.manifest().patch().is_empty() {
5056
bail!("published crates cannot contain [patch] sections");
5157
}
@@ -66,11 +72,12 @@ pub fn publish(ws: &Workspace, opts: &PublishOpts) -> CargoResult<()> {
6672
allow_dirty: opts.allow_dirty,
6773
target: opts.target,
6874
jobs: opts.jobs,
75+
registry: opts.registry.clone(),
6976
})?.unwrap();
7077

7178
// Upload said tarball to the specified destination
7279
opts.config.shell().status("Uploading", pkg.package_id().to_string())?;
73-
transmit(opts.config, pkg, tarball.file(), &mut registry, opts.dry_run)?;
80+
transmit(opts.config, pkg, tarball.file(), &mut registry, &reg_id, opts.dry_run)?;
7481

7582
Ok(())
7683
}
@@ -86,10 +93,14 @@ fn verify_dependencies(pkg: &Package, registry_src: &SourceId)
8693
}
8794
} else if dep.source_id() != registry_src {
8895
if dep.source_id().is_registry() {
89-
bail!("crates cannot be published to crates.io with dependencies sourced from other\n\
90-
registries either publish `{}` on crates.io or pull it into this repository\n\
91-
and specify it with a path and version\n\
92-
(crate `{}` is pulled from {}", dep.name(), dep.name(), dep.source_id());
96+
// Block requests to send to a registry if it is not an alternative
97+
// registry
98+
if !registry_src.is_alt_registry() {
99+
bail!("crates cannot be published to crates.io with dependencies sourced from other\n\
100+
registries either publish `{}` on crates.io or pull it into this repository\n\
101+
and specify it with a path and version\n\
102+
(crate `{}` is pulled from {})", dep.name(), dep.name(), dep.source_id());
103+
}
93104
} else {
94105
bail!("crates cannot be published to crates.io with dependencies sourced from \
95106
a repository\neither publish `{}` as its own crate on crates.io and \
@@ -106,8 +117,19 @@ fn transmit(config: &Config,
106117
pkg: &Package,
107118
tarball: &File,
108119
registry: &mut Registry,
120+
registry_id: &SourceId,
109121
dry_run: bool) -> CargoResult<()> {
122+
110123
let deps = pkg.dependencies().iter().map(|dep| {
124+
125+
// If the dependency is from a different registry, then include the
126+
// registry in the dependency.
127+
let dep_registry = if dep.source_id() != registry_id {
128+
Some(dep.source_id().url().to_string())
129+
} else {
130+
None
131+
};
132+
111133
NewCrateDependency {
112134
optional: dep.is_optional(),
113135
default_features: dep.uses_default_features(),
@@ -120,6 +142,7 @@ fn transmit(config: &Config,
120142
Kind::Build => "build",
121143
Kind::Development => "dev",
122144
}.to_string(),
145+
registry: dep_registry,
123146
}
124147
}).collect::<Vec<NewCrateDependency>>();
125148
let manifest = pkg.manifest();
@@ -222,9 +245,10 @@ pub fn registry(config: &Config,
222245
index: index_config,
223246
} = registry_configuration(config, registry.clone())?;
224247
let token = token.or(token_config);
225-
let sid = match (index_config, index) {
226-
(Some(index), _) | (None, Some(index)) => SourceId::for_registry(&index.to_url()?)?,
227-
(None, None) => SourceId::crates_io(config)?,
248+
let sid = match (index_config, index, registry) {
249+
(_, _, Some(registry)) => SourceId::alt_registry(config, &registry)?,
250+
(Some(index), _, _) | (None, Some(index), _) => SourceId::for_registry(&index.to_url()?)?,
251+
(None, None, _) => SourceId::crates_io(config)?,
228252
};
229253
let api_host = {
230254
let mut src = RegistrySource::remote(&sid, config);

src/cargo/util/toml/mod.rs

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ pub struct DetailedTomlDependency {
200200

201201
#[derive(Debug, Deserialize, Serialize)]
202202
pub struct TomlManifest {
203+
#[serde(rename = "cargo-features")]
204+
cargo_features: Option<Vec<String>>,
203205
package: Option<Box<TomlProject>>,
204206
project: Option<Box<TomlProject>>,
205207
profile: Option<TomlProfiles>,
@@ -223,8 +225,6 @@ pub struct TomlManifest {
223225
patch: Option<BTreeMap<String, BTreeMap<String, TomlDependency>>>,
224226
workspace: Option<TomlWorkspace>,
225227
badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
226-
#[serde(rename = "cargo-features")]
227-
cargo_features: Option<Vec<String>>,
228228
}
229229

230230
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
@@ -381,6 +381,44 @@ impl<'de> de::Deserialize<'de> for StringOrBool {
381381
}
382382
}
383383

384+
#[derive(Clone, Debug, Serialize)]
385+
#[serde(untagged)]
386+
pub enum VecStringOrBool {
387+
VecString(Vec<String>),
388+
Bool(bool),
389+
}
390+
391+
impl<'de> de::Deserialize<'de> for VecStringOrBool {
392+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
393+
where D: de::Deserializer<'de>
394+
{
395+
struct Visitor;
396+
397+
impl<'de> de::Visitor<'de> for Visitor {
398+
type Value = VecStringOrBool;
399+
400+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
401+
formatter.write_str("a boolean or vector of strings")
402+
}
403+
404+
fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
405+
where V: de::SeqAccess<'de>
406+
{
407+
let seq = de::value::SeqAccessDeserializer::new(v);
408+
Vec::deserialize(seq).map(VecStringOrBool::VecString)
409+
}
410+
411+
fn visit_bool<E>(self, b: bool) -> Result<Self::Value, E>
412+
where E: de::Error,
413+
{
414+
Ok(VecStringOrBool::Bool(b))
415+
}
416+
}
417+
418+
deserializer.deserialize_any(Visitor)
419+
}
420+
}
421+
384422
#[derive(Deserialize, Serialize, Clone, Debug)]
385423
pub struct TomlProject {
386424
name: String,
@@ -390,7 +428,7 @@ pub struct TomlProject {
390428
links: Option<String>,
391429
exclude: Option<Vec<String>>,
392430
include: Option<Vec<String>>,
393-
publish: Option<bool>,
431+
publish: Option<VecStringOrBool>,
394432
workspace: Option<String>,
395433
#[serde(rename = "im-a-teapot")]
396434
im_a_teapot: Option<bool>,
@@ -655,7 +693,16 @@ impl TomlManifest {
655693
}
656694
};
657695
let profiles = build_profiles(&me.profile);
658-
let publish = project.publish.unwrap_or(true);
696+
let publish = match project.publish {
697+
Some(VecStringOrBool::VecString(ref vecstring)) => {
698+
features.require(Feature::alternative_registries()).chain_err(|| {
699+
"the `publish` manifest key is unstable for anything other than a value of true or false"
700+
})?;
701+
Some(vecstring.clone())
702+
},
703+
Some(VecStringOrBool::Bool(false)) => Some(vec![]),
704+
None | Some(VecStringOrBool::Bool(true)) => None,
705+
};
659706
let mut manifest = Manifest::new(summary,
660707
targets,
661708
exclude,

src/crates-io/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ pub struct NewCrateDependency {
100100
pub version_req: String,
101101
pub target: Option<String>,
102102
pub kind: String,
103+
#[serde(skip_serializing_if = "Option::is_none")]
104+
pub registry: Option<String>,
103105
}
104106

105107
#[derive(Deserialize)]

0 commit comments

Comments
 (0)