Skip to main content

git_policy_cli/
commit_lifecycle.rs

1//! What:
2//!  The call order around a guarded Git child:
3//!       install the signal guard, run the child, release the locks and the transaction, drop the guard,
4//!       then exit with the child's code.
5//! Why:
6//!  The incumbent releases the transaction before the signal guard goes away, so a signal sent during
7//!  release is still relayed to the child.
8//!  The order after a landed commit (automatic maintenance, then the post-commit hook) is not here:
9//!  the incumbent runs maintenance without stopping on failure (`src/policy-engine/commit-transaction-conclusion.ts`),
10//!  and the post-landing lane owns that sequence.
11//!  Nothing calls this yet: the command path keeps refusing `git commit` until landing is merged.
12//!
13//! In TS you'd write (pseudocode):
14//! ```ts
15//! const guard = installSignalGuard(); const outcome = await run(child); await release(); guard.drop(); exit(exitCode(outcome));
16//! ```
17
18use super::forwarding::exit_code;
19use super::signal_relay::SignalGuard;
20use std::process::Command;
21
22/// What:
23///  The steps that release what a guarded child held, run after the child ends and before the guard drops.
24/// Why:
25///   Release runs while the guard still relays signals, so a signal sent during release reaches the child.
26///
27/// In TS you'd write (pseudocode):
28/// ```ts
29/// interface ReleaseSteps { release(): Promise<void>; }
30/// ```
31pub trait ReleaseSteps {
32    /// Releases the landing lock and removes the transaction.
33    fn release(&mut self) -> Result<(), String>;
34}
35
36/// What:
37///  Run a Git child under the signal guard, release what it held, and report its exit code.
38/// Why:
39///   Release runs even when the child fails to run, and each failure is reported, not hidden.
40///
41/// In TS you'd write (pseudocode):
42/// ```ts
43/// const guard = installSignalGuard(); const outcome = await run(child); await release(); guard.drop(); exit(exitCode(outcome));
44/// ```
45pub fn run_guarded_child(command: &mut Command, release: &mut dyn ReleaseSteps) -> Result<i32, String> {
46    let guard: SignalGuard = match SignalGuard::install() {
47        Ok(installed) => installed,
48        Err(error) => return Err(format!("installing the signal guard failed: {error}")),
49    };
50    let outcome = guard.run_child(command);
51    let released: Result<(), String> = release.release();
52    drop(guard);
53    let child = match outcome {
54        Ok(found) => found,
55        Err(error) => return Err(format!("running the child failed: {error}")),
56    };
57    if let Err(error) = released {
58        return Err(format!("releasing after the child failed: {error}"));
59    }
60    return Ok(exit_code(child));
61}
62
63#[cfg(test)]
64#[path = "commit_lifecycle_tests.rs"]
65mod tests;