Skip to main content

greentic_deploy_spec/
environment_runtime.rs

1//! `greentic.environment-runtime.v1` (`§5.1a`).
2//!
3//! Sibling file `runtime.json`. Written by the deployer env-pack after each
4//! apply; consumed by the runtime for `runtime://` lookups.
5
6use crate::capability_slot::PackDescriptor;
7use crate::version::SchemaVersion;
8use chrono::{DateTime, Utc};
9use greentic_types::EnvId;
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use std::collections::BTreeMap;
13
14/// A discovered runtime value — usually a string, occasionally a nested map
15/// (e.g. `generated_secret_arns: { bot_token: ... }`). Stored as `serde_json::Value`
16/// to preserve nesting without forcing a typed schema on every deployer.
17pub type RuntimeDiscoveryValue = Value;
18
19#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
20pub struct EnvironmentRuntime {
21    pub schema: SchemaVersion,
22    pub environment_id: EnvId,
23    #[serde(default)]
24    pub discovered: BTreeMap<String, RuntimeDiscoveryValue>,
25    pub generated_at: DateTime<Utc>,
26    pub generated_by: PackDescriptor,
27    /// Bumped each apply.
28    pub generation: u64,
29}
30
31impl EnvironmentRuntime {
32    pub fn schema_str() -> &'static str {
33        SchemaVersion::ENVIRONMENT_RUNTIME_V1
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40    use std::str::FromStr;
41
42    fn sample() -> EnvironmentRuntime {
43        let mut discovered = BTreeMap::new();
44        discovered.insert(
45            "alb_dns".into(),
46            serde_json::Value::String("a.example.com".into()),
47        );
48        EnvironmentRuntime {
49            schema: SchemaVersion::new(SchemaVersion::ENVIRONMENT_RUNTIME_V1),
50            environment_id: greentic_types::EnvId::from_str("local").unwrap(),
51            discovered,
52            generated_at: Utc::now(),
53            generated_by: PackDescriptor::try_new("greentic.deployer.local-process@1.0.0").unwrap(),
54            generation: 1,
55        }
56    }
57
58    #[test]
59    fn schema_str_matches_constant() {
60        assert_eq!(
61            EnvironmentRuntime::schema_str(),
62            SchemaVersion::ENVIRONMENT_RUNTIME_V1
63        );
64    }
65
66    #[test]
67    fn json_round_trip() {
68        let original = sample();
69        let json = serde_json::to_string(&original).unwrap();
70        let back: EnvironmentRuntime = serde_json::from_str(&json).unwrap();
71        assert_eq!(original, back);
72    }
73
74    #[test]
75    fn discovered_defaults_to_empty() {
76        let json = serde_json::json!({
77            "schema": SchemaVersion::ENVIRONMENT_RUNTIME_V1,
78            "environment_id": "local",
79            "generated_at": "2026-01-01T00:00:00Z",
80            "generated_by": "greentic.deployer.local-process@1.0.0",
81            "generation": 1
82        });
83        let rt: EnvironmentRuntime = serde_json::from_value(json).unwrap();
84        assert!(rt.discovered.is_empty());
85    }
86}