(
client: &Client,
registry: &str,
repo: &str,
tag: &str,
token: Option<&str>,
)
| 162 | } |
| 163 | |
| 164 | async fn fetch_manifest_inner( |
| 165 | client: &Client, |
| 166 | registry: &str, |
| 167 | repo: &str, |
| 168 | tag: &str, |
| 169 | token: Option<&str>, |
| 170 | ) -> Result<OciManifest> { |
| 171 | let url = format!("https://{registry}/v2/{repo}/manifests/{tag}"); |
| 172 | |
| 173 | let mut req = client.get(&url).header( |
| 174 | "Accept", |
| 175 | "application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json", |
| 176 | ); |
| 177 | if let Some(t) = token { |
| 178 | req = req.bearer_auth(t); |
| 179 | } |
| 180 | |
| 181 | let response = req.send().await.context("failed to fetch manifest")?; |
| 182 | |
| 183 | if !response.status().is_success() { |
| 184 | bail!( |
| 185 | "failed to fetch manifest: HTTP {} {}", |
| 186 | response.status(), |
| 187 | response.text().await.unwrap_or_default() |
| 188 | ); |
| 189 | } |
| 190 | |
| 191 | // Try to parse as a single manifest first |
| 192 | let body = response.text().await?; |
| 193 | if let Ok(manifest) = serde_json::from_str::<OciManifest>(&body) { |
| 194 | if !manifest.layers.is_empty() { |
| 195 | return Ok(manifest); |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | // Might be an index/manifest list — pick the first manifest |
| 200 | if let Ok(index) = serde_json::from_str::<OciIndex>(&body) { |
| 201 | if let Some(first) = index.manifests.into_iter().find(|m| { |
| 202 | // Prefer the non-attestation manifest |
| 203 | !m.media_type |
| 204 | .as_deref() |
| 205 | .is_some_and(|mt| mt.contains("attestation")) |
| 206 | }) { |
| 207 | return fetch_manifest(client, registry, repo, &first.digest, token).await; |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | bail!("unsupported manifest format"); |
| 212 | } |
| 213 | |
| 214 | /// Download and extract all layer blobs into `dest`. |
| 215 | async fn download_and_extract_layers( |
no test coverage detected