Skip to content

Commit

Permalink
Add archive functionality
Browse files Browse the repository at this point in the history
Adding archive functionality for repo hosts like Github instead
of deleting because archive is more easily recoverable if
something goes wrong.
  • Loading branch information
mlieberman85 committed Apr 11, 2024
1 parent f2b2f4e commit ea95ea9
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 23 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ pedantic = "deny"
nursery = "deny"
unwrap_used = "deny"
missing_errors_doc = "allow"
module_name_repetitions = "allow"
module_name_repetitions = "allow"
40 changes: 32 additions & 8 deletions skootrs-bin/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use skootrs_model::{
skootrs::{
facet::InitializedFacet, Config, EcosystemInitializeParams, FacetGetParams, FacetMapKey,
GithubRepoParams, GithubUser, GoParams, InitializedProject, MavenParams,
ProjectCreateParams, ProjectGetParams, ProjectOutputParams, ProjectOutputReference,
ProjectOutputType, ProjectOutputsListParams, ProjectReleaseParam, RepoCreateParams,
SkootError, SourceInitializeParams, SupportedEcosystems,
ProjectArchiveParams, ProjectCreateParams, ProjectGetParams, ProjectOutputParams,
ProjectOutputReference, ProjectOutputType, ProjectOutputsListParams, ProjectReleaseParam,
RepoCreateParams, SkootError, SourceInitializeParams, SupportedEcosystems,
},
};
use std::{collections::HashSet, io::Write, str::FromStr};
Expand Down Expand Up @@ -160,8 +160,8 @@ impl Project {
Ok(project)
}

async fn prompt_get(_config: &Config) -> Result<ProjectGetParams, SkootError> {
let projects = Project::list().await?;
async fn prompt_get(config: &Config) -> Result<ProjectGetParams, SkootError> {
let projects = Project::list(config).await?;
let selected_project =
inquire::Select::new("Select a project", projects.iter().collect()).prompt()?;
Ok(ProjectGetParams {
Expand All @@ -174,11 +174,35 @@ impl Project {
/// # Errors
///
/// Returns an error if the cache can't be loaded or if the list of projects can't be fetched.
pub async fn list() -> Result<HashSet<String>, SkootError> {
pub async fn list(_config: &Config) -> Result<HashSet<String>, SkootError> {
let cache = InMemoryProjectReferenceCache::load_or_create("./skootcache")?;
let projects: HashSet<String> = cache.list().await?;
Ok(projects)
}

/// Archives a project by archiving the repository and removing it from the local cache.
///
/// # Errors
///
/// Returns an error if the project can't be archived or deleted from the cache.
pub async fn archive<'a, T: ProjectService + ?Sized>(
config: &Config,
project_service: &'a T,
project_archive_params: Option<ProjectArchiveParams>,
) -> Result<(), SkootError> {
let project_archive_params = match project_archive_params {
Some(p) => p,
None => ProjectArchiveParams {
initialized_project: Project::get(config, project_service, None).await?,
},
};
let url = project_archive_params.initialized_project.repo.full_url();
project_service.archive(project_archive_params).await?;
let mut local_cache = InMemoryProjectReferenceCache::load_or_create("./skootcache")?;
local_cache.delete(url).await?;
local_cache.save()?;
Ok(())
}
}

pub struct Facet;
Expand Down Expand Up @@ -295,8 +319,8 @@ impl Output {
Ok(output_list)
}

async fn prompt_project_output(_config: &Config) -> Result<ProjectOutputParams, SkootError> {
let projects = Project::list().await?;
async fn prompt_project_output(config: &Config) -> Result<ProjectOutputParams, SkootError> {
let projects = Project::list(config).await?;
let selected_project =
inquire::Select::new("Select a project", projects.iter().collect()).prompt()?;
let selected_output_type =
Expand Down
22 changes: 21 additions & 1 deletion skootrs-bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ enum ProjectCommands {
#[clap(value_parser)]
input: Option<Input>,
},

/// Archive a project.
#[command(name = "archive")]
Archive {
/// This is an optional input parameter that can be used to pass in a file, pipe, url, or stdin.
/// This is expected to be YAML or JSON. If it is not provided, the CLI will prompt the user for the input.
#[clap(value_parser)]
input: Option<Input>,
},

/// List all the projects known to the local Skootrs
#[command(name = "list")]
List,
Expand Down Expand Up @@ -222,6 +232,7 @@ fn parse_optional_input<T: DeserializeOwned>(
}
}

#[allow(clippy::too_many_lines)]
#[tokio::main]
async fn main() -> std::result::Result<(), SkootError> {
init_tracing();
Expand Down Expand Up @@ -260,13 +271,22 @@ async fn main() -> std::result::Result<(), SkootError> {
}
}
ProjectCommands::List => {
if let Err(ref error) = helpers::Project::list()
if let Err(ref error) = helpers::Project::list(&config)
.await
.handle_response_output(stdout())
{
error!(error = error.as_ref(), "Failed to list projects");
}
}
ProjectCommands::Archive { input } => {
let project_archive_params = parse_optional_input(input)?;
if let Err(ref error) =
helpers::Project::archive(&config, &project_service, project_archive_params)
.await
{
error!(error = error.as_ref(), "Failed to archive project");
}
}
},
SkootrsCli::Facet { facet } => match facet {
FacetCommands::Get { input } => {
Expand Down
30 changes: 28 additions & 2 deletions skootrs-lib/src/service/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ use crate::service::facet::{FacetSetParamsGenerator, RootFacetService};

use skootrs_model::skootrs::{
facet::{CommonFacetCreateParams, InitializedFacet, SourceFile},
FacetGetParams, FacetMapKey, InitializedProject, InitializedSource, ProjectCreateParams,
ProjectGetParams, ProjectOutputReference, ProjectOutputsListParams, SkootError,
FacetGetParams, FacetMapKey, InitializedProject, InitializedSource, ProjectArchiveParams,
ProjectCreateParams, ProjectGetParams, ProjectOutputReference, ProjectOutputsListParams,
SkootError,
};

use super::{
Expand Down Expand Up @@ -72,10 +73,25 @@ pub trait ProjectService {
params: ProjectGetParams,
) -> impl std::future::Future<Output = Result<Vec<FacetMapKey>, SkootError>> + Send;

/// Lists the outputs of an initialized project.
///
/// # Errors
///
/// Returns an error if the list of outputs can't be fetched.
fn outputs_list(
&self,
params: ProjectOutputsListParams,
) -> impl std::future::Future<Output = Result<Vec<ProjectOutputReference>, SkootError>> + Send;

/// Archives an initialized project.
///
/// # Errors
///
/// Returns an error if the project can't be archived.
fn archive(
&self,
_params: ProjectArchiveParams,
) -> impl std::future::Future<Output = Result<String, SkootError>> + Send;
}

/// The `LocalProjectService` struct provides an implementation of the `ProjectService` trait for initializing
Expand Down Expand Up @@ -245,6 +261,12 @@ where
async fn list_facets(&self, params: ProjectGetParams) -> Result<Vec<FacetMapKey>, SkootError> {
Ok(self.get(params).await?.facets.keys().cloned().collect())
}

async fn archive(&self, params: ProjectArchiveParams) -> Result<String, SkootError> {
self.repo_service
.archive(params.initialized_project.repo)
.await
}
}

#[cfg(test)]
Expand Down Expand Up @@ -342,6 +364,10 @@ mod tests {

Ok("Worked".to_string())
}

async fn archive(&self, initialized_repo: InitializedRepo) -> Result<String, SkootError> {
Ok(initialized_repo.full_url())
}
}

impl EcosystemService for MockEcosystemService {
Expand Down
29 changes: 29 additions & 0 deletions skootrs-lib/src/service/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use std::{process::Command, str::FromStr, sync::Arc};

use chrono::Utc;
use octocrab::Octocrab;
use tracing::{info, debug};

use skootrs_model::{cd_events::repo_created::{RepositoryCreatedEvent, RepositoryCreatedEventContext, RepositoryCreatedEventContextId, RepositoryCreatedEventContextVersion, RepositoryCreatedEventSubject, RepositoryCreatedEventSubjectContent, RepositoryCreatedEventSubjectContentName, RepositoryCreatedEventSubjectContentUrl, RepositoryCreatedEventSubjectId}, skootrs::{InitializedRepoGetParams, GithubRepoParams, GithubUser, InitializedGithubRepo, InitializedRepo, InitializedSource, RepoCreateParams, SkootError}};
Expand Down Expand Up @@ -62,6 +63,8 @@ pub trait RepoService {
///
/// Returns an error if the file can't be fetched from the repository for any reason.
fn fetch_file_content<P: AsRef<std::path::Path> + Send>(&self, initialized_repo: &InitializedRepo, path: P) -> impl std::future::Future<Output = Result<String, SkootError>> + std::marker::Send;

fn archive(&self, initialized_repo: InitializedRepo) -> impl std::future::Future<Output = Result<String, SkootError>> + Send;
}

/// The `LocalRepoService` struct provides an implementation of the `RepoService` trait for initializing
Expand Down Expand Up @@ -171,6 +174,32 @@ impl RepoService for LocalRepoService {
}
}
}

async fn archive(&self, initialized_repo: InitializedRepo) -> Result<String, SkootError> {
match initialized_repo {
InitializedRepo::Github(g) => {
#[derive(serde::Serialize)]
struct ArchiveParams {
archived: bool,
}
let owner = g.organization.get_name();
let repo = g.name.clone();
let body = ArchiveParams {
archived: true,
};

info!("Archiving {owner}/{repo}");

// FIXME: This should work with `Octocrabe::instance()` but for some reason it doesn't pick up the token/session
let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required");
let octocrab = Octocrab::builder().personal_token(token).build()?;
let archived_response: serde_json::Value = octocrab.patch(format!("/repos/{owner}/{repo}"), Some(&body)).await?;
info!("Archived: {archived_response}");

Ok(g.full_url())
}
}
}
}

/// The `GithubRepoHandler` struct represents a handler for initializing and managing Github repos.
Expand Down
8 changes: 8 additions & 0 deletions skootrs-model/src/skootrs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@ pub struct ProjectOutputParams {
pub project_output: String,
}

/// The parameters for archiving a project.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct ProjectArchiveParams {
/// The initialized project to archive.
pub initialized_project: InitializedProject,
}

/// The set of supported output types
#[derive(Serialize, Deserialize, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
Expand Down
23 changes: 12 additions & 11 deletions skootrs-statestore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,8 @@ impl ProjectStateStore for GitProjectStateStore<LocalSourceService> {
Ok(Some(serde_json::from_str(&project).unwrap()))
}

fn update(
&self,
project: InitializedProject,
) -> impl std::future::Future<Output = Result<(), SkootError>> + Send {
self.create(project)
async fn update(&self, project: InitializedProject) -> Result<(), SkootError> {
self.create(project).await
}
}

Expand All @@ -86,6 +83,10 @@ pub trait ProjectReferenceCache {
&mut self,
repo_url: String,
) -> impl std::future::Future<Output = Result<(), SkootError>> + Send;
fn delete(
&mut self,
repo_url: String,
) -> impl std::future::Future<Output = Result<(), SkootError>> + Send;
}

pub struct InMemoryProjectReferenceCache {
Expand All @@ -103,12 +104,6 @@ impl ProjectReferenceCache for InMemoryProjectReferenceCache {

async fn get(&mut self, repo_url: String) -> Result<InitializedProject, SkootError> {
let repo = InitializedRepo::try_from(repo_url)?;
/*let local_clone = self
.local_repo_service
.clone_local(repo, self.clone_path.clone())?;
let project =
self.local_source_service
.read_file(&local_clone, "./", ".skootrs".to_string())?;*/
let project = self
.local_repo_service
.fetch_file_content(&repo, ".skootrs")
Expand All @@ -122,6 +117,12 @@ impl ProjectReferenceCache for InMemoryProjectReferenceCache {
self.save()?;
Ok(())
}

async fn delete(&mut self, repo_url: String) -> Result<(), SkootError> {
self.cache.remove(&repo_url);
self.save()?;
Ok(())
}
}

impl InMemoryProjectReferenceCache {
Expand Down

0 comments on commit ea95ea9

Please sign in to comment.