diff --git a/crates/forge-forgejo/src/lib.rs b/crates/forge-forgejo/src/lib.rs index e194aeab..692e5eb9 100644 --- a/crates/forge-forgejo/src/lib.rs +++ b/crates/forge-forgejo/src/lib.rs @@ -1,9 +1,5 @@ pub mod branch; -#[cfg(test)] -mod tests; - -use git_next_config as config; use git_next_git as git; use kxio::network::{self, Network}; @@ -25,10 +21,6 @@ impl git::ForgeLike for ForgeJo { "forgejo".to_string() } - async fn branches_get_all(&self) -> Result, git::branch::Error> { - branch::get_all(&self.repo_details, &self.net).await - } - async fn commit_status(&self, commit: &git::Commit) -> git::commit::Status { let repo_details = &self.repo_details; let hostname = &repo_details.forge.hostname(); diff --git a/crates/forge-forgejo/src/tests/data-forgejo-branches-get.json b/crates/forge-forgejo/src/tests/data-forgejo-branches-get.json deleted file mode 100644 index bc1e2ae9..00000000 --- a/crates/forge-forgejo/src/tests/data-forgejo-branches-get.json +++ /dev/null @@ -1,50 +0,0 @@ -[ - { - "commit": { - "added": [ - "string" - ], - "author": { - "email": "user@example.com", - "name": "string", - "username": "string" - }, - "committer": { - "email": "user@example.com", - "name": "string", - "username": "string" - }, - "id": "string", - "message": "string", - "modified": [ - "string" - ], - "removed": [ - "string" - ], - "timestamp": "2024-04-16T21:35:56.331Z", - "url": "string", - "verification": { - "payload": "string", - "reason": "string", - "signature": "string", - "signer": { - "email": "user@example.com", - "name": "string", - "username": "string" - }, - "verified": true - } - }, - "effective_branch_protection_name": "string", - "enable_status_check": true, - "name": "string", - "protected": true, - "required_approvals": 0, - "status_check_contexts": [ - "string" - ], - "user_can_merge": true, - "user_can_push": true - } -] diff --git a/crates/forge-forgejo/src/tests/mod.rs b/crates/forge-forgejo/src/tests/mod.rs deleted file mode 100644 index b5b02d71..00000000 --- a/crates/forge-forgejo/src/tests/mod.rs +++ /dev/null @@ -1,102 +0,0 @@ -// -use assert2::let_assert; -use kxio::network::{MockNetwork, Network, StatusCode}; -use std::path::Path; - -use crate as forgejo; -use git::ForgeLike as _; -use git_next_config as config; -use git_next_git as git; - -mod branch { - use super::*; - mod get_all { - - use super::*; - - #[tokio::test] - async fn test_branches_get() { - let_assert!(Ok(fs) = kxio::fs::temp()); - let mut net = MockNetwork::new(); - //given - given_forgejo_has_branches(&mut net, 1); - let repo_details = given_repo_details(fs.base(), 1); - - let net = Network::from(net); - let forge = forgejo::ForgeJo::new(repo_details, net.clone()); - - //when - let_assert!(Ok(branches) = forge.branches_get_all().await); - - //then - let_assert!(Some(requests) = net.mocked_requests()); - assert_eq!(requests.len(), 1); - assert_eq!(branches, vec![config::BranchName::new("string")]); - } - } - // mod validate_positions { - // - // use git::ForgeLike; - // - // use super::*; - // - // #[test] - // fn should_ok_all_branches_on_same_commit() { - // let_assert!(Ok(fs) = kxio::fs::temp()); - // let mut net = MockNetwork::new(); - // //given - // let repo_details = given_repo_details(fs.base(), 1); - // let net = Network::from(net); - // let (repo, _reality) = git::repository::mock(); - // let forge = given_a_forge(repo_details, &net, repo); - // let open_repository = given_an_open_repository(); - // let repo_config = given_a_repo_config(); - // - // let forge = forgejo::ForgeJo::new(repo_details, net.clone(), repo); - // - // let_assert!( - // Ok(positions) = forge - // .branches_validate_positions(open_repository, repo_config) - // .await - // ); - // } - // - // fn given_a_forge( - // repo_details: git::RepoDetails, - // net: &Network, - // repo: git::Repository, - // ) -> forgejo::ForgeJo { - // forgejo::ForgeJo::new(repo_details, net.clone(), repo) - // } - // fn given_an_open_repository() -> git::OpenRepository { - // todo!() - // } - // fn given_a_repo_config() -> config::RepoConfig { - // todo!() - // } - // } -} - -fn given_forgejo_has_branches(net: &mut MockNetwork, i: u32) { - let hostname = config::common::hostname(i); - let path = config::common::repo_path(i); - let api_token = config::common::api_token(i); - use secrecy::ExposeSecret; - let token = api_token.expose_secret(); - let url = format!("https://{hostname}/api/v1/repos/{path}/branches?token={token}"); - let body = include_str!("./data-forgejo-branches-get.json"); - net.add_get_response(&url, StatusCode::OK, body); -} - -fn given_repo_details(path: &Path, i: u32) -> git::RepoDetails { - git::common::repo_details( - i, - git::Generation::new(), - config::common::forge_details(i, config::ForgeType::ForgeJo), - Some(config::common::repo_config( - i, - config::RepoConfigSource::Repo, - )), - config::GitDir::new(path), - ) -} diff --git a/crates/forge/src/mock_forge.rs b/crates/forge/src/mock_forge.rs index 35c853bf..a5f211a6 100644 --- a/crates/forge/src/mock_forge.rs +++ b/crates/forge/src/mock_forge.rs @@ -1,7 +1,6 @@ // #![cfg(not(tarpaulin_include))] -use git_next_config as config; use git_next_git as git; struct MockForge; @@ -18,10 +17,6 @@ impl git::ForgeLike for MockForgeEnv { "mock".to_string() } - async fn branches_get_all(&self) -> Result, git::branch::Error> { - todo!() - } - async fn commit_status(&self, _commit: &git::Commit) -> git::commit::Status { todo!() } diff --git a/crates/git/src/branch.rs b/crates/git/src/branch.rs index 01978ba9..f4533e47 100644 --- a/crates/git/src/branch.rs +++ b/crates/git/src/branch.rs @@ -11,6 +11,12 @@ pub enum Error { Fetch(git::fetch::Error), Push(git::push::Error), + + Lock, + + Generic(String), + I1(gix::reference::iter::Error), + I2(gix::reference::iter::init::Error), } impl std::error::Error for Error {} diff --git a/crates/git/src/forge_like.rs b/crates/git/src/forge_like.rs index a8844c70..46dd08b4 100644 --- a/crates/git/src/forge_like.rs +++ b/crates/git/src/forge_like.rs @@ -1,13 +1,9 @@ use crate as git; -use git_next_config as config; #[async_trait::async_trait] pub trait ForgeLike { fn name(&self) -> String; - /// Returns a list of all branches in the repo. - async fn branches_get_all(&self) -> Result, git::branch::Error>; - /// Checks the results of any (e.g. CI) status checks for the commit. async fn commit_status(&self, commit: &git::Commit) -> git::commit::Status; } diff --git a/crates/git/src/repository/mock.rs b/crates/git/src/repository/mock.rs index f4d4f887..da53d191 100644 --- a/crates/git/src/repository/mock.rs +++ b/crates/git/src/repository/mock.rs @@ -15,7 +15,7 @@ use crate::{ }, GitRemote, RepoDetails, }; -use git_next_config::GitDir; +use git_next_config as config; #[derive(Debug, Default, Clone)] pub struct MockRepository(Arc>); @@ -23,7 +23,7 @@ impl MockRepository { pub fn new() -> Self { Self(Arc::new(Mutex::new(Reality::default()))) } - pub fn can_open_repo(&mut self, gitdir: &GitDir) -> Result { + pub fn can_open_repo(&mut self, gitdir: &config::GitDir) -> Result { self.0 .lock() .map_err(|_| Error::MockLock) @@ -31,7 +31,7 @@ impl MockRepository { } fn open_repository( &self, - gitdir: &GitDir, + gitdir: &config::GitDir, ) -> std::result::Result { self.0.lock().map_err(|_| Error::MockLock).and_then(|r| { r.open_repository(gitdir) @@ -47,7 +47,7 @@ impl MockRepository { impl RepositoryLike for MockRepository { fn open( &self, - gitdir: &GitDir, + gitdir: &config::GitDir, ) -> std::result::Result { Ok(OpenRepository::Mock(self.open_repository(gitdir)?)) } @@ -62,10 +62,10 @@ impl RepositoryLike for MockRepository { #[derive(Debug, Default)] pub struct Reality { - openable_repos: HashMap, + openable_repos: HashMap, } impl Reality { - pub fn can_open_repo(&mut self, gitdir: &GitDir) -> MockOpenRepository { + pub fn can_open_repo(&mut self, gitdir: &config::GitDir) -> MockOpenRepository { let mor = self.openable_repos.get(gitdir); match mor { Some(mor) => mor.clone(), @@ -76,7 +76,7 @@ impl Reality { } } } - pub fn open_repository(&self, gitdir: &GitDir) -> Option { + pub fn open_repository(&self, gitdir: &config::GitDir) -> Option { self.openable_repos.get(gitdir).cloned() } } @@ -109,6 +109,12 @@ impl InnerMockOpenRepository { #[allow(clippy::unwrap_used)] impl OpenRepositoryLike for MockOpenRepository { + fn remote_branches(&self) -> git::branch::Result> { + self.inner + .lock() + .map(|inner| inner.remote_branches()) + .unwrap() + } fn find_default_remote(&self, direction: Direction) -> Option { self.inner .lock() @@ -156,6 +162,9 @@ impl OpenRepositoryLike for MockOpenRepository { } } impl OpenRepositoryLike for InnerMockOpenRepository { + fn remote_branches(&self) -> git::branch::Result> { + todo!(); + } fn find_default_remote(&self, direction: Direction) -> Option { match direction { Direction::Push => self.default_push_remote.clone(), diff --git a/crates/git/src/repository/open/mod.rs b/crates/git/src/repository/open/mod.rs index 907b2479..4d1c267f 100644 --- a/crates/git/src/repository/open/mod.rs +++ b/crates/git/src/repository/open/mod.rs @@ -28,6 +28,7 @@ impl OpenRepository { } } pub trait OpenRepositoryLike { + fn remote_branches(&self) -> git::branch::Result>; fn find_default_remote(&self, direction: Direction) -> Option; fn fetch(&self) -> Result<(), git::fetch::Error>; fn push( diff --git a/crates/git/src/repository/open/oreal.rs b/crates/git/src/repository/open/oreal.rs index 033af33e..2f6a5000 100644 --- a/crates/git/src/repository/open/oreal.rs +++ b/crates/git/src/repository/open/oreal.rs @@ -1,5 +1,6 @@ // use crate as git; +use config::BranchName; use git_next_config as config; use gix::bstr::BStr; @@ -9,6 +10,28 @@ use tracing::{info, warn}; #[derive(Clone, Debug, derive_more::Constructor)] pub struct RealOpenRepository(Arc>); impl super::OpenRepositoryLike for RealOpenRepository { + fn remote_branches(&self) -> git::branch::Result> { + let refs = self + .0 + .lock() + .map_err(|_| git::branch::Error::Lock) + .and_then(|repo| { + Ok(repo.references()?).and_then(|refs| { + Ok(refs.remote_branches().map(|rb| { + rb.filter_map(|rbi| rbi.ok()) + .map(|r| r.name().to_owned()) + .map(|n| n.to_string()) + .filter_map(|p| { + p.strip_prefix("refs/remotes/origin/").map(|v| v.to_owned()) + }) + .filter(|b| b.as_str() != "HEAD") + .map(BranchName::new) + .collect::>() + })?) + }) + })?; + Ok(refs) + } fn find_default_remote(&self, direction: git::repository::Direction) -> Option { let Ok(repository) = self.0.lock() else { #[cfg(not(tarpaulin_include))] // don't test mutex lock failure diff --git a/crates/repo-actor/src/lib.rs b/crates/repo-actor/src/lib.rs index e604d3dd..8ae8a087 100644 --- a/crates/repo-actor/src/lib.rs +++ b/crates/repo-actor/src/lib.rs @@ -121,12 +121,11 @@ impl Handler for RepoActor { fn handle(&mut self, _msg: LoadConfigFromRepo, ctx: &mut Self::Context) -> Self::Result { let details = self.repo_details.clone(); let addr = ctx.address(); - let forge = self.forge.clone(); let Some(open_repository) = self.open_repository.clone() else { warn!("missing open repository - can't load configuration"); return; }; - repo_actor::load::load_file(details, addr, forge, open_repository) + repo_actor::load::load_file(details, addr, open_repository) .in_current_span() .into_actor(self) .wait(ctx); diff --git a/crates/repo-actor/src/load.rs b/crates/repo-actor/src/load.rs index c17412b0..e24e14de 100644 --- a/crates/repo-actor/src/load.rs +++ b/crates/repo-actor/src/load.rs @@ -4,7 +4,6 @@ use actix::prelude::*; use tracing::{error, info}; use git_next_config as config; -use git_next_forge as forge; use git_next_git as git; use super::{LoadedConfig, RepoActor}; @@ -14,11 +13,10 @@ use super::{LoadedConfig, RepoActor}; pub async fn load_file( repo_details: git::RepoDetails, addr: Addr, - forge: forge::Forge, open_repository: git::OpenRepository, ) { info!("Loading .git-next.toml from repo"); - let repo_config = match load(&repo_details, &forge, open_repository).await { + let repo_config = match load(&repo_details, &open_repository).await { Ok(repo_config) => repo_config, Err(err) => { error!(?err, "Failed to load config"); @@ -31,12 +29,11 @@ pub async fn load_file( async fn load( details: &git::RepoDetails, - forge: &forge::Forge, - open_repository: git::OpenRepository, + open_repository: &git::OpenRepository, ) -> Result { let contents = open_repository.read_file(&details.branch, ".git-next.toml")?; let config = config::RepoConfig::load(&contents)?; - let config = validate(config, forge).await?; + let config = validate(config, open_repository).await?; Ok(config) } @@ -45,18 +42,15 @@ pub enum Error { File(git::file::Error), Config(config::server::Error), Toml(toml::de::Error), - Forge(git::branch::Error), + Branch(git::branch::Error), BranchNotFound(config::BranchName), } pub async fn validate( config: config::RepoConfig, - forge: &forge::Forge, + open_repository: &git::OpenRepository, ) -> Result { - let branches = forge.branches_get_all().await.map_err(|e| { - error!(?e, "Failed to list branches"); - Error::Forge(e) - })?; + let branches = open_repository.remote_branches()?; if !branches .iter() .any(|branch| branch == &config.branches().main())