use kenshell::proto::v1 as pb; use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::rpcgit::archive::types::ArchiveFormat; use crate::service::GitService; use crate::service::rpc::{rpc_error, wait_job}; use crate::share::errors::GitError; use crate::share::validate::validate_revision; #[derive(Debug, Clone, Serialize, utoipa::ToSchema)] pub struct ArchiveResultDto { pub format: String, pub size: i64, pub cache_key: String, pub download_url: Option, } #[derive(Debug, Clone, Deserialize)] pub struct ArchiveOptionsDto { pub format: ArchiveFormat, #[serde(default)] pub path_filter: Option, } fn ext(format: ArchiveFormat) -> &'static str { match format { ArchiveFormat::Tar => "tar", ArchiveFormat::TarGz => "tar.gz", ArchiveFormat::TarBz2 => "tar.bz2", ArchiveFormat::Zip => "zip", } } fn archive_proto(format: ArchiveFormat) -> pb::ArchiveFormat { match format { ArchiveFormat::Tar => pb::ArchiveFormat::Tar, ArchiveFormat::TarGz => pb::ArchiveFormat::TarGz, ArchiveFormat::TarBz2 => pb::ArchiveFormat::TarBz2, ArchiveFormat::Zip => pb::ArchiveFormat::Zip, } } impl GitService { pub async fn archive_create( &self, space_name: String, repo_name: String, revision: String, opts: ArchiveOptionsDto, ) -> Result { validate_revision(&revision)?; let ctx = self.context(space_name, repo_name).await?; let mut client = ctx.ks.archive(); let job = client .start_archive(pb::StartArchiveRequest { repository: Some(ctx.repository_ref()), revision, format: archive_proto(opts.format) as i32, path_filters: opts.path_filter.into_iter().collect(), actor: Some(ctx.actor()), idempotency_key: Uuid::now_v7().to_string(), }) .await .map_err(rpc_error)? .into_inner() .job .ok_or_else(|| GitError::Internal("archive job missing".into()))?; let job = wait_job(&ctx.ks, &job.id, self.job_timeout()).await?; let artifact = match job.result { Some(pb::job::Result::Archive(archive)) => archive, _ => { return Err(GitError::Internal( "archive job returned no artifact".into(), )); } }; Ok(ArchiveResultDto { format: ext(opts.format).to_string(), size: artifact.size_bytes as i64, cache_key: artifact.storage_key, download_url: Some(artifact.download_url), }) } pub async fn archive_download( &self, space_name: String, repo_name: String, revision: String, opts: ArchiveOptionsDto, ) -> Result { validate_revision(&revision)?; let ctx = self.context(space_name, repo_name).await?; let mut client = ctx.ks.archive(); let artifact = client .get_archive_artifact(pb::GetArchiveArtifactRequest { repository: Some(ctx.repository_ref()), revision, format: archive_proto(opts.format) as i32, path_filters: opts.path_filter.into_iter().collect(), }) .await .map_err(rpc_error)? .into_inner() .artifact .ok_or(GitError::NotFound)?; Ok(ArchiveResultDto { format: ext(opts.format).to_string(), size: artifact.size_bytes as i64, cache_key: artifact.storage_key, download_url: Some(artifact.download_url), }) } pub async fn archive_create_bundle( &self, space_name: String, repo_name: String, ) -> Result { let ctx = self.context(space_name, repo_name).await?; let mut client = ctx.ks.archive(); let job = client .start_archive_bundle(pb::StartArchiveBundleRequest { repository: Some(ctx.repository_ref()), actor: Some(ctx.actor()), idempotency_key: Uuid::now_v7().to_string(), }) .await .map_err(rpc_error)? .into_inner() .job .ok_or_else(|| GitError::Internal("bundle job missing".into()))?; let job = wait_job(&ctx.ks, &job.id, self.job_timeout()).await?; let artifact = match job.result { Some(pb::job::Result::Archive(archive)) => archive, _ => { return Err(GitError::Internal( "archive job returned no artifact".into(), )); } }; Ok(ArchiveResultDto { format: "bundle".to_string(), size: artifact.size_bytes as i64, cache_key: artifact.storage_key, download_url: Some(artifact.download_url), }) } }