From d7280570d6ffc0be1dc4d40c61c3cc97130576f3 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 2 Mar 2023 22:37:45 +1100 Subject: [PATCH 1/6] Add option `--no-extract-github-token-from-config` Signed-off-by: Jiahao XU --- crates/bin/src/args.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/bin/src/args.rs b/crates/bin/src/args.rs index 0668c0935..0b8e134d8 100644 --- a/crates/bin/src/args.rs +++ b/crates/bin/src/args.rs @@ -145,6 +145,14 @@ pub struct Args { #[clap(help_heading = "Overrides", long, value_delimiter(','))] pub disable_strategies: Vec, + /// If `--github-token` or environment variable `GITHUB_TOKEN` is not + /// specified, then cargo-binstall will try to extract github token from + /// `$HOME/.git-credentials` or `$HOME/.config/gh/hosts.yml` by default. + /// + /// This option can be used to disable that behavior. + #[clap(help_heading = "Overrides", long)] + pub no_extract_github_token_from_config: bool, + /// Disable symlinking / versioned updates. /// /// By default, Binstall will install a binary named `-` in the install path, and From 02644a864cde0e2cb01e2aa776bf12832b5eb6dc Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 2 Mar 2023 23:02:44 +1100 Subject: [PATCH 2/6] Add new dep gh-token v0.1.0 to crates/bin Signed-off-by: Jiahao XU --- Cargo.lock | 32 ++++++++++++++++++++++++++++++++ crates/bin/Cargo.toml | 1 + 2 files changed, 33 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index c78e08663..6514754ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,6 +346,7 @@ dependencies = [ "embed-resource", "file-format", "fs-lock", + "gh-token", "log", "miette", "mimalloc", @@ -803,6 +804,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "gh-token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc623f67e9004eac91ad251d7d4c0b1fa7ac53646c09d9c8ef6e27a21b2d02fd" +dependencies = [ + "home", + "serde", + "serde_derive", + "serde_yaml", +] + [[package]] name = "gimli" version = "0.27.2" @@ -1964,6 +1977,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -2541,6 +2567,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "unsafe-libyaml" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2" + [[package]] name = "untrusted" version = "0.7.1" diff --git a/crates/bin/Cargo.toml b/crates/bin/Cargo.toml index 2c717027a..805de46ec 100644 --- a/crates/bin/Cargo.toml +++ b/crates/bin/Cargo.toml @@ -29,6 +29,7 @@ compact_str = "0.7.0" dirs = "4.0.0" file-format = { version = "0.14.0", default-features = false } fs-lock = { version = "0.1.0", path = "../fs-lock" } +gh-token = "0.1.0" log = { version = "0.4.17", features = ["std"] } miette = "5.5.0" mimalloc = { version = "0.1.34", default-features = false, optional = true } From 3d1235ad759da165946738c128cceb80167b3cea Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 2 Mar 2023 23:03:41 +1100 Subject: [PATCH 3/6] Extract github-token from git-credentials or gh config if `--github-token` or environment variable `GITHUB_TOKEN` is not present. Signed-off-by: Jiahao XU --- crates/bin/src/args.rs | 52 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/crates/bin/src/args.rs b/crates/bin/src/args.rs index 0b8e134d8..4fadc91ff 100644 --- a/crates/bin/src/args.rs +++ b/crates/bin/src/args.rs @@ -1,9 +1,9 @@ use std::{ env, ffi::OsString, - fmt, + fmt, fs, num::{NonZeroU64, ParseIntError}, - path::PathBuf, + path::{Path, PathBuf}, str::FromStr, }; @@ -14,6 +14,7 @@ use binstalk::{ }; use clap::{error::ErrorKind, CommandFactory, Parser, ValueEnum}; use compact_str::CompactString; +use dirs::home_dir; use log::LevelFilter; use semver::VersionReq; use strum::EnumCount; @@ -460,9 +461,36 @@ You cannot use --{option} and specify multiple packages at the same time. Do one .exit() } + if opts.github_token.is_none() && !opts.no_extract_github_token_from_config { + if let Some(home) = home_dir() { + if let Some(github_token) = try_extract_from_git_credentials(&home) { + opts.github_token = Some(github_token); + } else if let Ok(github_token) = gh_token::get() { + opts.github_token = Some(github_token.into()); + } + } + } + opts } +fn try_extract_from_git_credentials(home: &Path) -> Option { + fs::read_to_string(home.join(".git-credentials")) + .ok()? + .lines() + .find_map(extract_github_token_from_git_credentials_line) + .map(CompactString::from) +} + +fn extract_github_token_from_git_credentials_line(line: &str) -> Option<&str> { + let cred = line + .trim() + .strip_prefix("https://")? + .strip_suffix("@github.com")?; + + Some(cred.split_once(':')?.1) +} + #[cfg(test)] mod test { use super::*; @@ -471,4 +499,24 @@ mod test { fn verify_cli() { Args::command().debug_assert() } + + const GIT_CREDENTIALS_TEST_CASES: &[(&str, Option<&str>)] = &[ + // Success + ("https://NobodyXu:gho_asdc@github.com", Some("gho_asdc")), + ( + "https://NobodyXu:gho_asdc12dz@github.com", + Some("gho_asdc12dz"), + ), + // Failure + ("http://NobodyXu:gho_asdc@github.com", None), + ("https://NobodyXu:gho_asdc@gitlab.com", None), + ("https://NobodyXugho_asdc@github.com", None), + ]; + + #[test] + fn test_extract_github_token_from_git_credentials_line() { + GIT_CREDENTIALS_TEST_CASES.iter().for_each(|(line, res)| { + assert_eq!(extract_github_token_from_git_credentials_line(line), *res); + }) + } } From 2fe4fe8f6f6f61a1f0e6c4f298649c8320cb18e7 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 2 Mar 2023 23:19:02 +1100 Subject: [PATCH 4/6] Improve scraping `.git-credentials`: Respect `XDG_CONFIG_HOME` Signed-off-by: Jiahao XU --- crates/bin/src/args.rs | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/crates/bin/src/args.rs b/crates/bin/src/args.rs index 4fadc91ff..716a4bab5 100644 --- a/crates/bin/src/args.rs +++ b/crates/bin/src/args.rs @@ -1,7 +1,7 @@ use std::{ env, ffi::OsString, - fmt, fs, + fmt, fs, iter, num::{NonZeroU64, ParseIntError}, path::{Path, PathBuf}, str::FromStr, @@ -462,20 +462,36 @@ You cannot use --{option} and specify multiple packages at the same time. Do one } if opts.github_token.is_none() && !opts.no_extract_github_token_from_config { - if let Some(home) = home_dir() { - if let Some(github_token) = try_extract_from_git_credentials(&home) { - opts.github_token = Some(github_token); - } else if let Ok(github_token) = gh_token::get() { - opts.github_token = Some(github_token.into()); - } + if let Some(github_token) = try_extract_from_git_credentials() { + opts.github_token = Some(github_token); + } else if let Ok(github_token) = gh_token::get() { + opts.github_token = Some(github_token.into()); } } opts } -fn try_extract_from_git_credentials(home: &Path) -> Option { - fs::read_to_string(home.join(".git-credentials")) +fn try_extract_from_git_credentials() -> Option { + home_dir() + .map(|mut home| { + home.push(".git-credentials"); + home + }) + .into_iter() + .chain(iter::from_fn(|| { + let home = env::var_os("XDG_CONFIG_HOME")?; + (!home.is_empty()).then(|| { + let mut path = PathBuf::from(home); + path.push("git/credentials"); + path + }) + })) + .find_map(try_extract_from_git_credentials_from) +} + +fn try_extract_from_git_credentials_from(path: PathBuf) -> Option { + fs::read_to_string(path) .ok()? .lines() .find_map(extract_github_token_from_git_credentials_line) From c8e420bc0c0461ef777c96a2669aed686da9f167 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 2 Mar 2023 23:33:49 +1100 Subject: [PATCH 5/6] Rename `--no-extract-github-token-from-file` => `--no-scrap-github-token` Signed-off-by: Jiahao XU --- crates/bin/src/args.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bin/src/args.rs b/crates/bin/src/args.rs index 716a4bab5..bfb2127aa 100644 --- a/crates/bin/src/args.rs +++ b/crates/bin/src/args.rs @@ -152,7 +152,7 @@ pub struct Args { /// /// This option can be used to disable that behavior. #[clap(help_heading = "Overrides", long)] - pub no_extract_github_token_from_config: bool, + pub no_scrap_github_token: bool, /// Disable symlinking / versioned updates. /// @@ -461,7 +461,7 @@ You cannot use --{option} and specify multiple packages at the same time. Do one .exit() } - if opts.github_token.is_none() && !opts.no_extract_github_token_from_config { + if opts.github_token.is_none() && !opts.no_scrap_github_token { if let Some(github_token) = try_extract_from_git_credentials() { opts.github_token = Some(github_token); } else if let Ok(github_token) = gh_token::get() { From 07dd41c62631fad59e5d808df3460f21b0ca48c6 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Fri, 3 Mar 2023 12:56:59 +1100 Subject: [PATCH 6/6] Rename `--no-scrap-github-token` => `--no-discover-github-token` Signed-off-by: Jiahao XU --- crates/bin/src/args.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bin/src/args.rs b/crates/bin/src/args.rs index bfb2127aa..f24012886 100644 --- a/crates/bin/src/args.rs +++ b/crates/bin/src/args.rs @@ -3,7 +3,7 @@ use std::{ ffi::OsString, fmt, fs, iter, num::{NonZeroU64, ParseIntError}, - path::{Path, PathBuf}, + path::PathBuf, str::FromStr, }; @@ -152,7 +152,7 @@ pub struct Args { /// /// This option can be used to disable that behavior. #[clap(help_heading = "Overrides", long)] - pub no_scrap_github_token: bool, + pub no_discover_github_token: bool, /// Disable symlinking / versioned updates. /// @@ -461,7 +461,7 @@ You cannot use --{option} and specify multiple packages at the same time. Do one .exit() } - if opts.github_token.is_none() && !opts.no_scrap_github_token { + if opts.github_token.is_none() && !opts.no_discover_github_token { if let Some(github_token) = try_extract_from_git_credentials() { opts.github_token = Some(github_token); } else if let Ok(github_token) = gh_token::get() {