Skip to main content

inspect_git/
inspect_git.rs

1//! Bounded Git observations with explicit tool admission and status scope.
2
3#[cfg(unix)]
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    use ic_host_tools::{
6        provenance::{IgnoreSubmodules, StatusOptions, UntrackedFiles, capture_git},
7        tool::{AdmittedTool, ExecutionContext, OutputLimits, ToolSpec},
8    };
9    use std::{ffi::OsString, io, path::PathBuf, time::Duration};
10
11    let invalid = |message| io::Error::new(io::ErrorKind::InvalidInput, message);
12    let mut args = std::env::args_os().skip(1);
13    let mut required = || {
14        args.next().ok_or_else(|| invalid(
15        "usage: inspect_git TOOL SHA256 VERSION WORKDIR MAX_TOOL_BYTES MAX_STDOUT_BYTES MAX_STDERR_BYTES TIMEOUT_MS UNTRACKED SUBMODULES",
16    ))
17    };
18    let executable = PathBuf::from(required()?);
19    let digest = required()?
20        .into_string()
21        .map_err(|_| invalid("digest must be UTF-8"))?
22        .parse()?;
23    let version = required()?
24        .into_string()
25        .map_err(|_| invalid("version must be UTF-8"))?;
26    let directory = PathBuf::from(required()?);
27    let mut number = || -> Result<u64, Box<dyn std::error::Error>> {
28        Ok(required()?
29            .to_str()
30            .ok_or_else(|| invalid("limits must be UTF-8"))?
31            .parse()?)
32    };
33    let executable_bytes = number()?;
34    let limits = OutputLimits {
35        stdout_bytes: usize::try_from(number()?)?,
36        stderr_bytes: usize::try_from(number()?)?,
37        timeout: Duration::from_millis(number()?),
38    };
39    let untracked = match required()?.to_str() {
40        Some("no") => UntrackedFiles::No,
41        Some("normal") => UntrackedFiles::Normal,
42        Some("all") => UntrackedFiles::All,
43        _ => return Err(invalid("untracked must be no, normal or all").into()),
44    };
45    let ignore_submodules = match required()?.to_str() {
46        Some("none") => IgnoreSubmodules::None,
47        Some("untracked") => IgnoreSubmodules::Untracked,
48        Some("dirty") => IgnoreSubmodules::Dirty,
49        Some("all") => IgnoreSubmodules::All,
50        _ => return Err(invalid("submodules must be none, untracked, dirty or all").into()),
51    };
52    if args.next().is_some() {
53        return Err(invalid("unexpected argument").into());
54    }
55    // The example explicitly selects an empty environment; the library never
56    // chooses environment exclusions or tool/version pins for a consumer.
57    let context = ExecutionContext {
58        current_dir: &directory,
59        environment: &[],
60    };
61    let git = AdmittedTool::admit(
62        &ToolSpec {
63            executable: &executable,
64            sha256: digest,
65            executable_bytes,
66            version_arguments: &[OsString::from("--version")],
67            version_identity: &version,
68        },
69        &context,
70        limits,
71    )?;
72    let observed = capture_git(
73        &git,
74        &context,
75        StatusOptions {
76            untracked,
77            ignore_submodules,
78        },
79        limits,
80    )?;
81    println!("revision={}", observed.revision);
82    println!("tree={}", observed.tree);
83    println!("dirty={}", observed.is_dirty());
84    println!("status_bytes={}", observed.status_identity.bytes);
85    println!("status_sha256={}", observed.status_identity.sha256);
86    Ok(())
87}
88
89#[cfg(not(unix))]
90fn main() -> Result<(), std::io::Error> {
91    Err(std::io::Error::new(
92        std::io::ErrorKind::Unsupported,
93        "Git observations require a supported Unix host",
94    ))
95}