use anyhow::Result;
use super::git_output;
use super::semver::{SemVer, parse_semver_tag};
use super::status::{is_git_dirty, is_git_repo};
use super::tags::get_first_commit;
use crate::redact::redact_url_credentials;
#[derive(Debug, Clone)]
pub struct GitInfo {
pub tag: String,
pub commit: String,
pub short_commit: String,
pub branch: String,
pub dirty: bool,
pub semver: SemVer,
pub commit_date: String,
pub commit_timestamp: String,
pub previous_tag: Option<String>,
pub remote_url: String,
pub summary: String,
pub tag_subject: String,
pub tag_contents: String,
pub tag_body: String,
pub first_commit: Option<String>,
}
pub fn detect_git_info(tag: &str, skip_validate: bool) -> Result<GitInfo> {
if !is_git_repo() {
return Ok(GitInfo {
tag: tag.to_string(),
commit: String::new(),
short_commit: String::new(),
branch: String::new(),
dirty: false,
semver: SemVer {
major: 0,
minor: 0,
patch: 0,
prerelease: None,
build_metadata: None,
},
commit_date: String::new(),
commit_timestamp: String::new(),
previous_tag: None,
remote_url: String::new(),
summary: String::new(),
tag_subject: String::new(),
tag_contents: String::new(),
tag_body: String::new(),
first_commit: None,
});
}
let commit = git_output(&["rev-parse", "HEAD"])?;
let short_commit = git_output(&["rev-parse", "--short", "HEAD"])?;
let branch = git_output(&["rev-parse", "--abbrev-ref", "HEAD"]).unwrap_or_default();
let dirty = is_git_dirty();
let commit_date = git_output(&["-c", "log.showSignature=false", "log", "-1", "--format=%cI"])
.unwrap_or_default();
let commit_timestamp =
git_output(&["-c", "log.showSignature=false", "log", "-1", "--format=%at"])
.unwrap_or_default();
let remote_url_raw = match git_output(&["ls-remote", "--get-url"]) {
Ok(url) => url,
Err(e) => {
tracing::warn!(
error = %e,
"git ls-remote --get-url failed; remote_url left empty"
);
String::new()
}
};
let remote_url = redact_url_credentials(&remote_url_raw);
let summary = git_output(&[
"-c",
"log.showSignature=false",
"describe",
"--tags",
"--always",
"--dirty",
])
.unwrap_or_default();
let tag_subject = git_output(&["tag", "-l", "--format=%(contents:subject)", tag])
.ok()
.filter(|s| !s.is_empty())
.unwrap_or_else(|| {
git_output(&["-c", "log.showSignature=false", "log", "-1", "--format=%s"])
.unwrap_or_default()
});
let tag_contents = git_output(&["tag", "-l", "--format=%(contents)", tag])
.ok()
.filter(|s| !s.is_empty())
.unwrap_or_else(|| {
git_output(&["-c", "log.showSignature=false", "log", "-1", "--format=%B"])
.unwrap_or_default()
});
let tag_body = git_output(&["tag", "-l", "--format=%(contents:body)", tag])
.ok()
.filter(|s| !s.is_empty())
.unwrap_or_else(|| {
git_output(&["-c", "log.showSignature=false", "log", "-1", "--format=%b"])
.unwrap_or_default()
});
let semver = match parse_semver_tag(tag) {
Ok(sv) => sv,
Err(e) => {
if skip_validate {
tracing::warn!("current tag is not semver, skipping validation");
SemVer {
major: 0,
minor: 0,
patch: 0,
prerelease: None,
build_metadata: None,
}
} else {
return Err(e);
}
}
};
let first_commit = get_first_commit().ok();
Ok(GitInfo {
tag: tag.to_string(),
commit,
short_commit,
branch,
dirty,
semver,
commit_date,
commit_timestamp,
previous_tag: None,
remote_url,
summary,
tag_subject,
tag_contents,
tag_body,
first_commit,
})
}