From: Chris Swindle Date: Thu, 26 Oct 2017 13:54:13 +0000 (+0100) Subject: Merge branch 'credentials-hosts' of github.com:dethoter/cargo into registry-login X-Git-Tag: archive/raspbian/0.35.0-2+rpi1~3^2^2^2^2^2^2^2~22^2~5^2~13^2~7 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=e911a25f42030df8a6a93c6adc937066fce93795;p=cargo.git Merge branch 'credentials-hosts' of github.com:dethoter/cargo into registry-login Conflicts: src/bin/login.rs src/cargo/ops/registry.rs tests/login.rs tests/publish.rs --- e911a25f42030df8a6a93c6adc937066fce93795 diff --cc src/bin/login.rs index 8819eee33,468c8453c..7a5397778 mode 100644,100644..100755 --- a/src/bin/login.rs +++ b/src/bin/login.rs @@@ -42,16 -39,20 +42,20 @@@ pub fn execute(options: Options, config options.flag_quiet, &options.flag_color, options.flag_frozen, - options.flag_locked)?; + options.flag_locked, + &options.flag_z)?; - let token = match options.arg_token.clone() { + let token = match options.arg_token { Some(token) => token, None => { - let src = SourceId::crates_io(config)?; - let mut src = RegistrySource::remote(&src, config); - src.update()?; - let config = src.config()?.unwrap(); - let host = options.flag_host.clone().unwrap_or(config.api.unwrap()); + let host = match options.flag_host { + Some(ref host) => host.clone(), + None => { + let src = SourceId::crates_io(config)?; + let mut src = RegistrySource::remote(&src, config); + src.update()?; - src.config()?.unwrap().api ++ src.config()?.unwrap().api.unwrap() + } + }; - println!("please visit {}me and paste the API Token below", host); let mut line = String::new(); let input = io::stdin(); diff --cc src/cargo/core/source/source_id.rs index 75d180d0f,000000000..566e9855a mode 100644,000000..100644 --- a/src/cargo/core/source/source_id.rs +++ b/src/cargo/core/source/source_id.rs @@@ -1,522 -1,0 +1,522 @@@ +use std::cmp::{self, Ordering}; +use std::fmt::{self, Formatter}; +use std::hash::{self, Hash}; +use std::path::Path; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT}; +use std::sync::atomic::Ordering::SeqCst; + +use serde::ser; +use serde::de; +use url::Url; + +use ops; +use sources::git; +use sources::{PathSource, GitSource, RegistrySource, CRATES_IO}; +use sources::DirectorySource; +use util::{Config, CargoResult, ToUrl}; + +/// Unique identifier for a source of packages. +#[derive(Clone, Eq, Debug)] +pub struct SourceId { + inner: Arc, +} + +#[derive(Eq, Clone, Debug)] +struct SourceIdInner { + /// The source URL + url: Url, + /// `git::canonicalize_url(url)` for the url field + canonical_url: Url, + /// The source kind + kind: Kind, + // e.g. the exact git revision of the specified branch for a Git Source + precise: Option, +} + +/// The possible kinds of code source. Along with a URL, this fully defines the +/// source +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum Kind { + /// Kind::Git() represents a git repository + Git(GitReference), + /// represents a local path + Path, + /// represents a remote registry + Registry, + /// represents a local filesystem-based registry + LocalRegistry, + /// represents a directory-based registry + Directory, +} + +/// Information to find a specific commit in a git repository +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum GitReference { + /// from a tag + Tag(String), + /// from the HEAD of a branch + Branch(String), + /// from a specific revision + Rev(String), +} + +impl SourceId { + /// Create a SourceId object from the kind and url. + /// + /// The canonical url will be calculated, but the precise field will not + fn new(kind: Kind, url: Url) -> CargoResult { + let source_id = SourceId { + inner: Arc::new(SourceIdInner { + kind: kind, + canonical_url: git::canonicalize_url(&url)?, + url: url, + precise: None, + }), + }; + Ok(source_id) + } + + /// Parses a source URL and returns the corresponding ID. + /// + /// ## Example + /// + /// ``` + /// use cargo::core::SourceId; + /// SourceId::from_url("git+https://github.com/alexcrichton/\ + /// libssh2-static-sys#80e71a3021618eb05\ + /// 656c58fb7c5ef5f12bc747f"); + /// ``` + pub fn from_url(string: &str) -> CargoResult { + let mut parts = string.splitn(2, '+'); + let kind = parts.next().unwrap(); + let url = parts.next().ok_or_else(|| format!("invalid source `{}`", string))?; + + match kind { + "git" => { + let mut url = url.to_url()?; + let mut reference = GitReference::Branch("master".to_string()); + for (k, v) in url.query_pairs() { + match &k[..] { + // map older 'ref' to branch + "branch" | + "ref" => reference = GitReference::Branch(v.into_owned()), + + "rev" => reference = GitReference::Rev(v.into_owned()), + "tag" => reference = GitReference::Tag(v.into_owned()), + _ => {} + } + } + let precise = url.fragment().map(|s| s.to_owned()); + url.set_fragment(None); + url.set_query(None); + Ok(SourceId::for_git(&url, reference)?.with_precise(precise)) + }, + "registry" => { + let url = url.to_url()?; + Ok(SourceId::new(Kind::Registry, url)? + .with_precise(Some("locked".to_string()))) + } + "path" => { + let url = url.to_url()?; + SourceId::new(Kind::Path, url) + } + kind => Err(format!("unsupported source protocol: {}", kind).into()) + } + } + + /// A view of the `SourceId` that can be `Display`ed as a URL + pub fn to_url(&self) -> SourceIdToUrl { + SourceIdToUrl { inner: &*self.inner } + } + + /// Create a SourceId from a filesystem path. + /// + /// Pass absolute path + pub fn for_path(path: &Path) -> CargoResult { + let url = path.to_url()?; + SourceId::new(Kind::Path, url) + } + + /// Crate a SourceId from a git reference + pub fn for_git(url: &Url, reference: GitReference) -> CargoResult { + SourceId::new(Kind::Git(reference), url.clone()) + } + + /// Create a SourceId from a registry url + pub fn for_registry(url: &Url) -> CargoResult { + SourceId::new(Kind::Registry, url.clone()) + } + + /// Create a SourceId from a local registry path + pub fn for_local_registry(path: &Path) -> CargoResult { + let url = path.to_url()?; + SourceId::new(Kind::LocalRegistry, url) + } + + /// Create a SourceId from a directory path + pub fn for_directory(path: &Path) -> CargoResult { + let url = path.to_url()?; + SourceId::new(Kind::Directory, url) + } + + /// Returns the `SourceId` corresponding to the main repository. + /// + /// This is the main cargo registry by default, but it can be overridden in + /// a `.cargo/config`. + pub fn crates_io(config: &Config) -> CargoResult { - let cfg = ops::registry_configuration(config)?; ++ let cfg = ops::registry_configuration(config, None)?; + let url = if let Some(ref index) = cfg.index { + static WARNED: AtomicBool = ATOMIC_BOOL_INIT; + if !WARNED.swap(true, SeqCst) { + config.shell().warn("custom registry support via \ + the `registry.index` configuration is \ + being removed, this functionality \ + will not work in the future")?; + } + &index[..] + } else { + CRATES_IO + }; + let url = url.to_url()?; + SourceId::for_registry(&url) + } + + pub fn alt_registry(config: &Config, key: &str) -> CargoResult { + if let Some(index) = config.get_string(&format!("registries.{}.index", key))? { + let url = index.val.to_url()?; + Ok(SourceId { + inner: Arc::new(SourceIdInner { + kind: Kind::Registry, + canonical_url: git::canonicalize_url(&url)?, + url: url, + precise: None, + }), + }) + } else { Err(format!("No index found for registry: `{}`", key).into()) } + } + + /// Get this source URL + pub fn url(&self) -> &Url { + &self.inner.url + } + + pub fn display_registry(&self) -> String { + format!("registry `{}`", self.url()) + } + + /// Is this source from a filesystem path + pub fn is_path(&self) -> bool { + self.inner.kind == Kind::Path + } + + /// Is this source from a registry (either local or not) + pub fn is_registry(&self) -> bool { + match self.inner.kind { + Kind::Registry | Kind::LocalRegistry => true, + _ => false, + } + } + + /// Is this source from a git repository + pub fn is_git(&self) -> bool { + match self.inner.kind { + Kind::Git(_) => true, + _ => false, + } + } + + /// Creates an implementation of `Source` corresponding to this ID. + pub fn load<'a>(&self, config: &'a Config) -> CargoResult> { + trace!("loading SourceId; {}", self); + match self.inner.kind { + Kind::Git(..) => Ok(Box::new(GitSource::new(self, config)?)), + Kind::Path => { + let path = match self.inner.url.to_file_path() { + Ok(p) => p, + Err(()) => panic!("path sources cannot be remote"), + }; + Ok(Box::new(PathSource::new(&path, self, config))) + } + Kind::Registry => Ok(Box::new(RegistrySource::remote(self, config))), + Kind::LocalRegistry => { + let path = match self.inner.url.to_file_path() { + Ok(p) => p, + Err(()) => panic!("path sources cannot be remote"), + }; + Ok(Box::new(RegistrySource::local(self, &path, config))) + } + Kind::Directory => { + let path = match self.inner.url.to_file_path() { + Ok(p) => p, + Err(()) => panic!("path sources cannot be remote"), + }; + Ok(Box::new(DirectorySource::new(&path, self, config))) + } + } + } + + /// Get the value of the precise field + pub fn precise(&self) -> Option<&str> { + self.inner.precise.as_ref().map(|s| &s[..]) + } + + /// Get the git reference if this is a git source, otherwise None. + pub fn git_reference(&self) -> Option<&GitReference> { + match self.inner.kind { + Kind::Git(ref s) => Some(s), + _ => None, + } + } + + /// Create a new SourceId from this source with the given `precise` + pub fn with_precise(&self, v: Option) -> SourceId { + SourceId { + inner: Arc::new(SourceIdInner { + precise: v, + ..(*self.inner).clone() + }) + } + } + + /// Whether the remote registry is the standard https://crates.io + pub fn is_default_registry(&self) -> bool { + match self.inner.kind { + Kind::Registry => {} + _ => return false, + } + self.inner.url.to_string() == CRATES_IO + } + + /// Hash `self` + /// + /// For paths, remove the workspace prefix so the same source will give the + /// same hash in different locations. + pub fn stable_hash(&self, workspace: &Path, into: &mut S) { + if self.is_path() { + if let Ok(p) = self.inner.url.to_file_path().unwrap().strip_prefix(workspace) { + self.inner.kind.hash(into); + p.to_str().unwrap().hash(into); + return + } + } + self.hash(into) + } +} + +impl PartialEq for SourceId { + fn eq(&self, other: &SourceId) -> bool { + (*self.inner).eq(&*other.inner) + } +} + +impl PartialOrd for SourceId { + fn partial_cmp(&self, other: &SourceId) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for SourceId { + fn cmp(&self, other: &SourceId) -> Ordering { + self.inner.cmp(&other.inner) + } +} + +impl ser::Serialize for SourceId { + fn serialize(&self, s: S) -> Result + where S: ser::Serializer, + { + if self.is_path() { + None::.serialize(s) + } else { + s.collect_str(&self.to_url()) + } + } +} + +impl<'de> de::Deserialize<'de> for SourceId { + fn deserialize(d: D) -> Result + where D: de::Deserializer<'de>, + { + let string = String::deserialize(d)?; + SourceId::from_url(&string).map_err(de::Error::custom) + } +} + +impl fmt::Display for SourceId { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match *self.inner { + SourceIdInner { kind: Kind::Path, ref url, .. } => { + fmt::Display::fmt(url, f) + } + SourceIdInner { kind: Kind::Git(ref reference), ref url, + ref precise, .. } => { + write!(f, "{}", url)?; + if let Some(pretty) = reference.pretty_ref() { + write!(f, "?{}", pretty)?; + } + + if let Some(ref s) = *precise { + let len = cmp::min(s.len(), 8); + write!(f, "#{}", &s[..len])?; + } + Ok(()) + } + SourceIdInner { kind: Kind::Registry, ref url, .. } | + SourceIdInner { kind: Kind::LocalRegistry, ref url, .. } => { + write!(f, "registry `{}`", url) + } + SourceIdInner { kind: Kind::Directory, ref url, .. } => { + write!(f, "dir {}", url) + } + } + } +} + +// This custom implementation handles situations such as when two git sources +// point at *almost* the same URL, but not quite, even when they actually point +// to the same repository. +/// This method tests for self and other values to be equal, and is used by ==. +/// +/// For git repositories, the canonical url is checked. +impl PartialEq for SourceIdInner { + fn eq(&self, other: &SourceIdInner) -> bool { + if self.kind != other.kind { + return false; + } + if self.url == other.url { + return true; + } + + match (&self.kind, &other.kind) { + (&Kind::Git(ref ref1), &Kind::Git(ref ref2)) => { + ref1 == ref2 && self.canonical_url == other.canonical_url + } + _ => false, + } + } +} + +impl PartialOrd for SourceIdInner { + fn partial_cmp(&self, other: &SourceIdInner) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for SourceIdInner { + fn cmp(&self, other: &SourceIdInner) -> Ordering { + match self.kind.cmp(&other.kind) { + Ordering::Equal => {} + ord => return ord, + } + match self.url.cmp(&other.url) { + Ordering::Equal => {} + ord => return ord, + } + match (&self.kind, &other.kind) { + (&Kind::Git(ref ref1), &Kind::Git(ref ref2)) => { + (ref1, &self.canonical_url).cmp(&(ref2, &other.canonical_url)) + } + _ => self.kind.cmp(&other.kind), + } + } +} + +// The hash of SourceId is used in the name of some Cargo folders, so shouldn't +// vary. `as_str` gives the serialisation of a url (which has a spec) and so +// insulates against possible changes in how the url crate does hashing. +impl Hash for SourceId { + fn hash(&self, into: &mut S) { + self.inner.kind.hash(into); + match *self.inner { + SourceIdInner { kind: Kind::Git(..), ref canonical_url, .. } => { + canonical_url.as_str().hash(into) + } + _ => self.inner.url.as_str().hash(into), + } + } +} + +/// A `Display`able view into a SourceId that will write it as a url +pub struct SourceIdToUrl<'a> { + inner: &'a SourceIdInner, +} + +impl<'a> fmt::Display for SourceIdToUrl<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self.inner { + SourceIdInner { kind: Kind::Path, ref url, .. } => { + write!(f, "path+{}", url) + } + SourceIdInner { + kind: Kind::Git(ref reference), ref url, ref precise, .. + } => { + write!(f, "git+{}", url)?; + if let Some(pretty) = reference.pretty_ref() { + write!(f, "?{}", pretty)?; + } + if let Some(precise) = precise.as_ref() { + write!(f, "#{}", precise)?; + } + Ok(()) + } + SourceIdInner { kind: Kind::Registry, ref url, .. } => { + write!(f, "registry+{}", url) + } + SourceIdInner { kind: Kind::LocalRegistry, ref url, .. } => { + write!(f, "local-registry+{}", url) + } + SourceIdInner { kind: Kind::Directory, ref url, .. } => { + write!(f, "directory+{}", url) + } + } + } +} + +impl GitReference { + /// Returns a `Display`able view of this git reference, or None if using + /// the head of the "master" branch + pub fn pretty_ref(&self) -> Option { + match *self { + GitReference::Branch(ref s) if *s == "master" => None, + _ => Some(PrettyRef { inner: self }), + } + } +} + +/// A git reference that can be `Display`ed +pub struct PrettyRef<'a> { + inner: &'a GitReference, +} + +impl<'a> fmt::Display for PrettyRef<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self.inner { + GitReference::Branch(ref b) => write!(f, "branch={}", b), + GitReference::Tag(ref s) => write!(f, "tag={}", s), + GitReference::Rev(ref s) => write!(f, "rev={}", s), + } + } +} + +#[cfg(test)] +mod tests { + use super::{SourceId, Kind, GitReference}; + use util::ToUrl; + + #[test] + fn github_sources_equal() { + let loc = "https://github.com/foo/bar".to_url().unwrap(); + let master = Kind::Git(GitReference::Branch("master".to_string())); + let s1 = SourceId::new(master.clone(), loc).unwrap(); + + let loc = "git://github.com/foo/bar".to_url().unwrap(); + let s2 = SourceId::new(master, loc.clone()).unwrap(); + + assert_eq!(s1, s2); + + let foo = Kind::Git(GitReference::Branch("foo".to_string())); + let s3 = SourceId::new(foo, loc).unwrap(); + assert!(s1 != s3); + } +} diff --cc src/cargo/ops/registry.rs index d1bd31caf,cc63cb753..8119116f6 mode 100644,100644..100755 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@@ -202,10 -203,10 +216,10 @@@ pub fn registry(config: &Config let RegistryConfig { token: token_config, index: _index_config, - } = registry_configuration(config)?; + } = registry_configuration(config, index.clone())?; let token = token.or(token_config); let sid = match index { - Some(index) => SourceId::for_registry(&index.to_url()?), + Some(index) => SourceId::for_registry(&index.to_url()?)?, None => SourceId::crates_io(config)?, }; let api_host = { @@@ -293,8 -294,14 +307,14 @@@ pub fn http_timeout(config: &Config) - Ok(env::var("HTTP_TIMEOUT").ok().and_then(|s| s.parse().ok())) } - pub fn registry_login(config: &Config, token: String) -> CargoResult<()> { - let RegistryConfig { token: old_token, .. } = registry_configuration(config)?; + pub fn registry_login(config: &Config, + token: String, + host: Option) -> CargoResult<()> { + let RegistryConfig { - index: _, - token: old_token ++ token: old_token, ++ .. + } = registry_configuration(config, host.clone())?; + if let Some(old_token) = old_token { if old_token == token { return Ok(()); diff --cc tests/login.rs index 9bb060a1d,7907da157..7153002e6 mode 100644,100644..100755 --- a/tests/login.rs +++ b/tests/login.rs @@@ -123,6 -145,11 +145,11 @@@ fn new_credentials_is_used_instead_old( execs().with_status(0)); let config = Config::new(Shell::new(), cargo_home(), cargo_home()); + let token = config.get_string("registry.token").unwrap().map(|p| p.val); - assert!(token.unwrap() == TOKEN); + assert_eq!(token.unwrap(), TOKEN); + + let token_host = config.get_string(&format!(r#"registry.{}.token"#, registry().to_string())) + .unwrap().map(|p| p.val); - assert!(token_host.unwrap() == TOKEN); ++ assert_eq!(token_host.unwrap(), TOKEN); } diff --cc tests/publish.rs index d63087fe2,67ab05e09..d63087fe2 mode 100644,100644..100755 --- a/tests/publish.rs +++ b/tests/publish.rs