spin_oci/
utils.rs

1//! Utilities related to distributing Spin apps via OCI registries
2
3use anyhow::{Context, Result};
4use async_compression::tokio::bufread::GzipDecoder;
5use async_compression::tokio::write::GzipEncoder;
6use async_tar::Archive;
7use spin_common::ui::quoted_path;
8use std::path::{Path, PathBuf};
9
10/// Create a compressed archive of source, returning its path in working_dir
11pub async fn archive(source: &Path, working_dir: &Path) -> Result<PathBuf> {
12    // Create tar archive file
13    let tar_gz_path = working_dir
14        .join(source.file_name().unwrap())
15        .with_extension("tar.gz");
16    let tar_gz = tokio::fs::File::create(tar_gz_path.as_path())
17        .await
18        .context(format!(
19            "Unable to create tar archive for source {}",
20            quoted_path(source)
21        ))?;
22
23    // Create encoder
24    // TODO: use zstd? May be more performant
25    let tar_gz_enc = GzipEncoder::new(tar_gz);
26
27    // Build tar archive
28    let mut tar_builder = async_tar::Builder::new(
29        tokio_util::compat::TokioAsyncWriteCompatExt::compat_write(tar_gz_enc),
30    );
31    tar_builder
32        .append_dir_all(".", source)
33        .await
34        .context(format!(
35            "Unable to create tar archive for source {}",
36            quoted_path(source)
37        ))?;
38    // Finish writing the archive
39    tar_builder.finish().await?;
40    // Shutdown the encoder
41    use tokio::io::AsyncWriteExt;
42    tar_builder
43        .into_inner()
44        .await?
45        .into_inner()
46        .shutdown()
47        .await?;
48    Ok(tar_gz_path)
49}
50
51/// Unpack a compressed archive existing at source into dest
52pub async fn unarchive(source: &Path, dest: &Path) -> Result<()> {
53    let decoder = GzipDecoder::new(tokio::io::BufReader::new(
54        tokio::fs::File::open(source).await?,
55    ));
56    let archive = Archive::new(tokio_util::compat::TokioAsyncReadCompatExt::compat(decoder));
57    if let Err(e) = archive.unpack(dest).await {
58        return Err(e.into());
59    };
60    Ok(())
61}