Produce a two-layer OCI image: a base layer (the `base_view` materialized) plus a thin delta layer (files that differ on `view` versus `base_view`, with whiteouts for files removed on `view`). Built for the inner loop: the base is content-addressed by its `diff_id` and can be pulled once and cached, so each iteration ships only the small delta. Provenance (view names and Merkle states) is recorde
(&self, opts: StageOptions)
| 232 | /// delta. Provenance (view names and Merkle states) is recorded in the |
| 233 | /// manifest annotations. Returns the manifest digest and base `diff_id`. |
| 234 | pub fn stage(&self, opts: StageOptions) -> Result<StageResult, RepositoryError> { |
| 235 | // 1. Base layer: materialize the shared base view to a temp dir. |
| 236 | let base_dir = tempfile::tempdir()?; |
| 237 | self.materialize_view_to(&opts.base_view, base_dir.path())?; |
| 238 | let base_layer = oci::layer_from_dir(base_dir.path())?; |
| 239 | let base_diff_id = base_layer.diff_id.clone(); |
| 240 | |
| 241 | // 2. Delta: files new or changed on `view` vs. `base_view`, plus |
| 242 | // whiteouts for files present on the base but gone on `view`. |
| 243 | let view_paths = self.visible_file_paths(&opts.view)?; |
| 244 | let base_paths = self.visible_file_paths(&opts.base_view)?; |
| 245 | |
| 246 | let mut changed: Vec<(String, Vec<u8>)> = Vec::new(); |
| 247 | for path in &view_paths { |
| 248 | let view_bytes = match self.get_file_content_on_view(path, &opts.view)? { |
| 249 | Some(bytes) => bytes, |
| 250 | None => continue, |
| 251 | }; |
| 252 | let base_bytes = self.get_file_content_on_view(path, &opts.base_view)?; |
| 253 | if base_bytes.as_deref() != Some(view_bytes.as_slice()) { |
| 254 | changed.push((path.clone(), view_bytes)); |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | let mut whiteouts: Vec<String> = Vec::new(); |
| 259 | for path in &base_paths { |
| 260 | if !view_paths.contains(path) { |
| 261 | whiteouts.push(path.clone()); |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | let delta_files = changed.len(); |
| 266 | let delta_layer = oci::layer_from_entries(&changed, &whiteouts)?; |
| 267 | |
| 268 | // 3. Assemble the layout with provenance annotations. |
| 269 | let view_info = self.get_view_info(&opts.view)?; |
| 270 | let base_info = self.get_view_info(&opts.base_view)?; |
| 271 | let mut annotations = BTreeMap::new(); |
| 272 | annotations.insert("org.atomic.view".to_string(), opts.view.clone()); |
| 273 | annotations.insert("org.atomic.base-view".to_string(), opts.base_view.clone()); |
| 274 | annotations.insert( |
| 275 | "org.atomic.view-merkle".to_string(), |
| 276 | view_info.state_base32(), |
| 277 | ); |
| 278 | annotations.insert( |
| 279 | "org.atomic.base-merkle".to_string(), |
| 280 | base_info.state_base32(), |
| 281 | ); |
| 282 | |
| 283 | let config = OciImageConfig { |
| 284 | architecture: oci::host_arch(), |
| 285 | os: "linux".to_string(), |
| 286 | env: Vec::new(), |
| 287 | entrypoint: Vec::new(), |
| 288 | annotations, |
| 289 | }; |
| 290 | |
| 291 | let manifest_digest = |
no test coverage detected