sigstore_verification/sources/
github.rs1use crate::Result;
2use crate::api::{Attestation, AttestationClient, FetchParams};
3use crate::sources::{ArtifactRef, AttestationSource};
4use async_trait::async_trait;
5
6pub struct GitHubSource {
8 client: AttestationClient,
9 owner: String,
10 repo: String,
11}
12
13impl GitHubSource {
14 pub fn new(
16 owner: impl Into<String>,
17 repo: impl Into<String>,
18 token: Option<&str>,
19 ) -> Result<Self> {
20 let mut builder = Self::builder().owner(owner).repo(repo);
21 if let Some(token) = token {
22 builder = builder.token(token);
23 }
24 builder.build()
25 }
26
27 pub fn with_base_url(
31 owner: impl Into<String>,
32 repo: impl Into<String>,
33 token: Option<&str>,
34 base_url: &str,
35 ) -> Result<Self> {
36 let mut builder = Self::builder().owner(owner).repo(repo).base_url(base_url);
37 if let Some(token) = token {
38 builder = builder.token(token);
39 }
40 builder.build()
41 }
42
43 pub fn with_client(
47 owner: impl Into<String>,
48 repo: impl Into<String>,
49 client: AttestationClient,
50 ) -> Self {
51 Self {
52 client,
53 owner: owner.into(),
54 repo: repo.into(),
55 }
56 }
57
58 pub fn builder() -> GitHubSourceBuilder {
60 GitHubSourceBuilder::default()
61 }
62}
63
64#[derive(Debug, Default)]
66pub struct GitHubSourceBuilder {
67 owner: Option<String>,
68 repo: Option<String>,
69 token: Option<String>,
70 base_url: Option<String>,
71}
72
73impl GitHubSourceBuilder {
74 pub fn owner(mut self, owner: impl Into<String>) -> Self {
75 self.owner = Some(owner.into());
76 self
77 }
78
79 pub fn repo(mut self, repo: impl Into<String>) -> Self {
80 self.repo = Some(repo.into());
81 self
82 }
83
84 pub fn token(mut self, token: impl Into<String>) -> Self {
85 self.token = Some(token.into());
86 self
87 }
88
89 pub fn base_url(mut self, url: impl Into<String>) -> Self {
90 self.base_url = Some(url.into());
91 self
92 }
93
94 pub fn build(self) -> Result<GitHubSource> {
95 let owner = self.owner.ok_or_else(|| {
96 crate::AttestationError::Api("GitHubSource: owner is required".into())
97 })?;
98 let repo = self
99 .repo
100 .ok_or_else(|| crate::AttestationError::Api("GitHubSource: repo is required".into()))?;
101
102 let mut client_builder = AttestationClient::builder();
103 if let Some(token) = self.token {
104 client_builder = client_builder.github_token(&token);
105 }
106 if let Some(base_url) = self.base_url {
107 client_builder = client_builder.base_url(&base_url);
108 }
109 let client = client_builder.build()?;
110
111 Ok(GitHubSource {
112 client,
113 owner,
114 repo,
115 })
116 }
117}
118
119#[async_trait]
120impl AttestationSource for GitHubSource {
121 async fn fetch_attestations(&self, artifact: &ArtifactRef) -> Result<Vec<Attestation>> {
132 let params = FetchParams {
133 owner: self.owner.clone(),
134 repo: Some(format!("{}/{}", self.owner, self.repo)),
135 digest: artifact.digest.clone(),
136 limit: 30,
137 predicate_type: Some("https://slsa.dev/provenance/v1".to_string()),
138 };
139
140 self.client.fetch_attestations(params).await
141 }
142
143 fn source_type(&self) -> &'static str {
144 "GitHub"
145 }
146}