From 58e991b2b7fae7aaf9fe26385131988c41835baf Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 18 May 2024 11:41:18 +0100 Subject: [PATCH] test(git): make repository more testable Adds a layer around Repository to allow the use of a mock. Mock has still to be implemented. --- crates/cli/Cargo.toml | 1 + crates/cli/src/main.rs | 3 +- crates/config/src/tests.rs | 2 - crates/git/src/fetch.rs | 35 +-- crates/git/src/lib.rs | 3 +- crates/git/src/push.rs | 58 +---- crates/git/src/repository.rs | 227 ++++++++++++++++-- crates/git/src/validate.rs | 44 +--- crates/server/src/actors/repo/branch.rs | 4 +- crates/server/src/actors/repo/mod.rs | 6 +- crates/server/src/actors/server.rs | 10 +- crates/server/src/config/tests.rs | 15 +- .../forgejo/branch/validate_positions.rs | 2 +- crates/server/src/gitforge/forgejo/mod.rs | 30 +-- crates/server/src/gitforge/mock_forge.rs | 9 +- crates/server/src/gitforge/mod.rs | 17 +- crates/server/src/gitforge/tests/forgejo.rs | 6 +- crates/server/src/lib.rs | 5 +- 18 files changed, 295 insertions(+), 182 deletions(-) diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index b22f5b0..0b28397 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -5,6 +5,7 @@ edition = { workspace = true } [dependencies] git-next-server = { workspace = true } +git-next-git = { workspace = true } # CLI parsing clap = { workspace = true } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index e067bed..f8eb9da 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -30,6 +30,7 @@ enum Server { async fn main() { let fs = fs::new(PathBuf::default()); let net = Network::new_real(); + let repo = git_next_git::repository::new(); let commands = Commands::parse(); match commands.command { @@ -41,7 +42,7 @@ async fn main() { git_next_server::init(fs); } Server::Start => { - git_next_server::start(fs, net).await; + git_next_server::start(fs, net, repo).await; } }, } diff --git a/crates/config/src/tests.rs b/crates/config/src/tests.rs index 55c0c61..aeef277 100644 --- a/crates/config/src/tests.rs +++ b/crates/config/src/tests.rs @@ -381,8 +381,6 @@ mod forge_type { mod gitdir { use std::path::PathBuf; - use derive_more::Deref; - use crate::GitDir; #[test] diff --git a/crates/git/src/fetch.rs b/crates/git/src/fetch.rs index 4d561de..ec96932 100644 --- a/crates/git/src/fetch.rs +++ b/crates/git/src/fetch.rs @@ -1,36 +1,9 @@ -use std::ops::Deref; - -use tracing::{debug, info}; - -use super::{RepoDetails, Repository}; - -#[derive(Debug, derive_more::From, derive_more::Display)] +#[derive(Debug, derive_more::Display)] pub enum Error { - UnableToOpenRepo(Box), + UnableToOpenRepo(String), NoFetchRemoteFound, - Connect(Box), + Connect(String), Fetch(String), + Lock, } impl std::error::Error for Error {} - -#[tracing::instrument(skip_all, fields(repo = %repo_details))] -pub fn fetch(repository: &Repository, repo_details: &RepoDetails) -> Result<(), Error> { - let repository = repository.deref().to_thread_local(); - let Some(remote) = repository.find_default_remote(gix::remote::Direction::Fetch) else { - return Err(Error::NoFetchRemoteFound); - }; - debug!(?remote, "fetch remote"); - - remote - .map_err(|e| Error::Fetch(e.to_string()))? - .connect(gix::remote::Direction::Fetch) - .map_err(Box::new)? - .prepare_fetch(gix::progress::Discard, Default::default()) - .map_err(|e| Error::Fetch(e.to_string()))? - .receive(gix::progress::Discard, &Default::default()) - .map_err(|e| Error::Fetch(e.to_string()))?; - - info!("fetched"); - - Ok(()) -} diff --git a/crates/git/src/lib.rs b/crates/git/src/lib.rs index 6427946..f734ba2 100644 --- a/crates/git/src/lib.rs +++ b/crates/git/src/lib.rs @@ -13,11 +13,10 @@ pub mod validate; mod tests; pub use commit::Commit; -pub use fetch::fetch; pub use generation::Generation; pub use git_ref::GitRef; pub use git_remote::GitRemote; -pub use push::push; pub use repo_details::RepoDetails; +pub use repository::open::OpenRepository; pub use repository::Repository; pub use validate::validate; diff --git a/crates/git/src/push.rs b/crates/git/src/push.rs index c4a506a..5c0e6dd 100644 --- a/crates/git/src/push.rs +++ b/crates/git/src/push.rs @@ -1,10 +1,4 @@ -use std::ops::Deref; - -use git_next_config::BranchName; -use secrecy::ExposeSecret; -use tracing::{info, warn}; - -use super::{GitRef, RepoDetails, Repository}; +use super::GitRef; #[derive(Debug)] pub enum Force { @@ -20,60 +14,12 @@ impl std::fmt::Display for Force { } } -// TODO: (#72) reimplement using `gix` -#[tracing::instrument(skip_all, fields(branch = %branch_name, to = %to_commit, force = %force))] -pub fn push( - repository: &Repository, - repo_details: &RepoDetails, - branch_name: BranchName, - to_commit: GitRef, - force: Force, -) -> Result { - let origin = repo_details.origin(); - let force = match force { - Force::No => "".to_string(), - Force::From(old_ref) => format!("--force-with-lease={branch_name}:{old_ref}"), - }; - // INFO: never log the command as it contains the API token within the 'origin' - let command: secrecy::Secret = format!( - "/usr/bin/git push {} {to_commit}:{branch_name} {force}", - origin.expose_secret() - ) - .into(); - let git_dir = repository.deref().git_dir(); - let ctx = gix::diff::command::Context { - git_dir: Some(git_dir.to_path_buf()), - ..Default::default() - }; - match gix::command::prepare(command.expose_secret()) - .with_context(ctx) - .with_shell_allow_argument_splitting() - .stdout(std::process::Stdio::null()) - .stderr(std::process::Stdio::null()) - .spawn() - { - Ok(mut child) => match child.wait() { - Ok(_) => { - info!("Branch updated"); - Ok(()) - } - Err(err) => { - warn!(?err, ?git_dir, "Failed (wait)"); - Err(Error::Push) - } - }, - Err(err) => { - warn!(?err, ?git_dir, "Failed (spawn)"); - Err(Error::Push) - } - } -} - #[derive(Debug, derive_more::From, derive_more::Display)] pub enum Error { Open(Box), Fetch(super::fetch::Error), Push, + Lock, } impl std::error::Error for Error {} diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index b3562c7..fe351ba 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -1,22 +1,221 @@ // -use std::{ops::Deref as _, path::PathBuf, sync::atomic::AtomicBool}; +use std::{ + ops::Deref as _, + sync::{atomic::AtomicBool, Arc, Mutex}, +}; + +use git_next_config::{BranchName, GitDir, Hostname, RepoPath}; +use tracing::{info, warn}; + +use crate::{fetch, push, GitRef, GitRemote}; use super::RepoDetails; -#[derive(Debug, Clone, derive_more::From, derive_more::Deref)] -pub struct Repository(gix::ThreadSafeRepository); -impl Repository { - pub fn open(gitdir: impl Into) -> Result { - Ok(Self(gix::ThreadSafeRepository::open(gitdir.into())?)) - } - pub fn clone(repo_details: &RepoDetails) -> Result { - use secrecy::ExposeSecret; - let origin = repo_details.origin(); - let (repository, _outcome) = - gix::prepare_clone_bare(origin.expose_secret().as_str(), repo_details.gitdir.deref())? - .fetch_only(gix::progress::Discard, &AtomicBool::new(false))?; +#[derive(Clone, Copy, Debug)] +pub enum Repository { + Real, + Mock, +} +pub const fn new() -> Repository { + Repository::Real +} +pub const fn mock() -> Repository { + Repository::Mock +} +mod mock { + use super::*; + pub struct MockRepository; + impl RepositoryLike for MockRepository { + fn open(&self, _gitdir: &GitDir) -> Result { + Ok(open::OpenRepository::Mock) + } - Ok(repository.into_sync().into()) + fn git_clone(&self, _repo_details: &RepoDetails) -> Result { + Ok(open::OpenRepository::Mock) + } + } +} + +pub trait RepositoryLike { + fn open(&self, gitdir: &GitDir) -> Result; + fn git_clone(&self, repo_details: &RepoDetails) -> Result; +} +impl std::ops::Deref for Repository { + type Target = dyn RepositoryLike; + + fn deref(&self) -> &Self::Target { + match self { + Self::Real => &real::RealRepository, + Self::Mock => &mock::MockRepository, + } + } +} +mod real { + use super::*; + pub struct RealRepository; + impl RepositoryLike for RealRepository { + fn open(&self, gitdir: &GitDir) -> Result { + Ok(open::OpenRepository::Real(open::RealOpenRepository::new( + Arc::new(Mutex::new( + gix::ThreadSafeRepository::open(gitdir.to_path_buf())?.to_thread_local(), + )), + ))) + } + + fn git_clone(&self, repo_details: &RepoDetails) -> Result { + use secrecy::ExposeSecret; + let origin = repo_details.origin(); + let (repository, _outcome) = gix::prepare_clone_bare( + origin.expose_secret().as_str(), + repo_details.gitdir.deref(), + )? + .fetch_only(gix::progress::Discard, &AtomicBool::new(false))?; + + Ok(open::OpenRepository::Real(open::RealOpenRepository::new( + Arc::new(Mutex::new(repository)), + ))) + } + } +} + +pub mod open { + use super::*; + #[derive(Clone, Debug)] + pub enum OpenRepository { + Real(RealOpenRepository), + Mock, // TODO: (#38) contain a mock model of a repo + } + pub trait OpenRepositoryLike { + fn find_default_remote(&self, direction: Direction) -> Option; + fn fetch(&self) -> Result<(), fetch::Error>; + fn push( + &self, + repo_details: &RepoDetails, + branch_name: BranchName, + to_commit: GitRef, + force: push::Force, + ) -> Result<(), push::Error>; + } + impl std::ops::Deref for OpenRepository { + type Target = dyn OpenRepositoryLike; + + fn deref(&self) -> &Self::Target { + match self { + Self::Real(real) => real, + Self::Mock => todo!(), + } + } + } + + #[derive(Clone, Debug, derive_more::Constructor)] + pub struct RealOpenRepository(Arc>); + impl OpenRepositoryLike for RealOpenRepository { + fn find_default_remote(&self, direction: Direction) -> Option { + let Ok(repository) = self.0.lock() else { + return None; + }; + let Some(Ok(remote)) = repository.find_default_remote(direction.into()) else { + return None; + }; + let url = remote.url(direction.into())?; + let host = url.host()?; + let path = url.path.to_string(); + let path = path.strip_prefix('/').map_or(path.as_str(), |path| path); + let path = path.strip_suffix(".git").map_or(path, |path| path); + Some(GitRemote::new( + Hostname::new(host), + RepoPath::new(path.to_string()), + )) + } + + fn fetch(&self) -> Result<(), fetch::Error> { + let Ok(repository) = self.0.lock() else { + return Err(fetch::Error::Lock); + }; + let Some(Ok(remote)) = repository.find_default_remote(Direction::Fetch.into()) else { + return Err(fetch::Error::NoFetchRemoteFound); + }; + remote + .connect(gix::remote::Direction::Fetch) + .map_err(|e| fetch::Error::Connect(e.to_string()))? + .prepare_fetch(gix::progress::Discard, Default::default()) + .map_err(|e| fetch::Error::Fetch(e.to_string()))? + .receive(gix::progress::Discard, &Default::default()) + .map_err(|e| fetch::Error::Fetch(e.to_string()))?; + + Ok(()) + } + + // TODO: (#72) reimplement using `gix` + fn push( + &self, + repo_details: &RepoDetails, + branch_name: BranchName, + to_commit: GitRef, + force: push::Force, + ) -> Result<(), push::Error> { + let origin = repo_details.origin(); + let force = match force { + push::Force::No => "".to_string(), + push::Force::From(old_ref) => format!("--force-with-lease={branch_name}:{old_ref}"), + }; + // INFO: never log the command as it contains the API token within the 'origin' + use secrecy::ExposeSecret; + let command: secrecy::Secret = format!( + "/usr/bin/git push {} {to_commit}:{branch_name} {force}", + origin.expose_secret() + ) + .into(); + + let git_dir = self + .0 + .lock() + .map_err(|_| push::Error::Lock) + .map(|r| r.git_dir().to_path_buf())?; + + let ctx = gix::diff::command::Context { + git_dir: Some(git_dir.clone()), + ..Default::default() + }; + match gix::command::prepare(command.expose_secret()) + .with_context(ctx) + .with_shell_allow_argument_splitting() + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .spawn() + { + Ok(mut child) => match child.wait() { + Ok(_) => { + info!("Branch updated"); + Ok(()) + } + Err(err) => { + warn!(?err, ?git_dir, "Failed (wait)"); + Err(push::Error::Push) + } + }, + Err(err) => { + warn!(?err, ?git_dir, "Failed (spawn)"); + Err(push::Error::Push) + } + } + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Direction { + /// Push local changes to the remote. + Push, + /// Fetch changes from the remote to the local repository. + Fetch, +} +impl From for gix::remote::Direction { + fn from(value: Direction) -> Self { + match value { + Direction::Push => Self::Push, + Direction::Fetch => Self::Fetch, + } } } diff --git a/crates/git/src/validate.rs b/crates/git/src/validate.rs index e86fb80..23fa7ad 100644 --- a/crates/git/src/validate.rs +++ b/crates/git/src/validate.rs @@ -1,16 +1,18 @@ -use std::ops::Deref as _; - -use git_next_config::{Hostname, RepoPath}; -use gix::remote::Direction; use tracing::info; -use super::{GitRemote, RepoDetails, Repository}; +use crate::repository::{open::OpenRepository, Direction}; + +use super::{GitRemote, RepoDetails}; #[tracing::instrument(skip_all)] -pub fn validate(repository: &Repository, repo_details: &RepoDetails) -> Result<()> { +pub fn validate(repository: &OpenRepository, repo_details: &RepoDetails) -> Result<()> { let git_remote = repo_details.git_remote(); - let push_remote = find_default_remote(repository, Direction::Push)?; - let fetch_remote = find_default_remote(repository, Direction::Fetch)?; + let Some(push_remote) = repository.find_default_remote(Direction::Push) else { + return Err(Error::NoDefaultPushRemote); + }; + let Some(fetch_remote) = repository.find_default_remote(Direction::Fetch) else { + return Err(Error::NoDefaultFetchRemote); + }; info!(config = %git_remote, push = %push_remote, fetch = %fetch_remote, "Check remotes match"); if git_remote != push_remote { return Err(Error::MismatchDefaultPushRemote { @@ -27,35 +29,11 @@ pub fn validate(repository: &Repository, repo_details: &RepoDetails) -> Result<( Ok(()) } -#[tracing::instrument(skip_all, fields(?direction))] -pub fn find_default_remote( - repository: &Repository, - direction: gix::remote::Direction, -) -> Result { - let repository = repository.deref().to_thread_local(); - let Some(Ok(remote)) = repository.find_default_remote(gix::remote::Direction::Push) else { - return Err(Error::NoDefaultPushRemote); - }; - let Some(url) = remote.url(direction) else { - return Err(Error::NoUrlForDefaultPushRemote); - }; - let Some(host) = url.host() else { - return Err(Error::NoHostnameForDefaultPushRemote); - }; - let path = url.path.to_string(); - let path = path.strip_prefix('/').map_or(path.as_str(), |path| path); - let path = path.strip_suffix(".git").map_or(path, |path| path); - info!(%host, %path, "found"); - Ok(GitRemote::new( - Hostname::new(host), - RepoPath::new(path.to_string()), - )) -} - type Result = core::result::Result; #[derive(Debug, derive_more::Display)] pub enum Error { NoDefaultPushRemote, + NoDefaultFetchRemote, NoUrlForDefaultPushRemote, NoHostnameForDefaultPushRemote, UnableToOpenRepo(String), diff --git a/crates/server/src/actors/repo/branch.rs b/crates/server/src/actors/repo/branch.rs index 0455e44..5c79eb5 100644 --- a/crates/server/src/actors/repo/branch.rs +++ b/crates/server/src/actors/repo/branch.rs @@ -18,7 +18,7 @@ pub async fn advance_next( dev_commit_history: Vec, repo_config: RepoConfig, forge: gitforge::Forge, - repository: git::Repository, + repository: git::OpenRepository, addr: Addr, message_token: super::MessageToken, ) { @@ -82,7 +82,7 @@ pub async fn advance_main( next: git::Commit, repo_config: RepoConfig, forge: gitforge::Forge, - repository: git::Repository, + repository: git::OpenRepository, addr: Addr, message_token: super::MessageToken, ) { diff --git a/crates/server/src/actors/repo/mod.rs b/crates/server/src/actors/repo/mod.rs index e6689f7..40ee20d 100644 --- a/crates/server/src/actors/repo/mod.rs +++ b/crates/server/src/actors/repo/mod.rs @@ -7,6 +7,7 @@ pub mod webhook; mod tests; use actix::prelude::*; +use git::OpenRepository; use git_next_config::{ForgeType, RepoConfig}; use git_next_git::{self as git, Generation, RepoDetails}; use kxio::network::Network; @@ -28,7 +29,7 @@ pub struct RepoActor { last_main_commit: Option, last_next_commit: Option, last_dev_commit: Option, - repository: Option, + repository: Option, net: Network, forge: gitforge::Forge, } @@ -38,10 +39,11 @@ impl RepoActor { webhook: Webhook, generation: Generation, net: Network, + repo: git::Repository, ) -> Self { let forge = match details.forge.forge_type() { #[cfg(feature = "forgejo")] - ForgeType::ForgeJo => gitforge::Forge::new_forgejo(details.clone(), net.clone()), + ForgeType::ForgeJo => gitforge::Forge::new_forgejo(details.clone(), net.clone(), repo), ForgeType::MockForge => gitforge::Forge::new_mock(), }; debug!(?forge, "new"); diff --git a/crates/server/src/actors/server.rs b/crates/server/src/actors/server.rs index 0de8edb..c111bf8 100644 --- a/crates/server/src/actors/server.rs +++ b/crates/server/src/actors/server.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use actix::prelude::*; use git_next_config::{ForgeConfig, ForgeName, GitDir, RepoAlias, ServerRepoConfig}; -use git_next_git::{Generation, RepoDetails}; +use git_next_git::{Generation, RepoDetails, Repository}; use kxio::{fs::FileSystem, network::Network}; use tracing::{error, info, warn}; @@ -37,6 +37,7 @@ pub struct Server { webhook: Option>, fs: FileSystem, net: Network, + repo: Repository, } impl Actor for Server { type Context = Context; @@ -110,13 +111,14 @@ impl Handler for Server { } } impl Server { - pub fn new(fs: FileSystem, net: Network) -> Self { + pub fn new(fs: FileSystem, net: Network, repo: Repository) -> Self { let generation = Generation::new(); Self { generation, webhook: None, fs, net, + repo, } } fn create_forge_data_directories( @@ -175,6 +177,7 @@ impl Server { let server_storage = server_storage.clone(); let webhook = webhook.clone(); let net = self.net.clone(); + let repo = self.repo; let generation = self.generation; move |(repo_alias, server_repo_config)| { let span = tracing::info_span!("create_actor", alias = %repo_alias, config = %server_repo_config); @@ -202,7 +205,8 @@ impl Server { gitdir, ); info!("Starting Repo Actor"); - let actor = RepoActor::new(repo_details, webhook.clone(), generation, net.clone()); + let actor = + RepoActor::new(repo_details, webhook.clone(), generation, net.clone(), repo); (forge_name.clone(), repo_alias, actor) } } diff --git a/crates/server/src/config/tests.rs b/crates/server/src/config/tests.rs index de5572b..56ca7e1 100644 --- a/crates/server/src/config/tests.rs +++ b/crates/server/src/config/tests.rs @@ -1,12 +1,12 @@ use std::collections::BTreeMap; use assert2::let_assert; +use git::repository::Direction; use git_next_config::{ ForgeType, GitDir, Hostname, RepoBranches, RepoConfig, RepoConfigSource, RepoPath, ServerRepoConfig, }; -use git_next_git::{self as git, Generation, GitRemote, Repository}; -use gix::remote::Direction; +use git_next_git::{self as git, Generation, GitRemote}; use kxio::fs; use pretty_assertions::assert_eq; @@ -179,8 +179,11 @@ fn repo_details_find_default_push_remote_finds_correct_remote() -> Result<()> { .with_hostname(Hostname::new("git.kemitix.net")); repo_details.repo_path = RepoPath::new("kemitix/git-next".to_string()); let gitdir = &repo_details.gitdir; - let repository = Repository::open(gitdir)?; - let found_git_remote = git::validate::find_default_remote(&repository, Direction::Push)?; + let repository = git::repository::new().open(gitdir)?; + let_assert!( + Some(found_git_remote) = repository.find_default_remote(Direction::Push), + "Default Push Remote not found" + ); let config_git_remote = repo_details.git_remote(); assert_eq!( @@ -207,7 +210,7 @@ fn gitdir_validate_should_pass_a_valid_git_repo() -> Result<()> { .with_hostname(Hostname::new("git.kemitix.net")); repo_details.repo_path = RepoPath::new("kemitix/git-next".to_string()); let gitdir = &repo_details.gitdir; - let repository = Repository::open(gitdir)?; + let repository = git::repository::new().open(gitdir)?; git::validate(&repository, &repo_details)?; Ok(()) @@ -226,7 +229,7 @@ fn gitdir_validate_should_fail_a_git_repo_with_wrong_remote() -> Result<()> { ); repo_details.repo_path = RepoPath::new("hello/world".to_string()); let gitdir = &repo_details.gitdir; - let repository = Repository::open(gitdir)?; + let repository = git::repository::new().open(gitdir)?; let_assert!(Err(_) = git::validate(&repository, &repo_details)); Ok(()) diff --git a/crates/server/src/gitforge/forgejo/branch/validate_positions.rs b/crates/server/src/gitforge/forgejo/branch/validate_positions.rs index baca461..5ad5ba5 100644 --- a/crates/server/src/gitforge/forgejo/branch/validate_positions.rs +++ b/crates/server/src/gitforge/forgejo/branch/validate_positions.rs @@ -28,7 +28,7 @@ pub struct ValidatedPositions { pub async fn validate_positions( forge: &gitforge::forgejo::ForgeJoEnv, - repository: &git::Repository, + repository: &git::OpenRepository, repo_config: RepoConfig, ) -> Result { let repo_details = &forge.repo_details; diff --git a/crates/server/src/gitforge/forgejo/mod.rs b/crates/server/src/gitforge/forgejo/mod.rs index ef7a7fa..870aa5e 100644 --- a/crates/server/src/gitforge/forgejo/mod.rs +++ b/crates/server/src/gitforge/forgejo/mod.rs @@ -5,6 +5,7 @@ use std::time::Duration; use actix::prelude::*; +use git::OpenRepository; use git_next_config::{BranchName, GitDir, RepoConfig}; use git_next_git::{self as git, GitRef, RepoDetails, Repository}; use kxio::network::{self, Network}; @@ -21,10 +22,15 @@ struct ForgeJo; pub struct ForgeJoEnv { repo_details: RepoDetails, net: Network, + repo: Repository, } impl ForgeJoEnv { - pub(super) const fn new(repo_details: RepoDetails, net: Network) -> Self { - Self { repo_details, net } + pub(super) const fn new(repo_details: RepoDetails, net: Network, repo: Repository) -> Self { + Self { + repo_details, + net, + repo, + } } } #[async_trait::async_trait] @@ -47,7 +53,7 @@ impl super::ForgeLike for ForgeJoEnv { async fn branches_validate_positions( &self, - repository: git::Repository, + repository: git::OpenRepository, repo_config: RepoConfig, addr: Addr, message_token: MessageToken, @@ -71,19 +77,13 @@ impl super::ForgeLike for ForgeJoEnv { fn branch_reset( &self, - repository: &git::Repository, + repository: &git::OpenRepository, branch_name: BranchName, to_commit: GitRef, force: git::push::Force, ) -> git::push::Result { - git::fetch(repository, &self.repo_details)?; - git::push( - repository, - &self.repo_details, - branch_name, - to_commit, - force, - ) + repository.fetch()?; + repository.push(&self.repo_details, branch_name, to_commit, force) } async fn commit_status(&self, commit: &git::Commit) -> gitforge::CommitStatus { @@ -130,12 +130,12 @@ impl super::ForgeLike for ForgeJoEnv { } } - fn repo_clone(&self, gitdir: GitDir) -> Result { + fn repo_clone(&self, gitdir: GitDir) -> Result { let repository = if !gitdir.exists() { info!("Local copy not found - cloning..."); - Repository::clone(&self.repo_details)? + self.repo.git_clone(&self.repo_details)? } else { - Repository::open(gitdir)? + self.repo.open(&gitdir)? }; info!("Validating..."); git::validate(&repository, &self.repo_details) diff --git a/crates/server/src/gitforge/mock_forge.rs b/crates/server/src/gitforge/mock_forge.rs index 830e85e..ce2014b 100644 --- a/crates/server/src/gitforge/mock_forge.rs +++ b/crates/server/src/gitforge/mock_forge.rs @@ -1,5 +1,6 @@ +use git::OpenRepository; use git_next_config::{BranchName, GitDir, RepoConfig}; -use git_next_git::{self as git, GitRef, Repository}; +use git_next_git::{self as git, GitRef}; use crate::{actors::repo::RepoActor, gitforge, types::MessageToken}; @@ -31,7 +32,7 @@ impl super::ForgeLike for MockForgeEnv { async fn branches_validate_positions( &self, - _repository: Repository, + _repository: OpenRepository, _repo_config: RepoConfig, _addr: actix::prelude::Addr, _message_token: MessageToken, @@ -41,7 +42,7 @@ impl super::ForgeLike for MockForgeEnv { fn branch_reset( &self, - _repository: &Repository, + _repository: &OpenRepository, _branch_name: BranchName, _to_commit: GitRef, _force: git::push::Force, @@ -53,7 +54,7 @@ impl super::ForgeLike for MockForgeEnv { todo!() } - fn repo_clone(&self, _gitdir: GitDir) -> Result { + fn repo_clone(&self, _gitdir: GitDir) -> Result { todo!() } } diff --git a/crates/server/src/gitforge/mod.rs b/crates/server/src/gitforge/mod.rs index 824f84f..49fba9d 100644 --- a/crates/server/src/gitforge/mod.rs +++ b/crates/server/src/gitforge/mod.rs @@ -1,7 +1,8 @@ #![allow(dead_code)] +use git::OpenRepository; use git_next_config::{BranchName, GitDir, RepoConfig}; -use git_next_git::{self as git, GitRef, RepoDetails, Repository}; +use git_next_git::{self as git, GitRef, RepoDetails}; use kxio::network::Network; #[cfg(feature = "forgejo")] @@ -38,7 +39,7 @@ pub trait ForgeLike { /// positions as needed. async fn branches_validate_positions( &self, - repository: Repository, + repository: OpenRepository, repo_config: RepoConfig, addr: actix::prelude::Addr, message_token: MessageToken, @@ -47,7 +48,7 @@ pub trait ForgeLike { /// Moves a branch to a new commit. fn branch_reset( &self, - repository: &Repository, + repository: &OpenRepository, branch_name: BranchName, to_commit: GitRef, force: git::push::Force, @@ -57,7 +58,7 @@ pub trait ForgeLike { async fn commit_status(&self, commit: &git::Commit) -> CommitStatus; /// Clones a repo to disk. - fn repo_clone(&self, gitdir: GitDir) -> Result; + fn repo_clone(&self, gitdir: GitDir) -> Result; } #[derive(Clone, Debug)] @@ -74,8 +75,12 @@ impl Forge { Self::Mock(mock_forge::MockForgeEnv::new()) } #[cfg(feature = "forgejo")] - pub const fn new_forgejo(repo_details: RepoDetails, net: Network) -> Self { - Self::ForgeJo(forgejo::ForgeJoEnv::new(repo_details, net)) + pub const fn new_forgejo( + repo_details: RepoDetails, + net: Network, + repo: git::Repository, + ) -> Self { + Self::ForgeJo(forgejo::ForgeJoEnv::new(repo_details, net, repo)) } #[cfg(feature = "github")] pub const fn new_github(net: Network) -> Self { diff --git a/crates/server/src/gitforge/tests/forgejo.rs b/crates/server/src/gitforge/tests/forgejo.rs index fe2b628..9314445 100644 --- a/crates/server/src/gitforge/tests/forgejo.rs +++ b/crates/server/src/gitforge/tests/forgejo.rs @@ -13,6 +13,7 @@ fn test_name() { panic!("fs") }; let net = Network::new_mock(); + let repo = git::repository::mock(); let repo_details = common::repo_details( 1, Generation::new(), @@ -20,7 +21,7 @@ fn test_name() { Some(common::repo_config(1, RepoConfigSource::Repo)), GitDir::new(fs.base()), ); - let forge = Forge::new_forgejo(repo_details, net); + let forge = Forge::new_forgejo(repo_details, net, repo); assert_eq!(forge.name(), "forgejo"); } @@ -39,6 +40,7 @@ async fn test_branches_get() { let body = include_str!("./data-forgejo-branches-get.json"); net.add_get_response(&url, StatusCode::OK, body); let net = Network::from(net); + let repo = git::repository::mock(); let repo_details = common::repo_details( 1, @@ -48,7 +50,7 @@ async fn test_branches_get() { GitDir::new(fs.base()), ); - let forge = Forge::new_forgejo(repo_details, net.clone()); + let forge = Forge::new_forgejo(repo_details, net.clone(), repo); let_assert!(Ok(branches) = forge.branches_get_all().await); diff --git a/crates/server/src/lib.rs b/crates/server/src/lib.rs index 3dc0a72..80f5a3c 100644 --- a/crates/server/src/lib.rs +++ b/crates/server/src/lib.rs @@ -5,6 +5,7 @@ pub mod types; use actix::prelude::*; +use git_next_git::Repository; use kxio::{fs::FileSystem, network::Network}; use std::path::PathBuf; @@ -38,11 +39,11 @@ pub fn init(fs: FileSystem) { } } -pub async fn start(fs: FileSystem, net: Network) { +pub async fn start(fs: FileSystem, net: Network, repo: Repository) { init_logging(); info!("Starting Server..."); - let server = Server::new(fs.clone(), net.clone()).start(); + let server = Server::new(fs.clone(), net.clone(), repo).start(); server.do_send(FileUpdated); info!("Starting File Watcher...");