use std::{env, error::Error, path::Path}; use git2::{BranchType, Cred, IndexAddOption, PushOptions, RemoteCallbacks, Repository, Signature}; use log::info; use semver::Version; use crate::{GITHUB_OWNER, GITHUB_REPO, UPSTREAM_GIT_URL}; pub fn git_checkout>( path: P, url: &str, r#ref: &str, ) -> Result> { info!( "Cloning \"{}\" ref \"{}\" into \"{}\".", UPSTREAM_GIT_URL, r#ref, path.as_ref().display() ); let repository = Repository::clone(url, path)?; { let (object, reference) = repository.revparse_ext(r#ref)?; repository.checkout_tree(&object, None)?; match reference { Some(reference) => { repository.set_head(reference.name().expect("Reference name should exist."))? } None => repository.set_head_detached(object.id())?, } } Ok(repository) } pub fn git_version_tags(repository: &Repository) -> Result, Box> { let tag_names = repository.tag_names(None)?; let mut tag_names = tag_names .into_iter() .flat_map(|tag_name| { tag_name.and_then(|tag_name| Version::parse(tag_name.trim_start_matches("v")).ok()) }) .collect::>(); tag_names.sort(); Ok(tag_names) } pub fn git_has_remote_branch(repository: &Repository, branch_name: &str) -> bool { repository .find_branch(&format!("origin/{branch_name}"), BranchType::Remote) .is_ok() } pub fn git_create_branch(repository: &Repository, branch_name: &str) -> Result<(), Box> { info!("Creating branch {branch_name}."); let branch = repository.branch(branch_name, &repository.head()?.peel_to_commit()?, false)?; repository.set_head( branch .into_reference() .name() .expect("Reference name should exist."), )?; Ok(()) } pub fn git_commit(repository: &Repository, message: &str) -> Result<(), Box> { info!("Committing."); repository .index()? .add_all(["."], IndexAddOption::DEFAULT, None)?; repository.index()?.write()?; let signature = Signature::now(&env::var("GIT_USER_NAME")?, &env::var("GIT_USER_EMAIL")?)?; let tree = repository.find_tree(repository.index()?.write_tree()?)?; let parent = repository.head()?.peel_to_commit()?; repository.commit( Some("HEAD"), &signature, &signature, message, &tree, &[&parent], )?; Ok(()) } pub fn git_push(repository: &Repository, branch_name: &str) -> Result<(), Box> { info!("Pushing branch {branch_name}."); let branch = repository.find_branch(branch_name, BranchType::Local)?; let branch_reference = branch.into_reference(); let branch_ref = branch_reference .name() .expect("Reference name should exist."); let username = env::var("GITHUB_ACTOR")?; let password = env::var("GITHUB_TOKEN")?; let mut callbacks = RemoteCallbacks::new(); callbacks.credentials(|_, _, _| Cred::userpass_plaintext(&username, &password)); let mut options = PushOptions::new(); options.remote_callbacks(callbacks); let mut remote = repository.find_remote("origin")?; remote.push(&[branch_ref], Some(&mut options))?; Ok(()) } pub async fn github_create_pull_request( branch_name: &str, title: &str, body: &str, ) -> Result<(), Box> { let octocrab = octocrab::instance(); info!("Creating pull request for branch {branch_name}."); octocrab .pulls(GITHUB_OWNER, GITHUB_REPO) .create(title, branch_name, "main") .body(body) .send() .await?; Ok(()) }