use std::path::PathBuf;
use crate::paths::copy_cached_to_installed;
use crate::paths::DomainPaths;
use crate::quilt::flow::browse::cache_remote_manifest;
use crate::quilt::flow::install_paths::install_paths;
use crate::quilt::flow::status::InstalledPackageStatus;
use crate::quilt::flow::uninstall_paths::uninstall_paths;
use crate::quilt::lineage::PackageLineage;
use crate::quilt::manifest_handle;
use crate::quilt::storage::Storage;
use crate::quilt::Error;
use crate::s3_utils;
pub async fn pull_package(
lineage: PackageLineage,
manifest: &(impl manifest_handle::ReadableManifest + Sync),
paths: &DomainPaths,
storage: &(impl Storage + Sync),
working_dir: PathBuf,
status: InstalledPackageStatus,
namespace: String,
) -> Result<PackageLineage, Error> {
if !status.changes.is_empty() {
return Err(Error::Package("package has pending changes".to_string()));
}
if lineage.commit.is_some() {
return Err(Error::Package("package has pending commits".to_string()));
}
if lineage.remote.hash != lineage.base_hash {
return Err(Error::Package("package has diverged".to_string()));
}
if lineage.base_hash == lineage.latest_hash {
return Err(Error::Package("package is already up-to-date".to_string()));
}
let installed_paths: Vec<String> = lineage.paths.keys().cloned().collect();
let mut lineage =
uninstall_paths(lineage, working_dir.clone(), storage, &installed_paths).await?;
lineage.remote.hash = lineage.latest_hash.clone();
lineage.base_hash = lineage.latest_hash.clone();
let remote = s3_utils::RemoteS3::new();
cache_remote_manifest(paths, storage, &remote, &lineage.remote).await?;
copy_cached_to_installed(
paths,
storage,
&lineage.remote.bucket,
&namespace,
&lineage.remote.hash,
)
.await?;
let materialized_manifest = manifest.read(storage).await?;
let paths_to_install = installed_paths
.into_iter()
.filter(|x| materialized_manifest.records.contains_key(x))
.collect();
install_paths(
lineage,
manifest,
paths,
working_dir,
namespace,
storage,
&paths_to_install,
)
.await
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
use crate::quilt::flow::status::Change;
use crate::quilt::flow::status::DiscreteChange;
use crate::quilt::mocks;
use crate::quilt::storage::mock_storage::MockStorage;
use crate::quilt::RemoteManifest;
#[tokio::test]
async fn test_no_pull_if_changes() -> Result<(), Error> {
let storage = MockStorage::default();
let lineage = mocks::lineage::with_paths(&vec!["a/a"]);
let status = InstalledPackageStatus {
changes: BTreeMap::from([(
"foo".to_string(),
Change {
previous: None,
current: None,
state: DiscreteChange::Pristine,
},
)]),
..InstalledPackageStatus::default()
};
let error = pull_package(
lineage,
&mocks::manifest::with_record_keys(vec!["a/a".to_string()]),
&DomainPaths::default(),
&storage,
PathBuf::default(),
status,
String::default(),
)
.await;
assert_eq!(
error.unwrap_err().to_string(),
"General error regarding package: package has pending changes".to_string()
);
Ok(())
}
#[tokio::test]
async fn test_no_pull_if_commit() {
let storage = MockStorage::default();
let lineage = mocks::lineage::with_commit();
let error = pull_package(
lineage,
&mocks::manifest::default(),
&DomainPaths::default(),
&storage,
PathBuf::default(),
InstalledPackageStatus::default(),
String::default(),
)
.await;
assert_eq!(
error.unwrap_err().to_string(),
"General error regarding package: package has pending commits".to_string()
);
}
#[tokio::test]
async fn test_no_pull_if_diverged() {
let storage = MockStorage::default();
let lineage = PackageLineage {
remote: RemoteManifest {
hash: "a".to_string(),
..RemoteManifest::default()
},
base_hash: "b".to_string(),
..PackageLineage::default()
};
let error = pull_package(
lineage,
&mocks::manifest::default(),
&DomainPaths::default(),
&storage,
PathBuf::default(),
InstalledPackageStatus::default(),
String::default(),
)
.await;
assert_eq!(
error.unwrap_err().to_string(),
"General error regarding package: package has diverged".to_string()
);
}
#[tokio::test]
async fn test_no_pull_if_up_to_date() {
let storage = MockStorage::default();
let lineage = PackageLineage {
remote: RemoteManifest {
hash: "a".to_string(),
..RemoteManifest::default()
},
base_hash: "a".to_string(),
latest_hash: "a".to_string(),
..PackageLineage::default()
};
let error = pull_package(
lineage,
&mocks::manifest::default(),
&DomainPaths::default(),
&storage,
PathBuf::default(),
InstalledPackageStatus::default(),
String::default(),
)
.await;
assert_eq!(
error.unwrap_err().to_string(),
"General error regarding package: package is already up-to-date".to_string()
);
}
}