git-next/src/server/gitforge/forgejo/mod.rs

160 lines
5 KiB
Rust

pub mod branch;
mod file;
mod repo;
use std::time::Duration;
use actix::prelude::*;
use kxio::network::{self, Network};
use tracing::{error, info, warn};
use crate::server::{
actors::repo::{RepoActor, StartMonitoring, ValidateRepo},
config::{BranchName, GitDir, RepoConfig, RepoDetails},
gitforge::{self, forgejo::branch::ValidatedPositions, RepoCloneError},
types::{GitRef, MessageToken},
};
struct ForgeJo;
#[derive(Clone, Debug)]
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<Vec<super::Branch>, gitforge::ForgeBranchError> {
branch::get_all(&self.repo_details, &self.net).await
}
async fn file_contents_get(
&self,
branch: &BranchName,
file_path: &str,
) -> Result<String, gitforge::ForgeFileError> {
file::contents_get(&self.repo_details, &self.net, branch, file_path).await
}
async fn branches_validate_positions(
&self,
repo_config: RepoConfig,
addr: Addr<RepoActor>,
message_token: MessageToken,
) {
match branch::validate_positions(self, repo_config).await {
Ok(ValidatedPositions {
main,
next,
dev,
dev_commit_history,
}) => {
addr.do_send(StartMonitoring {
main,
next,
dev,
dev_commit_history,
});
}
Err(err) => {
warn!("{}", err);
tokio::time::sleep(Duration::from_secs(10)).await;
addr.do_send(ValidateRepo::new(message_token));
}
}
}
fn branch_reset(
&self,
branch_name: BranchName,
to_commit: GitRef,
force: gitforge::Force,
) -> gitforge::BranchResetResult {
branch::fetch(&self.repo_details)?;
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::<CombinedStatus>(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!("Local copy not found - cloning...");
repo::clone(&self.repo_details, gitdir.clone())?;
}
info!("Validating...");
gitdir
.validate(&self.repo_details)
.map_err(|e| RepoCloneError::Validation(e.to_string()))
.inspect(|_| info!("Validation - 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,
}