Skip to main content

git_policy_cli/
lfs_push_precheck.rs

1//! What:
2//!  The check, before anything starts, that the Git LFS upload a push is about to run can start.
3//! Why:
4//!  A push of a repository with LFS keys is preceded by `git-lfs push`, which the parent starts.
5//!  When no directory of `PATH` holds `git-lfs`, one stat per `PATH` directory, with no process started, finds that
6//!  first, and the parent says so with the full explanation. A push that bypasses the upload (`--no-verify`,
7//!  `GIT_LFS_SKIP_PUSH`, or the switch `-c hook.cli-git-lfs-pre-push.enabled=false`) needs no program, so it is never
8//!  refused here.
9//!
10//! In TS you'd write (pseudocode):
11//! ```ts
12//! const refusal = hookIsActive(prefix, region) && !programOnPath(env.PATH) ? missingProgramMessage() : undefined;
13//! ```
14
15/// The invocation's environment values.
16use super::child_environment::environment_value;
17/// The table-driven read of a push's options.
18use super::command_push::parse_push_region;
19/// The hook switch, the lookup and the refusal text.
20use super::lfs_pre_push::{hook_switched_off, missing_program_message, program_on_path, skip_requested};
21/// Owned OS strings for Git arguments.
22use std::ffi::OsString;
23
24/// What:
25///  The refusal text when the upload hook is active and `git-lfs` is not on the invocation's `PATH`.
26/// Why:
27///   A region Git itself would refuse is left to Git, which says why.
28///
29/// In TS you'd write (pseudocode):
30/// ```ts
31/// function lfsRefusal(globalPrefix, region, environment): string | undefined;
32/// ```
33pub fn lfs_refusal(
34    global_prefix: &[OsString],
35    region: &[OsString],
36    environment: &[(OsString, OsString)],
37) -> Option<String> {
38    if hook_switched_off(global_prefix, environment) || skip_requested(environment) {
39        return None;
40    }
41    let parsed = parse_push_region(region, &[]).ok()?;
42    if parsed.no_verify || parsed.dry_run {
43        return None;
44    }
45    let path: OsString = environment_value(environment, "PATH").unwrap_or_default();
46    if program_on_path(path.as_os_str()) {
47        return None;
48    }
49    return Some(missing_program_message());
50}
51
52/// The refusal is controlled in the test module.
53#[cfg(test)]
54#[path = "lfs_push_precheck_tests.rs"]
55mod tests;