use std::path::Path;
use git_lfs_api::{ApiError, Client as ApiClient, Lock, Ref, VerifyLocksRequest};
use git_lfs_git::ConfigScope;
use crate::push::PushCommandError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Setting {
Enabled,
Disabled,
Unset,
}
pub enum Outcome {
Skipped,
Verified { ours: Vec<Lock>, theirs: Vec<Lock> },
Aborted,
}
pub fn run(
runtime: &tokio::runtime::Runtime,
api: &ApiClient,
cwd: &Path,
remote_label: &str,
endpoint: &str,
refspec: Option<&Ref>,
) -> Result<Outcome, PushCommandError> {
let setting = read_setting(cwd, endpoint)?;
if matches!(setting, Setting::Disabled) {
return Ok(Outcome::Skipped);
}
let mut req = VerifyLocksRequest::default();
if let Some(r) = refspec {
req.r#ref = Some(r.clone());
}
match runtime.block_on(api.verify_locks(&req)) {
Ok(resp) => {
if matches!(setting, Setting::Unset) {
eprintln!(
"Locking support detected on remote \"{remote_label}\". \
Consider enabling it with: "
);
eprintln!(" $ git config lfs.{endpoint}.locksverify true");
}
Ok(Outcome::Verified {
ours: resp.ours,
theirs: resp.theirs,
})
}
Err(ApiError::Status { status: 501, .. }) => {
let key = format!("lfs.{endpoint}.locksverify");
let _ = git_lfs_git::config::set(cwd, ConfigScope::Local, &key, "false");
Ok(Outcome::Skipped)
}
Err(ApiError::Status { status, .. }) if (500..600).contains(&status) => {
if matches!(setting, Setting::Enabled) {
eprintln!(
"\"{remote_label}\" does not support the Git LFS locking API. \
Consider disabling it with:"
);
eprintln!(" $ git config lfs.{endpoint}.locksverify false");
Ok(Outcome::Aborted)
} else {
eprintln!("\"{remote_label}\" does not support the Git LFS locking API.");
Ok(Outcome::Skipped)
}
}
Err(ApiError::Status { status: 403, .. }) => {
if matches!(setting, Setting::Enabled) {
eprintln!("error: Authentication error: lock verification failed");
Ok(Outcome::Aborted)
} else {
eprintln!("warning: Authentication error: lock verification failed");
Ok(Outcome::Skipped)
}
}
Err(ApiError::Status { status: 404, .. }) => {
Ok(Outcome::Skipped)
}
Err(e) => {
eprintln!("warning: lock verify failed: {e}");
Ok(Outcome::Skipped)
}
}
}
fn read_setting(cwd: &Path, endpoint: &str) -> Result<Setting, PushCommandError> {
let endpoint_key = format!("lfs.{endpoint}.locksverify");
if let Some(v) = git_lfs_git::config::get_effective(cwd, &endpoint_key)? {
return Ok(parse_bool(&v));
}
if let Some(v) = git_lfs_git::config::get_effective(cwd, "lfs.locksverify")? {
return Ok(parse_bool(&v));
}
Ok(Setting::Unset)
}
fn parse_bool(s: &str) -> Setting {
match s.to_ascii_lowercase().as_str() {
"true" | "1" | "yes" | "on" => Setting::Enabled,
"false" | "0" | "no" | "off" | "" => Setting::Disabled,
_ => Setting::Unset,
}
}