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            fast2flow: Default::default(),
73        }
74    }
75
76    fn resolved_config() -> greentic_config::ResolvedConfig {
77        greentic_config::ResolvedConfig {
78            config: greentic_config_types::GreenticConfig {
79                schema_version: greentic_config_types::ConfigVersion::v1(),
80                environment: greentic_config_types::EnvironmentConfig {
81                    env_id: greentic_types::EnvId::new("local").expect("env"),
82                    deployment: None,
83                    connection: None,
84                    region: None,
85                },
86                paths: greentic_config_types::PathsConfig {
87                    greentic_root: PathBuf::from(".greentic"),
88                    state_dir: PathBuf::from(".greentic/state"),
89                    cache_dir: PathBuf::from(".greentic/cache"),
90                    logs_dir: PathBuf::from(".greentic/logs"),
91                },
92                packs: None,
93                services: None,
94                events: None,
95                runtime: greentic_config_types::RuntimeConfig::default(),
96                telemetry: greentic_config_types::TelemetryConfig::default(),
97                network: greentic_config_types::NetworkConfig::default(),
98                deployer: None,
99                secrets: greentic_config_types::SecretsBackendRefConfig::default(),
100                dev: None,
101            },
102            provenance: HashMap::new(),
103            warnings: Vec::new(),
104        }
105    }
106
107    #[tokio::test]
108    async fn start_embedded_host_rejects_empty_builder() {
109        assert!(start_embedded_host(HostBuilder::new()).await.is_err());
110    }
111
112    #[tokio::test]
113    async fn start_embedded_host_starts_with_minimal_config() {
114        let host = start_embedded_host(HostBuilder::new().with_config(host_config()))
115            .await
116            .expect("embedded host");
117        host.stop().await.expect("stop");
118    }
119
120    #[tokio::test]
121    async fn run_http_host_rejects_empty_runner_config() {
122        let cfg = RunnerConfig {
123            tenant_bindings: HashMap::new(),
124            pack: runner_core::env::PackConfig {
125                source: runner_core::env::PackSource::Fs,
126                index_location: runner_core::env::IndexLocation::File(PathBuf::from("index.json")),
127                cache_dir: PathBuf::from("cache"),
128                public_key: None,
129                network: None,
130            },
131            port: 0,
132            refresh_interval: std::time::Duration::from_secs(1),
133            routing: greentic_runner_host::RoutingConfig::default(),
134            admin: greentic_runner_host::AdminAuth::default(),
135            telemetry: None,
136            secrets_backend: greentic_runner_host::secrets::SecretsBackend::Env,
137            wasi_policy: greentic_runner_host::RunnerWasiPolicy::default(),
138            resolved_config: resolved_config(),
139            trace: greentic_runner_host::trace::TraceConfig::from_env(),
140            validation: greentic_runner_host::validate::ValidationConfig::from_env(),
141        };
142
143        assert!(run_http_host(cfg).await.is_err());
144    }
145}