1use chrono::{DateTime, Utc};
9use oxidized_state::storage_traits::{ReleaseRegistry, RunId, RunLedger};
10
11use crate::deploy_runner::DeployByDigestRunner;
12use crate::domain::{AivcsError, Result};
13use crate::replay::{replay_run, ReplaySummary};
14
15#[derive(Debug, Clone)]
17pub struct DeployResult {
18 pub run_id: RunId,
20 pub spec_digest: String,
22 pub summary: ReplaySummary,
24}
25
26pub async fn deploy_by_digest(
35 registry: &dyn ReleaseRegistry,
36 ledger: &dyn RunLedger,
37 agent_name: &str,
38 timestamp: Option<DateTime<Utc>>,
39) -> Result<DeployResult> {
40 let release = registry
42 .current(agent_name)
43 .await
44 .map_err(|e| AivcsError::StorageError(e.to_string()))?
45 .ok_or_else(|| {
46 AivcsError::ReleaseConflict(format!("no current release for agent '{}'", agent_name))
47 })?;
48
49 let spec_digest_str = release.spec_digest.as_str().to_string();
50
51 let output = match timestamp {
53 Some(ts) => {
54 DeployByDigestRunner::run_at(ledger, &release.spec_digest, agent_name, ts).await?
55 }
56 None => DeployByDigestRunner::run(ledger, &release.spec_digest, agent_name).await?,
57 };
58
59 let (_events, replay_summary) = replay_run(ledger, &output.run_id.0).await?;
61
62 Ok(DeployResult {
63 run_id: output.run_id,
64 spec_digest: spec_digest_str,
65 summary: replay_summary,
66 })
67}