mod branch; mod file; mod repo; use actix::prelude::*; use kxio::network::{self, Network}; use tracing::{error, info, warn}; use crate::server::{ actors::repo::RepoActor, config::{BranchName, GitDir, RepoConfig, RepoDetails}, gitforge::{self, RepoCloneError}, types::GitRef, }; struct ForgeJo; #[derive(Clone)] pub struct ForgeJoEnv { repo_details: RepoDetails, net: Network, } impl ForgeJoEnv { pub(super) const fn new(repo_details: RepoDetails, net: Network) -> Self { Self { repo_details, net } } } #[async_trait::async_trait] impl super::ForgeLike for ForgeJoEnv { fn name(&self) -> String { "forgejo".to_string() } async fn branches_get_all(&self) -> Result, gitforge::ForgeBranchError> { branch::get_all(&self.repo_details, &self.net).await } async fn file_contents_get( &self, branch: &BranchName, file_path: &str, ) -> Result { file::contents_get(&self.repo_details, &self.net, branch, file_path).await } async fn branches_validate_positions(&self, repo_config: RepoConfig, addr: Addr) { branch::validate_positions(self, repo_config, addr).await } fn branch_reset( &self, branch_name: BranchName, to_commit: GitRef, force: gitforge::Force, ) -> gitforge::BranchResetResult { branch::reset(&self.repo_details, branch_name, to_commit, force) } async fn commit_status(&self, commit: &gitforge::Commit) -> gitforge::CommitStatus { let repo_details = &self.repo_details; let hostname = &repo_details.forge.hostname; let repo_path = &repo_details.repo_path; let api_token = &repo_details.forge.token; use secrecy::ExposeSecret; let token = api_token.expose_secret(); let url = network::NetUrl::new(format!( "https://{hostname}/api/v1/repos/{repo_path}/commits/{commit}/status?token={token}" )); let request = network::NetRequest::new( network::RequestMethod::Get, url, network::NetRequestHeaders::new(), network::RequestBody::None, network::ResponseType::Json, None, network::NetRequestLogging::None, ); let result = self.net.get::(request).await; match result { Ok(response) => { match response.response_body() { Some(status) => match status.state { CommitStatusState::Success => gitforge::CommitStatus::Pass, CommitStatusState::Pending => gitforge::CommitStatus::Pending, CommitStatusState::Failure => gitforge::CommitStatus::Fail, CommitStatusState::Error => gitforge::CommitStatus::Fail, CommitStatusState::Blank => gitforge::CommitStatus::Pending, }, None => { warn!("No status found for commit"); gitforge::CommitStatus::Pending // assume issue is transient and allow retry } } } Err(e) => { error!(?e, "Failed to get commit status"); gitforge::CommitStatus::Pending // assume issue is transient and allow retry } } } fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError> { if gitdir.exists() { info!(?gitdir, "Gitdir already exists - validating..."); gitdir .validate(&self.repo_details) .map_err(|e| RepoCloneError::Validation(e.to_string())) .inspect(|_| info!(?gitdir, "Validation - OK")) } else { info!(?gitdir, "Gitdir doesn't exists - cloning..."); repo::clone(&self.repo_details, gitdir).inspect(|_| info!("Cloned - OK")) } } } #[derive(Debug, serde::Deserialize)] pub struct CombinedStatus { pub state: CommitStatusState, } #[derive(Debug, serde::Deserialize)] pub enum CommitStatusState { #[serde(rename = "success")] Success, #[serde(rename = "pending")] Pending, #[serde(rename = "failure")] Failure, #[serde(rename = "error")] Error, #[serde(rename = "")] Blank, }