Skip to content

Commit

Permalink
Add libgit2 code to clone and update repos
Browse files Browse the repository at this point in the history
- todo: Create repo directory structure!
  • Loading branch information
Reboot-Codes committed Nov 14, 2024
1 parent e7de2d9 commit 83f3bb4
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 51 deletions.
1 change: 1 addition & 0 deletions clover-hub/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![feature(stmt_expr_attributes)]
#![feature(let_chains)]
#![feature(ascii_char)]

mod server;
mod tui;
Expand Down
2 changes: 1 addition & 1 deletion clover-hub/src/server/evtbuzz/models.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{collections::HashMap, sync::Arc};
use tokio::sync::Mutex;
use serde::{Deserialize, Serialize};
use crate::{server::{arbiter::models::{ApiKey, ApiKeyWithKeyWithoutUID, User}, warehouse::{config::models::Config, manifest::models::Manifest}, modman::models::Module, appd::models::Application}, utils::{gen_api_key_with_check, gen_uid_with_check}};
use crate::{server::{arbiter::models::{ApiKey, ApiKeyWithKeyWithoutUID, User}, warehouse::{config::models::Config, repos::models::Manifest}, modman::models::Module, appd::models::Application}, utils::{gen_api_key_with_check, gen_uid_with_check}};

// TODO: Define defaults via `Default` trait impl.

Expand Down
3 changes: 0 additions & 3 deletions clover-hub/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,6 @@ pub async fn server_main(data_dir: &String, port: u16, cancellation_token: Cance
warehouse::Error::FailedToCreateDataDir { error } => {
error!("Failed to create data directory! Please create `{}` and set the proper permissions manually, then re-run the server. Failed due to:\n{}", data_dir.clone(), error);
},
warehouse::Error::FailedToCheckConfigFile { error } => {
error!("Failed to check existence of config file, due to:\n{}", error);
},
warehouse::Error::FailedToCreateConfigFile { error } => {
error!("Failed to create the configuration file, due to:\n{}", error);
},
Expand Down
13 changes: 11 additions & 2 deletions clover-hub/src/server/warehouse/config/models.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use crate::server::warehouse::manifest::models::Manifest;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub docker_daemon: String,
pub repos: HashMap<String, Manifest>,
pub repos: HashMap<String, RepoSpec>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RepoSpec {
/// Friendly user-set name. (Can override the repo-name if the user so chooses.)
pub name: Option<String>,
/// The source for this repo, be it a local directory, or a remote git repository via HTTP(S) or SSH.
pub src: String,
/// The branch to clone and merge
pub branch: String,
}

impl Default for Config {
Expand Down
1 change: 0 additions & 1 deletion clover-hub/src/server/warehouse/manifest/mod.rs

This file was deleted.

101 changes: 57 additions & 44 deletions clover-hub/src/server/warehouse/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
pub mod config;
pub mod manifest;
pub mod repos;

use config::models::Config;
use repos::{download_repo_updates, update_repo_dir_structure};
use os_path::OsPath;
use std::io::{Read, Write};
use std::sync::Arc;
use std::fs;
use log::{debug, info};
use simple_error::SimpleError;

use crate::server::evtbuzz::models::Store;

// TODO: Move to snafu crate.
Expand All @@ -20,9 +20,6 @@ pub enum Error {
FailedToCreateDataDir {
error: SimpleError
},
FailedToCheckConfigFile {
error: SimpleError
},
FailedToOpenConfigFile {
error: SimpleError
},
Expand All @@ -42,34 +39,22 @@ pub enum Error {

pub async fn setup_warehouse(data_dir: String, store: Arc<Store>) -> Result<(), Error> {
let mut err: Option<Result<(), Error>> = None;
let mut data_dir_path = OsPath::new().join(data_dir.clone());
data_dir_path.resolve();

debug!("Setting up Warehouse in {}...", data_dir.clone());

// Ensure that the data dir is valid.
match fs::exists(data_dir.clone()) {
Ok(data_dir_exists) => {
if !data_dir_exists {
match fs::create_dir_all(data_dir.clone()) {
Ok(_) => {
match fs::exists(data_dir.clone()) {
Ok(exists) => {
if !exists {
err = Some(Err(Error::FailedToCreateDataDir { error: SimpleError::new("Check failed after creation!") }));
}
},
Err(e) => {
err = Some(Err(Error::FailedToCreateDataDir { error: SimpleError::from(e) }));
}
}
},
Err(e) => {
err = Some(Err(Error::FailedToCreateDataDir { error: SimpleError::from(e) }));
}
if !data_dir_path.exists() {
match fs::create_dir_all(data_dir.clone()) {
Ok(_) => {
if !data_dir_path.exists() {
err = Some(Err(Error::FailedToCreateDataDir { error: SimpleError::new("Check failed after creation!") }));
}
},
Err(e) => {
err = Some(Err(Error::FailedToCreateDataDir { error: SimpleError::from(e) }));
}
},
Err(e) => {
err = Some(Err(Error::FailedToCheckDataDir { error: SimpleError::from(e) }));
}
}

Expand All @@ -80,28 +65,21 @@ pub async fn setup_warehouse(data_dir: String, store: Arc<Store>) -> Result<(),
match err {
Some(_) => {},
None => {
match fs::exists(config_file_path.clone()) {
Ok(config_file_exists) => {
if !config_file_exists {
match fs::File::create(config_file_path.clone()) {
Ok(mut file) => {
match file.write_all(serde_jsonc::to_string::<Config>(&Default::default()).unwrap().as_bytes()) {
Ok(_) => {
info!("Wrote default config!");
},
Err(e) => {
err = Some(Err(Error::FailedToWriteToConfigFile { error: SimpleError::from(e) }))
}
}
if !config_file_path.exists() {
match fs::File::create(config_file_path.clone()) {
Ok(mut file) => {
match file.write_all(serde_jsonc::to_string::<Config>(&Default::default()).unwrap().as_bytes()) {
Ok(_) => {
info!("Wrote default config!");
},
Err(e) => {
err = Some(Err(Error::FailedToCreateConfigFile { error: SimpleError::from(e) }))
err = Some(Err(Error::FailedToWriteToConfigFile { error: SimpleError::from(e) }))
}
}
},
Err(e) => {
err = Some(Err(Error::FailedToCreateConfigFile { error: SimpleError::from(e) }))
}
},
Err(e) => {
err = Some(Err(Error::FailedToCheckConfigFile { error: SimpleError::from(e) }));
}
}
}
Expand Down Expand Up @@ -140,8 +118,43 @@ pub async fn setup_warehouse(data_dir: String, store: Arc<Store>) -> Result<(),
}

// Read repo data and load into applicable areas in the store.
let repo_dir_path = warehouse_path.join("/repos/");
match err {
Some(_) => {},
None => {
if !repo_dir_path.exists() {
match fs::create_dir(repo_dir_path.clone()) {
Ok(_) => {},
Err(e) => {

}
}
}
}
}

match err {
Some(_) => {},
None => {
match update_repo_dir_structure(store.config.lock().await.repos.clone()).await {
Ok(_) => {
match download_repo_updates(store.config.lock().await.repos.clone(), store.clone(), repo_dir_path.clone()).await {
Ok(_) => {

},
Err(e) => {

}
}
},
Err(e) => {

}
}
}
}

// Return any errors if they occurred
match err {
Some(e) => {
match e {
Expand Down
162 changes: 162 additions & 0 deletions clover-hub/src/server/warehouse/repos/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
pub mod models;

use std::{collections::HashMap, sync::Arc};
use git2::{BranchType, FileFavor, MergeOptions, Repository};
use log::{info, warn};
use os_path::OsPath;
use simple_error::SimpleError;

use crate::server::evtbuzz::models::Store;
use super::config::models::RepoSpec;

pub struct Error(SimpleError);

enum RepoDirTreeEntry {
String,
HashMap(String, Box<RepoDirTreeEntry>)
}

pub async fn update_repo_dir_structure(repos: HashMap<String, RepoSpec>) -> Result<(), Error> {
let mut err = None;
let mut repo_dir_tree: HashMap<String, RepoDirTreeEntry> = HashMap::new();

// Build the tree where strings are the source url (and therefore during dir creation, create a directory called `@repo` under it), and hashmaps are more directories to create
for (repo_id, repo_spec) in repos {
for id_segment in repo_id.split(".") {

}
}

// Recursively create directories following the tree structure

match err {
Some(e) => { Err(e) },
None => { Ok(()) }
}
}

impl From<git2::Error> for Error {
fn from(value: git2::Error) -> Self {
Error(SimpleError::from(value))
}
}

pub async fn download_repo_updates(repos: HashMap<String, RepoSpec>, store: Arc<Store>, repo_dir_path: OsPath) -> Result<(), Error> {
let mut err = None;
let mut repos_updated = 0;

for (repo_id, repo_spec) in repos {
let mut repo_path = OsPath::new().join(repo_dir_path.clone().to_string()).join("/@repo/");
for id_segment in repo_id.split(".").collect::<Vec<&str>>() {
repo_path.push(id_segment);
}

let repo_str;
match repo_spec.name {
Some(name) => {
repo_str = format!("{} ({})", name, repo_id.clone());
},
None => {
repo_str = repo_id.clone();
}
}

if repo_dir_path.join("/.git/").is_dir() {
match Repository::open(repo_dir_path.to_string()) {
Ok(repo) => {
match repo.remotes() {
Ok(remotes) => {
let mut main_remote = None;
for remote_name in remotes.into_iter() {
match remote_name {
None => {},
Some(remote_name_str) => {
match repo.find_remote(remote_name_str) {
Ok(remote) => {
// Fetch the url
match remote.url() {
Some(remote_url) => {
if remote_url == repo_spec.src {
main_remote = Some(remote);
break;
}
},
None => {}
}
},
Err(e) => {

}
}
}
}
}

match main_remote {
Some(mut remote) => {
match remote.fetch(&[repo_spec.branch.clone()], None, None) {
Ok(_) => {
let remote_branch = repo.find_branch(&repo_spec.branch.clone(), BranchType::Remote)?;
if remote_branch.is_head() && (remote_branch.get().resolve()?.target().unwrap() == repo.head().unwrap().resolve()?.target().unwrap()) {

} else {
match repo.merge(
&[&repo.find_annotated_commit(remote_branch.into_reference().resolve()?.target().unwrap())?],
Some(MergeOptions::new().file_favor(FileFavor::Theirs)),
None
) {
Ok(_) => {
match repo.cleanup_state() {
Ok(_) => {
repos_updated += 1;
let comm = repo.head()?.peel_to_commit()?;
info!("Updated {} to {} ({})!", repo_str, comm.message().unwrap_or("*no message*"), comm.id());
},
Err(e) => {

}
}
},
Err(e) => {
repo.cleanup_state();
}
}
}
},
Err(e) => {

}
}
},
None => {
warn!("No remote source for {}!", repo_str.clone());
}
}
},
Err(e) => {
match Repository::clone_recurse(&repo_spec.src.clone(), repo_path.clone()) {
Ok(repo) => {

},
Err(e) => {

}
}
}
}
},
Err(e) => {

}
}
}
}

match err {
Some(e) => { Err(e) },
None => {
if repos_updated > 0 { info!("Updated {} repo(s)!", repos_updated); }
Ok(())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
link:
type: doc
id: intro
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Configuration

The configuration file (usually `config.jsonc`) holds persistent configuration for clover.

0 comments on commit 83f3bb4

Please sign in to comment.