Skip to main content

git_policy_cli/
service_handoff.rs

1//! What:
2//!  The wrapper's side of after-commit work: leave the push and upkeep records for a commit the service landed, wake
3//!  the service, and show the notices that earlier work left.
4//! Why:
5//!  The commit returns as soon as it is saved. The push (which can take as long as the network does) and the upkeep run
6//!  in the service afterwards; a push that fails is reported at the person's next commit through the service
7//!  (`doc/planning/cli-git-resident-service.md`, "After-commit work"). The wrapper starts no program and waits for no
8//!  one: it writes whole files and sends one message.
9//!
10//! In TS you'd write (pseudocode):
11//! ```ts
12//! await enqueue(workDir, push); await enqueue(workDir, maintenance); await wake(commonDir);
13//! ```
14
15use super::iso_time::now_iso;
16use super::service_client::{ClientConfig, wake};
17use super::service_messages::ClientFailure;
18use super::service_work::{take_notices, work_directory};
19use super::standards_review_input::has_shadow_log;
20use super::work_item::{WorkItem, WorkKind, enqueue};
21use std::io::Write;
22use std::path::PathBuf;
23
24/// What the wrapper knows about a commit the service landed.
25#[derive(Clone, Debug)]
26pub struct ServiceHandoff {
27    /// How to reach the service.
28    pub config: ClientConfig,
29    /// The worktree root.
30    pub repository: PathBuf,
31    /// The common Git directory.
32    pub common_dir: PathBuf,
33    /// The branch the commit went to.
34    pub reference: String,
35    /// The branch value the commit was made on.
36    pub parent: String,
37}
38
39/// The kinds of work a landed commit leaves for the service.
40pub const HANDED_OVER: [WorkKind; 2] = [WorkKind::Push, WorkKind::Maintenance];
41
42/// What:
43///  Writes the work records for a landed commit and wakes the service.
44/// Why:
45///   The records are written before the message is sent, so a service that wakes always finds them; records that were
46///  written but not announced stay pending and are run by the next wake for the repository.
47///
48/// In TS you'd write (pseudocode):
49/// ```ts
50/// await handOver(handoff, landed)
51/// ```
52pub fn hand_over(handoff: &ServiceHandoff, landed: &str, notices: &mut dyn Write) -> Result<(), ClientFailure> {
53    let work_dir: PathBuf = work_directory(handoff.common_dir.as_path());
54    // A review is left only where the repository keeps a shadow log; the file is the repository's own switch.
55    let review: &[WorkKind] = if has_shadow_log(handoff.repository.as_path()) { &[WorkKind::Review] } else { &[] };
56    for kind in HANDED_OVER.iter().chain(review.iter()).copied() {
57        let item: WorkItem = WorkItem {
58            kind,
59            repository: handoff.repository.display().to_string(),
60            git_dir: handoff.common_dir.display().to_string(),
61            branch_ref: handoff.reference.clone(),
62            oid: landed.to_string(),
63            parent: handoff.parent.clone(),
64            created: now_iso(),
65            tries: 0,
66        };
67        enqueue(work_dir.as_path(), &item).map_err(ClientFailure::WorkNotHandedOver)?;
68    }
69    return wake(&handoff.config, handoff.common_dir.as_path(), notices);
70}
71
72/// Shows and removes the notices that earlier work left for this repository.
73pub fn show_notices(handoff: &ServiceHandoff, out: &mut dyn Write) {
74    for text in take_notices(handoff.common_dir.as_path()) {
75        // A closed terminal is not a reason to fail a commit that already landed.
76        let _written = writeln!(out, "{text}");
77    }
78}
79
80/// Handoff controls stay out of the release executable.
81#[cfg(test)]
82#[path = "service_handoff_tests.rs"]
83mod tests;