use std::path::{Path, PathBuf};
use strop_core::worker::CancelToken;
use strop_workspace::ContainerId;
use crate::exec::GitExec;
use crate::remote::{context_with, discover_with, RemoteGitError};
use crate::target::RepoTarget;
use crate::GitContext;
pub fn discover(
id: &ContainerId,
from: &Path,
cancel: &CancelToken,
) -> Result<Option<PathBuf>, RemoteGitError> {
let exec = GitExec::Container {
container: id.clone(),
workdir: from,
};
discover_with(&exec, cancel)
}
pub fn context(
id: &ContainerId,
workdir: &Path,
cancel: &CancelToken,
) -> Result<GitContext, RemoteGitError> {
let exec = GitExec::Container {
container: id.clone(),
workdir,
};
let repo = RepoTarget::Container {
container: id.clone(),
workdir: workdir.to_path_buf(),
};
context_with(&exec, repo, cancel)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::exec::GitRun;
use crate::remote::{context_from_runs, discover_from_run};
fn run(code: i32, stdout: &[u8], stderr: &[u8]) -> GitRun {
GitRun {
success: code == 0,
code: Some(code),
stdout: stdout.to_vec(),
stderr: stderr.to_vec(),
stdout_dropped: 0,
stderr_dropped: 0,
}
}
fn id() -> ContainerId {
ContainerId::canonical("a".repeat(64)).expect("64 hex is a canonical id")
}
#[test]
fn no_repo_is_none_and_other_failures_are_typed() {
let absent = run(
128,
b"",
b"fatal: not a git repository (or any of the parent directories): .git\n",
);
assert_eq!(discover_from_run(&absent), Ok(None));
let present = run(0, b"/work/app\n", b"");
assert_eq!(
discover_from_run(&present).unwrap(),
Some(PathBuf::from("/work/app"))
);
match discover_from_run(&run(128, b"", b"fatal: unsafe repository\n")) {
Err(RemoteGitError::Exit { op, code, stderr }) => {
assert_eq!(op, "rev-parse --show-toplevel");
assert_eq!(code, 128);
assert_eq!(stderr, "fatal: unsafe repository");
}
other => panic!("expected Exit, got {other:?}"),
}
}
#[test]
fn context_reports_container_target_and_head_state() {
let repo = RepoTarget::Container {
container: id(),
workdir: PathBuf::from("/work/app"),
};
let head = run(0, b"c59d8ceb7aeb96a1cdccff5646ec485acce32d45\n", b"");
let branch = run(128, b"", b"fatal: ref HEAD is not a symbolic ref\n");
let config = run(0, b"remote.origin.url\ngit@gh:acme/demo.git\0", b"");
let context = context_from_runs(repo.clone(), &head, &branch, &config).unwrap();
assert_eq!(context.repo, repo);
assert_eq!(
context.head_sha.as_deref(),
Some("c59d8ceb7aeb96a1cdccff5646ec485acce32d45")
);
assert_eq!(context.head_branch, None, "detached HEAD");
assert_eq!(
context.remotes,
vec![("origin".to_string(), "git@gh:acme/demo.git".to_string())]
);
}
#[test]
fn unborn_head_and_no_remotes_are_honest_data() {
let repo = RepoTarget::Container {
container: id(),
workdir: PathBuf::from("/work/app"),
};
let head = run(1, b"", b"");
let branch = run(0, b"main\n", b"");
let config = run(1, b"", b"");
let context = context_from_runs(repo, &head, &branch, &config).unwrap();
assert_eq!(context.head_sha, None, "unborn HEAD");
assert_eq!(context.head_branch.as_deref(), Some("main"));
assert!(context.remotes.is_empty());
}
}