Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite login and registry cleanups. #6466

Merged
merged 1 commit into from
Dec 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 10 additions & 42 deletions src/bin/cargo/commands/login.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
use crate::command_prelude::*;

use std::io::{self, BufRead};

use cargo::core::{Source, SourceId};
use cargo::ops;
use cargo::sources::RegistrySource;
use cargo::util::CargoResultExt;

pub fn cli() -> App {
subcommand("login")
Expand All @@ -14,46 +9,19 @@ pub fn cli() -> App {
If token is not specified, it will be read from stdin.",
)
.arg(Arg::with_name("token"))
.arg(opt("host", "Host to set the token for").value_name("HOST"))
.arg(
opt("host", "Host to set the token for")
.value_name("HOST")
.hidden(true),
)
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
}

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let registry = args.registry(config)?;

let token = match args.value_of("token") {
Some(token) => token.to_string(),
None => {
let host = match registry {
Some(ref _registry) => {
return Err(failure::format_err!(
"token must be provided when \
--registry is provided."
)
.into());
}
None => {
let src = SourceId::crates_io(config)?;
let mut src = RegistrySource::remote(src, config);
src.update()?;
let config = src.config()?.unwrap();
args.value_of("host")
.map(|s| s.to_string())
.unwrap_or_else(|| config.api.unwrap())
}
};
println!("please visit {}/me and paste the API Token below", host);
let mut line = String::new();
let input = io::stdin();
input
.lock()
.read_line(&mut line)
.chain_err(|| "failed to read stdin")
.map_err(failure::Error::from)?;
line.trim().to_string()
}
};

ops::registry_login(config, token, registry)?;
ops::registry_login(
config,
args.value_of("token").map(String::from),
args.value_of("registry").map(String::from),
)?;
Ok(())
}
1 change: 0 additions & 1 deletion src/bin/cargo/commands/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
allow_dirty: args.is_present("allow-dirty"),
target: args.target(),
jobs: args.jobs()?,
registry: None,
},
)?;
Ok(())
Expand Down
1 change: 0 additions & 1 deletion src/cargo/ops/cargo_package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ pub struct PackageOpts<'cfg> {
pub verify: bool,
pub jobs: Option<u32>,
pub target: Option<String>,
pub registry: Option<String>,
}

static VCS_INFO_FILE: &'static str = ".cargo_vcs_info.json";
Expand Down
77 changes: 53 additions & 24 deletions src/cargo/ops/registry.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::collections::BTreeMap;
use std::fs::{self, File};
use std::io::{self, BufRead};
use std::iter::repeat;
use std::str;
use std::time::Duration;
Expand Down Expand Up @@ -65,6 +66,7 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
opts.token.clone(),
opts.index.clone(),
opts.registry.clone(),
true,
)?;
verify_dependencies(pkg, reg_id)?;

Expand All @@ -80,7 +82,6 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
allow_dirty: opts.allow_dirty,
target: opts.target.clone(),
jobs: opts.jobs,
registry: opts.registry.clone(),
},
)?
.unwrap();
Expand Down Expand Up @@ -320,6 +321,7 @@ pub fn registry(
token: Option<String>,
index: Option<String>,
registry: Option<String>,
force_update: bool,
) -> CargoResult<(Registry, SourceId)> {
// Parse all configuration options
let RegistryConfig {
Expand All @@ -330,9 +332,17 @@ pub fn registry(
let sid = get_source_id(config, index_config.or(index), registry)?;
let api_host = {
let mut src = RegistrySource::remote(sid, config);
src.update()
.chain_err(|| format!("failed to update {}", sid))?;
(src.config()?).unwrap().api.unwrap()
// Only update the index if the config is not available or `force` is set.
let cfg = src.config();
let cfg = if force_update || cfg.is_err() {
src.update()
.chain_err(|| format!("failed to update {}", sid))?;
cfg.or_else(|_| src.config())?
} else {
cfg.unwrap()
};
cfg.and_then(|cfg| cfg.api)
.ok_or_else(|| failure::format_err!("{} does not support API commands", sid))?
};
let handle = http_handle(config)?;
Ok((Registry::new_handle(api_host, token, handle), sid))
Expand Down Expand Up @@ -503,18 +513,51 @@ fn http_proxy_exists(config: &Config) -> CargoResult<bool> {
}
}

pub fn registry_login(config: &Config, token: String, registry: Option<String>) -> CargoResult<()> {
pub fn registry_login(
config: &Config,
token: Option<String>,
reg: Option<String>,
) -> CargoResult<()> {
let (registry, _) = registry(config, token.clone(), None, reg.clone(), false)?;

let token = match token {
Some(token) => token,
None => {
println!(
"please visit {}/me and paste the API Token below",
registry.host()
);
let mut line = String::new();
let input = io::stdin();
input
.lock()
.read_line(&mut line)
.chain_err(|| "failed to read stdin")
.map_err(failure::Error::from)?;
line.trim().to_string()
}
};

let RegistryConfig {
token: old_token, ..
} = registry_configuration(config, registry.clone())?;
} = registry_configuration(config, reg.clone())?;

if let Some(old_token) = old_token {
if old_token == token {
config.shell().status("Login", "already logged in")?;
return Ok(());
}
}

config::save_credentials(config, token, registry)
config::save_credentials(config, token, reg.clone())?;
config.shell().status(
"Login",
format!(
"token for `{}` saved",
reg.as_ref().map_or("crates.io", String::as_str)
),
)?;
Ok(())
}

pub struct OwnersOptions {
Expand Down Expand Up @@ -542,6 +585,7 @@ pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> {
opts.token.clone(),
opts.index.clone(),
opts.registry.clone(),
true,
)?;

if let Some(ref v) = opts.to_add {
Expand Down Expand Up @@ -602,7 +646,7 @@ pub fn yank(
None => failure::bail!("a version must be specified to yank"),
};

let (mut registry, _) = registry(config, token, index, reg)?;
let (mut registry, _) = registry(config, token, index, reg, true)?;

if undo {
config
Expand Down Expand Up @@ -658,22 +702,7 @@ pub fn search(
prefix
}

let sid = get_source_id(config, index, reg)?;

let mut regsrc = RegistrySource::remote(sid, config);
let cfg = match regsrc.config() {
Ok(c) => c,
Err(_) => {
regsrc
.update()
.chain_err(|| format!("failed to update {}", &sid))?;
regsrc.config()?
}
};

let api_host = cfg.unwrap().api.unwrap();
let handle = http_handle(config)?;
let mut registry = Registry::new_handle(api_host, None, handle);
let (mut registry, _) = registry(config, None, index, reg, false)?;
let (crates, total_crates) = registry
.search(query, limit)
.chain_err(|| "failed to retrieve search results from the registry")?;
Expand Down
1 change: 1 addition & 0 deletions src/cargo/sources/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ pub struct RegistryConfig {

/// API endpoint for the registry. This is what's actually hit to perform
/// operations like yanks, owner modifications, publish new crates, etc.
/// If this is None, the registry does not support API commands.
pub api: Option<String>,
}

Expand Down
11 changes: 10 additions & 1 deletion src/crates-io/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET};
pub type Result<T> = std::result::Result<T, failure::Error>;

pub struct Registry {
/// The base URL for issuing API requests.
host: String,
/// Optional authorization token.
/// If None, commands requiring authorization will fail.
token: Option<String>,
/// Curl handle for issuing requests.
handle: Easy,
}

Expand Down Expand Up @@ -51,7 +55,8 @@ pub struct NewCrate {
pub license_file: Option<String>,
pub repository: Option<String>,
pub badges: BTreeMap<String, BTreeMap<String, String>>,
#[serde(default)] pub links: Option<String>,
#[serde(default)]
pub links: Option<String>,
}

#[derive(Serialize)]
Expand Down Expand Up @@ -131,6 +136,10 @@ impl Registry {
}
}

pub fn host(&self) -> &str {
&self.host
}

pub fn add_owners(&mut self, krate: &str, owners: &[&str]) -> Result<String> {
let body = serde_json::to_string(&OwnersReq { users: owners })?;
let body = self.put(&format!("/crates/{}/owners", krate), body.as_bytes())?;
Expand Down
100 changes: 95 additions & 5 deletions tests/testsuite/alt_registry.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::support::registry::{self, alt_api_path, Package};
use crate::support::{basic_manifest, paths, project};
use std::fs::File;
use crate::support::{basic_manifest, git, paths, project};
use std::fs::{self, File};
use std::io::Write;

#[test]
Expand Down Expand Up @@ -203,13 +203,13 @@ fn depend_on_alt_registry_depends_on_crates_io() {

p.cargo("build")
.masquerade_as_nightly_cargo()
.with_stderr(&format!(
.with_stderr_unordered(&format!(
"\
[UPDATING] `{alt_reg}` index
[UPDATING] `{reg}` index
[DOWNLOADING] crates ...
[DOWNLOADED] [..] v0.0.1 (registry `[ROOT][..]`)
[DOWNLOADED] [..] v0.0.1 (registry `[ROOT][..]`)
[DOWNLOADED] baz v0.0.1 (registry `[ROOT][..]`)
[DOWNLOADED] bar v0.0.1 (registry `[ROOT][..]`)
[COMPILING] baz v0.0.1 (registry `[ROOT][..]`)
[COMPILING] bar v0.0.1 (registry `[ROOT][..]`)
[COMPILING] foo v0.0.1 ([CWD])
Expand Down Expand Up @@ -605,3 +605,93 @@ Caused by:
.run();
}
}

#[test]
fn no_api() {
Package::new("bar", "0.0.1").alternative(true).publish();
// Configure without `api`.
let repo = git2::Repository::open(registry::alt_registry_path()).unwrap();
let cfg_path = registry::alt_registry_path().join("config.json");
fs::write(
cfg_path,
format!(r#"{{"dl": "{}"}}"#, registry::alt_dl_url()),
)
.unwrap();
git::add(&repo);
git::commit(&repo);

// First check that a dependency works.
let p = project()
.file(
"Cargo.toml",
r#"
cargo-features = ["alternative-registries"]
[package]
name = "foo"
version = "0.0.1"

[dependencies.bar]
version = "0.0.1"
registry = "alternative"
"#,
)
.file("src/lib.rs", "")
.build();

p.cargo("build")
.masquerade_as_nightly_cargo()
.with_stderr(&format!(
"\
[UPDATING] `{reg}` index
[DOWNLOADING] crates ...
[DOWNLOADED] bar v0.0.1 (registry `[ROOT][..]`)
[COMPILING] bar v0.0.1 (registry `[ROOT][..]`)
[COMPILING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
",
reg = registry::alt_registry_path().to_str().unwrap()
))
.run();

// Check all of the API commands.
let err = format!(
"[ERROR] registry `{}` does not support API commands",
registry::alt_registry_path().display()
);

p.cargo("login --registry alternative TOKEN -Zunstable-options")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr_contains(&err)
.run();

p.cargo("publish --registry alternative -Zunstable-options")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr_contains(&err)
.run();

p.cargo("search --registry alternative -Zunstable-options")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr_contains(&err)
.run();

p.cargo("owner --registry alternative -Zunstable-options --list")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr_contains(&err)
.run();

p.cargo("yank --registry alternative -Zunstable-options --vers=0.0.1 bar")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr_contains(&err)
.run();

p.cargo("yank --registry alternative -Zunstable-options --vers=0.0.1 bar")
.masquerade_as_nightly_cargo()
.with_stderr_contains(&err)
.with_status(101)
.run();
}
Loading