Skip to main content

ci_engine/
model.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Public executor inputs and outputs.
3
4use std::path::Path;
5
6use ci_config::Trigger;
7use crypto::{Basis, CiVerdictBody, Conclusion, StateRef};
8use serde::{Deserialize, Serialize};
9
10use crate::{
11    HermeticEnv, ProcGroupRegistry, ServiceProvider,
12    result_cache::{ResultCache, SpotCheck},
13};
14
15/// Facts about the exact state/tree being evaluated.
16#[derive(Debug, Clone)]
17pub struct ExecutionContext {
18    /// Repository identifier.
19    pub repo: String,
20    /// State reference carried in the verdict body.
21    pub state: StateRef,
22    /// Exact evaluated tree and basis.
23    pub basis: Basis,
24    /// Typed-blob digest of the raw definition.
25    pub definition_digest: String,
26    /// Optional toolchain string.
27    pub toolchain: Option<String>,
28    /// Hosted pick id; absent locally.
29    pub pick_id: Option<String>,
30    /// One-based run attempt.
31    pub attempt: u32,
32    /// Hosted runner identity; absent locally.
33    pub runner: Option<String>,
34    /// Immutable image digest, when used.
35    pub image_digest: Option<String>,
36}
37
38/// Stable options for an executor invocation.
39pub struct RunOptions<'a> {
40    /// Directory in which argv executes.
41    pub workdir: &'a Path,
42    /// Service lifecycle provider.
43    pub services: &'a dyn ServiceProvider,
44    /// Injected RFC3339 clock.
45    pub now_rfc3339: &'a dyn Fn() -> String,
46}
47
48/// Additive execution controls.
49#[derive(Default)]
50pub struct RunControls<'a> {
51    /// Optional trigger filter.
52    pub trigger: Option<Trigger>,
53    /// Cache root outside the evaluated source tree.
54    pub cache_root: Option<&'a Path>,
55    /// Optional injected hermetic environment.
56    pub hermetic_env: Option<&'a HermeticEnv>,
57    /// Optional group registry for runner drain.
58    pub proc_groups: Option<ProcGroupRegistry>,
59    /// Optional content-addressed result cache.
60    pub result_cache: Option<&'a dyn ResultCache>,
61    /// Spot-check policy applied to cache hits. Ignored when no cache is set.
62    pub spot_check: SpotCheck,
63}
64
65/// Operational record for one flake-retry attempt.
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
67pub struct AttemptRecord {
68    /// One-based attempt index.
69    pub attempt: u32,
70    /// Attempt conclusion.
71    pub conclusion: Conclusion,
72    /// Wall time in milliseconds.
73    pub duration_ms: u64,
74    /// Whether output matched a flake signature.
75    pub flake_matched: bool,
76}
77
78/// One check's verdict body and operational sidecars.
79#[derive(Debug, Clone)]
80pub struct CheckResult {
81    /// Signable verdict-v2 body.
82    pub body: CiVerdictBody,
83    /// ANSI-stripped combined output.
84    pub combined_output: String,
85    /// Attempts performed.
86    pub attempts: u32,
87    /// Attempt records, not part of the signed body.
88    pub attempt_records: Vec<AttemptRecord>,
89}
90
91impl CheckResult {
92    /// Terminal conclusion.
93    #[must_use]
94    pub fn conclusion(&self) -> Conclusion {
95        self.body.outcome.conclusion
96    }
97
98    /// Whether a successful result needed a flake retry.
99    #[must_use]
100    pub fn recovered_after_flake(&self) -> bool {
101        self.conclusion() == Conclusion::Success
102            && self
103                .attempt_records
104                .iter()
105                .any(|attempt| attempt.flake_matched)
106    }
107}