use git2::{Cred, PushOptions, RemoteCallbacks, Repository};
use std::env;
use std::path::Path;
fn main() -> Result<(), git2::Error> {
let repo_path = env::args().nth(1).unwrap_or_else(|| ".".to_string());
let repo = Repository::open(Path::new(&repo_path))?;
println!("Opened repository: {:?}", repo.path());
let remote_name = env::args().nth(2).unwrap_or_else(|| "origin".to_string());
let mut remote = repo.find_remote(&remote_name)?;
println!("Remote '{}' URL: {:?}", remote_name, remote.url());
let mut callbacks = RemoteCallbacks::new();
callbacks.credentials(|url, username_from_url, allowed_types| {
println!("Auth requested for: {}", url);
println!("Username hint: {:?}", username_from_url);
println!("Allowed types: {:?}", allowed_types);
if allowed_types.contains(git2::CredentialType::SSH_KEY) {
let username = username_from_url.unwrap_or("git");
if let Ok(cred) = Cred::ssh_key_from_agent(username) {
println!("Using SSH agent");
return Ok(cred);
}
let home = env::var("HOME").unwrap_or_else(|_| ".".to_string());
let private_key = Path::new(&home).join(".ssh/id_ed25519");
let public_key = Path::new(&home).join(".ssh/id_ed25519.pub");
if private_key.exists() {
println!("Using SSH key: {:?}", private_key);
return Cred::ssh_key(username, Some(&public_key), &private_key, None);
}
let private_key = Path::new(&home).join(".ssh/id_rsa");
let public_key = Path::new(&home).join(".ssh/id_rsa.pub");
if private_key.exists() {
println!("Using SSH key: {:?}", private_key);
return Cred::ssh_key(username, Some(&public_key), &private_key, None);
}
}
if allowed_types.contains(git2::CredentialType::USER_PASS_PLAINTEXT) {
if let Ok(token) = env::var("GITHUB_TOKEN") {
println!("Using GITHUB_TOKEN");
return Cred::userpass_plaintext("x-access-token", &token);
}
if let Ok(token) = env::var("GH_TOKEN") {
println!("Using GH_TOKEN");
return Cred::userpass_plaintext("x-access-token", &token);
}
}
if allowed_types.contains(git2::CredentialType::DEFAULT) {
println!("Using default credentials");
return Cred::default();
}
if allowed_types.contains(git2::CredentialType::USER_PASS_PLAINTEXT) {
println!("Trying git credential helper...");
let config = git2::Config::open_default()?;
return Cred::credential_helper(&config, url, username_from_url);
}
Err(git2::Error::from_str("no authentication available"))
});
callbacks.push_update_reference(|refname, status| {
match status {
None => println!("Updated {}", refname),
Some(msg) => println!("Failed to update {}: {}", refname, msg),
}
Ok(())
});
callbacks.push_transfer_progress(|current, total, bytes| {
println!("Transfer: {}/{} objects, {} bytes", current, total, bytes);
});
let mut push_opts = PushOptions::new();
push_opts.remote_callbacks(callbacks);
let head = repo.head()?;
let branch_name = head
.shorthand()
.ok_or_else(|| git2::Error::from_str("HEAD is not a branch"))?;
let refspec = format!("refs/heads/{}:refs/heads/{}", branch_name, branch_name);
println!("Pushing: {}", refspec);
remote.push(&[&refspec], Some(&mut push_opts))?;
println!("Push complete!");
Ok(())
}