use anyhow::{Context, Result};
use git2::{Config, IndexAddOption, Repository, Signature, Tree};
pub struct Git {
email: String,
name: String,
branch: String,
repo: Repository,
}
impl Git {
pub fn from_env(branch: &str) -> Result<Self> {
let email = std::env::var("CARGO_TAG_EMAIL").context("CARGO_TAG_EMAIL not set")?;
let name = std::env::var("CARGO_TAG_NAME").context("CARGO_TAG_NAME not set")?;
Git::open(branch, &email, &name)
}
pub fn from_git_config(branch: &str) -> Result<Self> {
let cfg = Config::open_default().context("Cannot open git config")?;
let email = cfg
.get_entry("user.email")
.context("user.email not found")?;
let email = email.value().context("user.email not utf8")?;
let name = cfg.get_entry("user.name").context("user.name not found")?;
let name = name.value().context("user.name not utf8")?;
Git::open(branch, email, name).context("Failed to open Git repository")
}
pub fn open(branch: &str, email: &str, name: &str) -> Result<Self> {
let repo = Repository::open_from_env()?;
Ok(Self {
email: email.into(),
name: name.into(),
branch: branch.into(),
repo,
})
}
pub fn commit(&self, message: &str) -> Result<()> {
let signature = self.signature()?;
let head = self.repo.head()?.peel_to_commit()?;
let tree = self.tagging_tree()?;
self.repo.commit(
Some(&format!("refs/heads/{}", &self.branch)),
&signature,
&signature,
message,
&tree,
&[&head],
)?;
Ok(())
}
pub fn tag(&self, version_str: &str, message: &str) -> Result<()> {
let tagger = self.signature()?;
let head = self.repo.head()?.peel_to_commit()?;
let obj = head.as_object();
self.repo.tag(version_str, obj, &tagger, message, false)?;
Ok(())
}
fn signature(&self) -> Result<Signature<'_>> {
let signature = Signature::now(&self.name, &self.email)?;
Ok(signature)
}
fn tagging_tree(&self) -> Result<Tree<'_>> {
let mut index = self.repo.index()?;
index.add_all(["*"].iter(), IndexAddOption::DEFAULT, None)?;
index.write()?;
let tree_id = index.write_tree()?;
let tree = self.repo.find_tree(tree_id)?;
Ok(tree)
}
}