Skip to main content

greentic_runner/
lib.rs

1#![forbid(unsafe_code)]
2//! Canonical entrypoint for embedding the Greentic runner.
3//!
4//! This crate provides two supported integration paths:
5//! - [`run_http_host`] mirrors the CLI and starts the HTTP server that exposes
6//!   ingress adapters, admin endpoints, and the pack watcher.
7//! - [`start_embedded_host`] constructs a [`RunnerHost`] without spinning up the
8//!   HTTP server so callers can drive `handle_activity` manually (desktop/dev
9//!   harnesses, tests, etc.).
10
11use anyhow::Result;
12
13pub use greentic_runner_host::{
14    self as host, Activity, ActivityKind, HostBuilder, HostServer, RunnerConfig, RunnerHost,
15    TenantHandle, config, http, pack, routing, runner, runtime, runtime_wasmtime, telemetry,
16    verify, watcher,
17};
18
19pub mod desktop {
20    pub use greentic_runner_desktop::*;
21}
22
23pub mod gen_bindings;
24pub mod info;
25
26/// Launch the canonical HTTP host. This is equivalent to running the
27/// `greentic-runner` binary with the provided [`RunnerConfig`].
28pub async fn run_http_host(cfg: RunnerConfig) -> Result<()> {
29    greentic_runner_host::run(cfg).await
30}
31
32/// Build and start a [`RunnerHost`] without wiring the HTTP ingress server.
33/// Callers are responsible for loading packs via [`RunnerHost::load_pack`] and
34/// invoking [`RunnerHost::handle_activity`] directly.
35pub async fn start_embedded_host(builder: HostBuilder) -> Result<RunnerHost> {
36    let host = builder.build()?;
37    host.start().await?;
38    Ok(host)
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use greentic_runner_host::config::{
45        FlowRetryConfig, HostConfig, OperatorPolicy, RateLimits, SecretsPolicy, StateStorePolicy,
46        WebhookPolicy,
47    };
48    use greentic_runner_host::trace::TraceConfig;
49    use greentic_runner_host::validate::ValidationConfig;
50    use std::collections::HashMap;
51    use std::path::PathBuf;
52
53    fn host_config() -> HostConfig {
54        HostConfig {
55            tenant: "embedded".into(),
56            bindings_path: PathBuf::from("<test>"),
57            flow_type_bindings: HashMap::new(),
58            rate_limits: RateLimits::default(),
59            retry: FlowRetryConfig::default(),
60            http_enabled: false,
61            secrets_policy: SecretsPolicy::allow_all(),
62            state_store_policy: StateStorePolicy::default(),
63            webhook_policy: WebhookPolicy::default(),
64            timers: Vec::new(),
65            oauth: None,
66            mocks: None,
67            pack_bindings: Vec::new(),
68            env_passthrough: Vec::new(),
69            trace: TraceConfig::from_env(),
70            validation: ValidationConfig::from_env(),
71            operator_policy: OperatorPolicy::allow_all(),
72        }
73    }
74
75    fn resolved_config() -> greentic_config::ResolvedConfig {
76        greentic_config::ResolvedConfig {
77            config: greentic_config_types::GreenticConfig {
78                schema_version: greentic_config_types::ConfigVersion::v1(),
79                environment: greentic_config_types::EnvironmentConfig {
80                    env_id: greentic_types::EnvId::new("local").expect("env"),
81                    deployment: None,
82                    connection: None,
83                    region: None,
84                },
85                paths: greentic_config_types::PathsConfig {
86                    greentic_root: PathBuf::from(".greentic"),
87                    state_dir: PathBuf::from(".greentic/state"),
88                    cache_dir: PathBuf::from(".greentic/cache"),
89                    logs_dir: PathBuf::from(".greentic/logs"),
90                },
91                packs: None,
92                services: None,
93                events: None,
94                runtime: greentic_config_types::RuntimeConfig::default(),
95                telemetry: greentic_config_types::TelemetryConfig::default(),
96                network: greentic_config_types::NetworkConfig::default(),
97                deployer: None,
98                secrets: greentic_config_types::SecretsBackendRefConfig::default(),
99                dev: None,
100            },
101            provenance: HashMap::new(),
102            warnings: Vec::new(),
103        }
104    }
105
106    #[tokio::test]
107    async fn start_embedded_host_rejects_empty_builder() {
108        assert!(start_embedded_host(HostBuilder::new()).await.is_err());
109    }
110
111    #[tokio::test]
112    async fn start_embedded_host_starts_with_minimal_config() {
113        let host = start_embedded_host(HostBuilder::new().with_config(host_config()))
114            .await
115            .expect("embedded host");
116        host.stop().await.expect("stop");
117    }
118
119    #[tokio::test]
120    async fn run_http_host_rejects_empty_runner_config() {
121        let cfg = RunnerConfig {
122            tenant_bindings: HashMap::new(),
123            pack: runner_core::env::PackConfig {
124                source: runner_core::env::PackSource::Fs,
125                index_location: runner_core::env::IndexLocation::File(PathBuf::from("index.json")),
126                cache_dir: PathBuf::from("cache"),
127                public_key: None,
128                network: None,
129            },
130            port: 0,
131            refresh_interval: std::time::Duration::from_secs(1),
132            routing: greentic_runner_host::RoutingConfig::default(),
133            admin: greentic_runner_host::AdminAuth::default(),
134            telemetry: None,
135            secrets_backend: greentic_runner_host::secrets::SecretsBackend::Env,
136            wasi_policy: greentic_runner_host::RunnerWasiPolicy::default(),
137            resolved_config: resolved_config(),
138            trace: greentic_runner_host::trace::TraceConfig::from_env(),
139            validation: greentic_runner_host::validate::ValidationConfig::from_env(),
140        };
141
142        assert!(run_http_host(cfg).await.is_err());
143    }
144}