Skip to main content

systemprompt_runtime/optimization/
git_sources.rs

1//! Application-owned credential resolution for Git import, sync and
2//! verification.
3//!
4//! Copyright (c) systemprompt.io — Business Source License 1.1.
5//! See <https://systemprompt.io> for licensing details.
6
7use super::OptimizationError;
8use std::collections::BTreeMap;
9use systemprompt_config::SecretsBootstrap;
10use systemprompt_identifiers::{ManagedSourceId, UserId};
11use systemprompt_marketplace::managed::{
12    GitSyncRequest, GitSyncResult, ManagedRepository, SourceSpec,
13};
14use systemprompt_models::feedback::verification::{
15    DependencyVerificationManifest, DependencyVerificationRequest,
16};
17
18#[derive(Debug, Clone)]
19pub struct GitSourceOrchestrator {
20    managed: ManagedRepository,
21}
22
23impl GitSourceOrchestrator {
24    pub const fn new(managed: ManagedRepository) -> Self {
25        Self { managed }
26    }
27
28    pub async fn synchronize(
29        &self,
30        owner: &UserId,
31        request: &GitSyncRequest,
32    ) -> Result<GitSyncResult, OptimizationError> {
33        let credential = self.credential(owner, &request.source_id).await?;
34        Ok(self
35            .managed
36            .sync_git_source_with_credential(owner, request, credential.as_deref())
37            .await?)
38    }
39
40    pub async fn verify(
41        &self,
42        owner: &UserId,
43        request: &DependencyVerificationRequest,
44    ) -> Result<DependencyVerificationManifest, OptimizationError> {
45        request.validate().map_err(|error| {
46            OptimizationError::Source(format!("Invalid dependency verification request: {error}"))
47        })?;
48        let mut credentials = BTreeMap::new();
49        for revision in &request.revisions {
50            if !credentials.contains_key(&revision.source_id)
51                && let Some(credential) = self.credential(owner, &revision.source_id).await?
52            {
53                credentials.insert(revision.source_id.clone(), credential);
54            }
55        }
56        Ok(self
57            .managed
58            .verify_git_dependencies(owner, request, &credentials)
59            .await?)
60    }
61
62    async fn credential(
63        &self,
64        owner: &UserId,
65        source: &ManagedSourceId,
66    ) -> Result<Option<String>, OptimizationError> {
67        match self.managed.get_source(owner, source).await? {
68            SourceSpec::Git {
69                credential_reference: Some(reference),
70                ..
71            } => {
72                let secrets = SecretsBootstrap::get().map_err(|error| {
73                    OptimizationError::Source(format!("Git credentials are unavailable: {error}"))
74                })?;
75                let credential = secrets
76                    .get(&reference)
77                    .filter(|value| !value.is_empty())
78                    .ok_or_else(|| {
79                        OptimizationError::Source(
80                            "Git credential reference is unresolved".to_owned(),
81                        )
82                    })?;
83                Ok(Some(credential.clone()))
84            },
85            SourceSpec::Git {
86                credential_reference: None,
87                ..
88            } => Ok(None),
89            _ => Err(OptimizationError::Source(
90                "Operation requires a registered Git source".to_owned(),
91            )),
92        }
93    }
94}