Fix GitHub token auto discovery (#1335)

* Fix GitHub token auto discovery

Fixed #1333

 - Rm dep `gh-token` since it is broken and we can simply run
   `gh auth token` in `cargo-binstall` instead.
 - binstalk-downloader: Make sure GitHub token is at least 40B long
   and other than the `_`, composes of only alphanumeric characters.
 - Warn on failure to read `git/credential` files
 - Optimize `try_from_home` to avoid heap allocation of `PathBuf`

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>

* Fix typo and clippy

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>

* Simplify `is_valid_gh_token` & `is_ascii_alphanumeric` impl

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>

* Improve err msg in `get_inner`

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>

* Improve err msg of `cargo_binstall::gh_token::get`

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>

---------

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
Jiahao XU 2023-09-01 11:14:59 +10:00 committed by GitHub
parent 0ca38ab0e3
commit 8a08cdda6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 85 additions and 53 deletions

View file

@ -28,7 +28,6 @@ clap = { version = "4.3.0", features = ["derive", "env"] }
compact_str = "0.7.0"
dirs = "5.0.1"
file-format = { version = "0.19.0", default-features = false }
gh-token = "0.1.2"
home = "0.5.5"
log = { version = "0.4.18", features = ["std"] }
miette = "5.9.0"

View file

@ -538,12 +538,6 @@ You cannot use --{option} and specify multiple packages at the same time. Do one
if opts.github_token.is_none() {
if let Ok(github_token) = env::var("GH_TOKEN") {
opts.github_token = Some(github_token.into());
} else if !opts.no_discover_github_token {
if let Some(github_token) = crate::git_credentials::try_from_home() {
opts.github_token = Some(github_token);
} else if let Ok(github_token) = gh_token::get() {
opts.github_token = Some(github_token.into());
}
}
}

View file

@ -33,7 +33,7 @@ use tracing::{debug, error, info, warn};
use crate::{
args::{Args, Strategy},
install_path,
gh_token, git_credentials, install_path,
ui::confirm,
};
@ -107,7 +107,16 @@ pub fn install_crates(
)
.map_err(BinstallError::from)?;
let gh_api_client = GhApiClient::new(client.clone(), args.github_token);
let gh_api_client = GhApiClient::new(
client.clone(),
args.github_token.or_else(|| {
if args.no_discover_github_token {
None
} else {
git_credentials::try_from_home().or_else(gh_token::get)
}
}),
);
// Create binstall_opts
let binstall_opts = Arc::new(Options {

View file

@ -0,0 +1,38 @@
use std::{io, process};
use compact_str::CompactString;
use tracing::warn;
fn get_inner() -> io::Result<CompactString> {
let process::Output { status, stdout, .. } = process::Command::new("gh")
.args(["auth", "token"])
.stdin(process::Stdio::null())
.stdout(process::Stdio::piped())
.stderr(process::Stdio::null())
.output()?;
if !status.success() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("process exited with `{status}`"),
));
}
// Use String here instead of CompactString here since
// `CompactString::from_utf8` allocates if it's longer than 24B.
let s = String::from_utf8(stdout).map_err(|_err| {
io::Error::new(io::ErrorKind::InvalidData, "Invalid output, expected utf8")
})?;
Ok(s.trim().into())
}
pub(super) fn get() -> Option<CompactString> {
match get_inner() {
Ok(token) => Some(token),
Err(err) => {
warn!(?err, "Failed to retrieve token from `gh auth token`");
None
}
}
}

View file

@ -1,10 +1,8 @@
use std::{
env, fs,
path::{Path, PathBuf},
};
use std::{env, fs, path::PathBuf};
use compact_str::CompactString;
use dirs::home_dir;
use tracing::warn;
pub fn try_from_home() -> Option<CompactString> {
if let Some(mut home) = home_dir() {
@ -15,7 +13,9 @@ pub fn try_from_home() -> Option<CompactString> {
}
if let Some(home) = env::var_os("XDG_CONFIG_HOME") {
let home = Path::new(&home).join("git/credentials");
let mut home = PathBuf::from(home);
home.push("git/credentials");
if let Some(cred) = from_file(home) {
return Some(cred);
}
@ -25,8 +25,7 @@ pub fn try_from_home() -> Option<CompactString> {
}
fn from_file(path: PathBuf) -> Option<CompactString> {
fs::read_to_string(path)
.ok()?
read_cred_file(path)?
.lines()
.find_map(from_line)
.map(CompactString::from)
@ -41,6 +40,20 @@ fn from_line(line: &str) -> Option<&str> {
Some(cred.split_once(':')?.1)
}
fn read_cred_file(path: PathBuf) -> Option<String> {
match fs::read_to_string(&path) {
Ok(s) => Some(s),
Err(err) => {
warn!(
?err,
"Failed to read git credential file {}",
path.display()
);
None
}
}
}
#[cfg(test)]
mod test {
use super::*;

View file

@ -3,6 +3,7 @@
mod args;
mod bin_util;
mod entry;
mod gh_token;
mod git_credentials;
mod install_path;
mod logging;