mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-24 14:28:42 +00:00
Use git credential helper (#1871)
* Use git credential helper * Fix compilation Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add back `gh auth token` in `gh_token::get` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix confusing `.expect` msg Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> --------- Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> Co-authored-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
8dbc22a45b
commit
b1aaafcd75
1 changed files with 85 additions and 22 deletions
|
@ -4,33 +4,96 @@ use std::{
|
|||
str,
|
||||
};
|
||||
|
||||
use tokio::process::Command;
|
||||
use zeroize::Zeroizing;
|
||||
use tokio::{io::AsyncWriteExt, process::Command};
|
||||
use zeroize::{Zeroize, Zeroizing};
|
||||
|
||||
pub(super) async fn get() -> io::Result<Zeroizing<Box<str>>> {
|
||||
let Output { status, stdout, .. } = Command::new("gh")
|
||||
let output = Command::new("gh")
|
||||
.args(["auth", "token"])
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::null())
|
||||
.output()
|
||||
.stdout_with_optional_input(None)
|
||||
.await?;
|
||||
|
||||
let stdout = Zeroizing::new(stdout);
|
||||
|
||||
if !status.success() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("process exited with `{status}`"),
|
||||
));
|
||||
if !output.is_empty() {
|
||||
return Ok(output);
|
||||
}
|
||||
|
||||
let s = str::from_utf8(&stdout).map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("Invalid output, expected utf8: {err}"),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(Zeroizing::new(s.trim().into()))
|
||||
Command::new("git")
|
||||
.args(["credential", "fill"])
|
||||
.stdout_with_optional_input(Some("host=github.com\nprotocol=https".as_bytes()))
|
||||
.await?
|
||||
.lines()
|
||||
.find_map(|line| {
|
||||
line.trim()
|
||||
.strip_prefix("password=")
|
||||
.map(|token| Zeroizing::new(token.into()))
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Password not found in `git credential fill` output",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
trait CommandExt {
|
||||
// Helper function to execute a command, optionally with input
|
||||
async fn stdout_with_optional_input(
|
||||
&mut self,
|
||||
input: Option<&[u8]>,
|
||||
) -> io::Result<Zeroizing<Box<str>>>;
|
||||
}
|
||||
|
||||
impl CommandExt for Command {
|
||||
async fn stdout_with_optional_input(
|
||||
&mut self,
|
||||
input: Option<&[u8]>,
|
||||
) -> io::Result<Zeroizing<Box<str>>> {
|
||||
self.stdout(Stdio::piped())
|
||||
.stderr(Stdio::null())
|
||||
.stdin(if input.is_some() {
|
||||
Stdio::piped()
|
||||
} else {
|
||||
Stdio::null()
|
||||
});
|
||||
|
||||
let mut child = self.spawn()?;
|
||||
|
||||
if let Some(input) = input {
|
||||
child.stdin.take().unwrap().write_all(input).await?;
|
||||
}
|
||||
|
||||
let Output { status, stdout, .. } = child.wait_with_output().await?;
|
||||
|
||||
if status.success() {
|
||||
let s = String::from_utf8(stdout).map_err(|err| {
|
||||
let msg = format!(
|
||||
"Invalid output for `{:?}`, expected utf8: {err}",
|
||||
self.as_std()
|
||||
);
|
||||
|
||||
zeroize_and_drop(err.into_bytes());
|
||||
|
||||
io::Error::new(io::ErrorKind::InvalidData, msg)
|
||||
})?;
|
||||
|
||||
let trimmed = s.trim();
|
||||
|
||||
Ok(if trimmed.len() == s.len() {
|
||||
Zeroizing::new(s.into_boxed_str())
|
||||
} else {
|
||||
Zeroizing::new(trimmed.into())
|
||||
})
|
||||
} else {
|
||||
zeroize_and_drop(stdout);
|
||||
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("`{:?}` process exited with `{status}`", self.as_std()),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn zeroize_and_drop(mut bytes: Vec<u8>) {
|
||||
bytes.zeroize();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue