include: Vec<String>,
metadata: ManifestMetadata,
profiles: Profiles,
- publish: bool,
+ publish: Option<Vec<String>>,
replace: Vec<(PackageIdSpec, Dependency)>,
patch: HashMap<Url, Vec<Dependency>>,
workspace: WorkspaceConfig,
links: Option<String>,
metadata: ManifestMetadata,
profiles: Profiles,
- publish: bool,
+ publish: Option<Vec<String>>,
replace: Vec<(PackageIdSpec, Dependency)>,
patch: HashMap<Url, Vec<Dependency>>,
workspace: WorkspaceConfig,
pub fn version(&self) -> &Version { self.package_id().version() }
pub fn warnings(&self) -> &[DelayedWarning] { &self.warnings }
pub fn profiles(&self) -> &Profiles { &self.profiles }
- pub fn publish(&self) -> bool { self.publish }
+ pub fn publish(&self) -> &Option<Vec<String>> { &self.publish }
pub fn replace(&self) -> &[(PackageIdSpec, Dependency)] { &self.replace }
pub fn original(&self) -> &TomlManifest { &self.original }
pub fn patch(&self) -> &HashMap<Url, Vec<Dependency>> { &self.patch }
pub fn targets(&self) -> &[Target] { self.manifest.targets() }
pub fn version(&self) -> &Version { self.package_id().version() }
pub fn authors(&self) -> &Vec<String> { &self.manifest.metadata().authors }
- pub fn publish(&self) -> bool { self.manifest.publish() }
+ pub fn publish(&self) -> &Option<Vec<String>> { self.manifest.publish() }
pub fn has_custom_build(&self) -> bool {
self.targets().iter().any(|t| t.is_custom_build())
pub fn publish(ws: &Workspace, opts: &PublishOpts) -> CargoResult<()> {
let pkg = ws.current()?;
- if !pkg.publish() {
- bail!("some crates cannot be published.\n\
- `{}` is marked as unpublishable", pkg.name());
+ if let &Some(ref allowed_registries) = pkg.publish() {
+ let index = opts.index.clone();
+ if index.is_none() || !allowed_registries.contains(&index.unwrap()) {
+ bail!("some crates cannot be published.\n\
+ `{}` is marked as unpublishable", pkg.name());
+ }
}
+
if !pkg.manifest().patch().is_empty() {
bail!("published crates cannot contain [patch] sections");
}
}
}
+#[derive(Clone, Debug, Serialize)]
+#[serde(untagged)]
+pub enum VecStringOrBool {
+ VecString(Vec<String>),
+ Bool(bool),
+}
+
+impl<'de> de::Deserialize<'de> for VecStringOrBool {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where D: de::Deserializer<'de>
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = VecStringOrBool;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("a boolean or vector of strings")
+ }
+
+ fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
+ where V: de::SeqAccess<'de>
+ {
+ let seq = de::value::SeqAccessDeserializer::new(v);
+ Vec::deserialize(seq).map(VecStringOrBool::VecString)
+ }
+
+ fn visit_bool<E>(self, b: bool) -> Result<Self::Value, E>
+ where E: de::Error,
+ {
+ Ok(VecStringOrBool::Bool(b))
+ }
+ }
+
+ deserializer.deserialize_any(Visitor)
+ }
+}
+
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct TomlProject {
name: String,
links: Option<String>,
exclude: Option<Vec<String>>,
include: Option<Vec<String>>,
- publish: Option<bool>,
+ publish: Option<VecStringOrBool>,
workspace: Option<String>,
#[serde(rename = "im-a-teapot")]
im_a_teapot: Option<bool>,
}
};
let profiles = build_profiles(&me.profile);
- let publish = project.publish.unwrap_or(true);
+ let publish = match project.publish {
+ Some(VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()),
+ Some(VecStringOrBool::Bool(false)) => Some(vec![]),
+ _ => None,
+ };
let empty = Vec::new();
let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
let features = Features::new(cargo_features, &mut warnings)?;
// Ensure the API request wasn't actually made
assert!(!publish::upload_path().join("api/v1/crates/new").exists());
}
+
+#[test]
+fn index_not_in_publish_list() {
+ publish::setup();
+
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ license = "MIT"
+ description = "foo"
+ publish = [
+ "test"
+ ]
+ "#)
+ .file("src/main.rs", "fn main() {}");
+
+ assert_that(p.cargo_process("publish")
+ .arg("--index").arg(publish::registry().to_string()),
+ execs().with_status(101).with_stderr("\
+[ERROR] some crates cannot be published.
+`foo` is marked as unpublishable
+"));
+}
+
+#[test]
+fn publish_empty_list() {
+ publish::setup();
+
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ license = "MIT"
+ description = "foo"
+ publish = []
+ "#)
+ .file("src/main.rs", "fn main() {}");
+
+ assert_that(p.cargo_process("publish")
+ .arg("--index").arg(publish::registry().to_string()),
+ execs().with_status(101).with_stderr("\
+[ERROR] some crates cannot be published.
+`foo` is marked as unpublishable
+"));
+}
+
+#[test]
+fn publish_allowed_index() {
+ publish::setup();
+
+ let p = project("foo");
+ p.build();
+
+ repo(&paths::root().join("foo"))
+ .file("Cargo.toml", &format!(r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ license = "MIT"
+ description = "foo"
+ documentation = "foo"
+ homepage = "foo"
+ publish = ["{}"]
+ "#, publish::registry().to_string()))
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ assert_that(p.cargo("publish")
+ .arg("--index").arg(publish::registry().to_string()),
+ execs().with_status(0));
+}