From d3827f86c2043046a8b5f6523051f4192c6bc9fb Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Sun, 1 Nov 2020 01:04:28 -0700 Subject: [PATCH] add support for loading pypi cred from token and pypirc --- Cargo.lock | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 ++- src/main.rs | 48 ++++++++++++++++++++++++++-- 3 files changed, 139 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 500a96454..66c27b739 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,18 @@ version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c" +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "atty" version = "0.2.14" @@ -126,6 +138,17 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -285,6 +308,18 @@ dependencies = [ "bitflags", ] +[[package]] +name = "configparser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe1d7dcda7d1da79e444bdfba1465f2f849a58b07774e1df473ee77030cb47a7" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "core-foundation" version = "0.7.0" @@ -316,6 +351,17 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg 1.0.1", + "cfg-if 0.1.10", + "lazy_static", +] + [[package]] name = "crypto-mac" version = "0.8.0" @@ -344,6 +390,26 @@ dependencies = [ "generic-array", ] +[[package]] +name = "dirs" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + [[package]] name = "dtoa" version = "0.4.6" @@ -813,6 +879,8 @@ dependencies = [ "bytesize", "cargo_metadata", "cbindgen", + "configparser", + "dirs", "flate2", "glob", "goblin", @@ -1361,6 +1429,17 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + [[package]] name = "regex" version = "1.4.1" @@ -1449,6 +1528,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rust-argon2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" +dependencies = [ + "base64 0.12.3", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + [[package]] name = "rustc-demangle" version = "0.1.18" diff --git a/Cargo.toml b/Cargo.toml index c19a04feb..cbe4b0c54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,8 @@ toml = "0.5.6" walkdir = "2.3.1" zip = "0.5.5" thiserror = "1.0.20" +dirs = { version = "3.0.1", optional = true } +configparser = { version = "1.0.0", optional = true } [dev-dependencies] indoc = "1.0.2" @@ -66,7 +68,7 @@ indoc = "1.0.2" [features] default = ["auditwheel", "log", "upload", "rustls", "human-panic"] auditwheel = [] -upload = ["reqwest", "rpassword"] +upload = ["reqwest", "rpassword", "configparser", "dirs"] password-storage = ["upload", "keyring"] log = ["pretty_env_logger"] # We use rustls for manylinux compliance and because unlike both dynamic and diff --git a/src/main.rs b/src/main.rs index e96319dc8..6defba660 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,8 @@ use anyhow::{bail, Context, Result}; #[cfg(feature = "upload")] use bytesize::ByteSize; use cargo_metadata::MetadataCommand; +#[cfg(feature = "upload")] +use configparser::ini::Ini; #[cfg(feature = "human-panic")] use human_panic::setup_panic; #[cfg(feature = "password-storage")] @@ -70,14 +72,54 @@ fn get_username() -> String { } #[cfg(feature = "upload")] -/// Asks for username and password for a registry account where missing. -fn complete_registry(opt: &PublishOpt) -> Result<(Registry, bool)> { +fn load_pypi_cred_from_config(package_name: &str) -> Option<(String, String)> { + if let Some(mut config_path) = dirs::home_dir() { + config_path.push(".pypirc"); + if let Ok(pypirc) = fs::read_to_string(config_path.as_path()) { + let mut config = Ini::new(); + if config.read(pypirc).is_err() { + return None; + } + + if let (Some(username), Some(password)) = ( + config.get(package_name, "username"), + config.get(package_name, "password"), + ) { + return Some((username, password)); + } + } + } + + None +} + +#[cfg(feature = "upload")] +fn resolve_pypi_cred(opt: &PublishOpt, package_name: &str) -> (String, String, bool) { + // API token from environment variable takes priority + if let Ok(token) = env::var("MATURIN_PYPI_TOKEN") { + return ("__token__".to_string(), token, false); + } + + // load creds from pypirc if found + if let Some((username, password)) = load_pypi_cred_from_config(package_name) { + println!("🔐 Using credential in pypirc for upload"); + return (username, password, false); + } + + // fallback to username and password let username = opt.username.clone().unwrap_or_else(get_username); let (password, reenter) = match opt.password { Some(ref password) => (password.clone(), false), None => get_password(&username), }; + (username, password, reenter) +} + +#[cfg(feature = "upload")] +/// Asks for username and password for a registry account where missing. +fn complete_registry(opt: &PublishOpt, package_name: &str) -> Result<(Registry, bool)> { + let (username, password, reenter) = resolve_pypi_cred(opt, package_name); let registry = Registry::new(username, password, Url::parse(&opt.registry)?); Ok((registry, reenter)) @@ -321,7 +363,7 @@ fn upload_ui(build: BuildOptions, publish: &PublishOpt, no_sdist: bool) -> Resul } } - let (mut registry, reenter) = complete_registry(&publish)?; + let (mut registry, reenter) = complete_registry(&publish, &build_context.metadata21.name)?; loop { println!("🚀 Uploading {} packages", wheels.len());