115 lines
3.1 KiB
Rust
115 lines
3.1 KiB
Rust
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
use anyhow::{Context, Result, bail};
|
|
|
|
pub fn current_branch(workdir: &Path) -> Result<Option<String>> {
|
|
let out = match Command::new("git")
|
|
.current_dir(workdir)
|
|
.args(["rev-parse", "--abbrev-ref", "HEAD"])
|
|
.output()
|
|
{
|
|
Ok(out) => out,
|
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
|
bail!("`git` not found in PATH — install git to use the 'branch' check")
|
|
}
|
|
Err(err) => return Err(err).context("failed to spawn `git`"),
|
|
};
|
|
|
|
if !out.status.success() {
|
|
let stderr = String::from_utf8_lossy(&out.stderr);
|
|
let fail = stderr.trim();
|
|
if fail.contains("not a git repository")
|
|
|| fail.contains("not inside any git repository")
|
|
|| fail.contains("does not appear to be a git repository")
|
|
{
|
|
return Ok(None);
|
|
}
|
|
bail!("`git rev-parse` failed: {}", fail);
|
|
}
|
|
|
|
let name = String::from_utf8(out.stdout)
|
|
.context("git returned non-UTF-8 output")?
|
|
.trim()
|
|
.to_string();
|
|
if name.is_empty() {
|
|
return Ok(None);
|
|
}
|
|
Ok(Some(name))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
|
|
fn tmpdir(tag: &str) -> PathBuf {
|
|
let dir =
|
|
std::env::temp_dir().join(format!("xboct-deploy-git-{}-{tag}", std::process::id()));
|
|
let _ = fs::remove_dir_all(&dir);
|
|
fs::create_dir_all(&dir).unwrap();
|
|
dir
|
|
}
|
|
|
|
fn git(dir: &Path, args: &[&str]) {
|
|
let status = Command::new("git")
|
|
.current_dir(dir)
|
|
.args(args)
|
|
.status()
|
|
.unwrap();
|
|
assert!(status.success(), "git {args:?} failed in {}", dir.display());
|
|
}
|
|
|
|
fn git_repo(tag: &str) -> PathBuf {
|
|
let dir = tmpdir(tag);
|
|
git(&dir, &["init", "-b", "test"]);
|
|
fs::write(dir.join("f.txt"), "hi").unwrap();
|
|
git(&dir, &["add", "."]);
|
|
git(
|
|
&dir,
|
|
&[
|
|
"-c",
|
|
"user.email=t@t",
|
|
"-c",
|
|
"user.name=t",
|
|
"commit",
|
|
"-m",
|
|
"init",
|
|
],
|
|
);
|
|
dir
|
|
}
|
|
|
|
#[test]
|
|
fn returns_branch_in_repo() {
|
|
let dir = git_repo("branch");
|
|
assert_eq!(current_branch(&dir).unwrap().as_deref(), Some("test"));
|
|
let _ = fs::remove_dir_all(dir);
|
|
}
|
|
|
|
#[test]
|
|
fn returns_head_when_detached() {
|
|
let dir = git_repo("detached");
|
|
git(&dir, &["switch", "--detach"]);
|
|
assert_eq!(current_branch(&dir).unwrap().as_deref(), Some("HEAD"));
|
|
let _ = fs::remove_dir_all(dir);
|
|
}
|
|
|
|
#[test]
|
|
fn returns_none_outside_repo() {
|
|
let dir = tmpdir("norepo");
|
|
assert_eq!(current_branch(&dir).unwrap(), None);
|
|
let _ = fs::remove_dir_all(dir);
|
|
}
|
|
|
|
#[test]
|
|
fn works_from_subdirectory() {
|
|
let dir = git_repo("subdir");
|
|
let sub = dir.join("inner");
|
|
fs::create_dir_all(&sub).unwrap();
|
|
assert_eq!(current_branch(&sub).unwrap().as_deref(), Some("test"));
|
|
let _ = fs::remove_dir_all(dir);
|
|
}
|
|
}
|