2024-05-03 17:50:50 +01:00
|
|
|
pub mod branch;
|
2024-04-16 22:21:55 +01:00
|
|
|
mod file;
|
2024-04-19 18:56:42 +01:00
|
|
|
|
2024-05-04 12:37:35 +01:00
|
|
|
use std::time::Duration;
|
|
|
|
|
2024-04-16 22:21:55 +01:00
|
|
|
use actix::prelude::*;
|
|
|
|
|
|
|
|
use kxio::network::{self, Network};
|
2024-04-27 15:23:42 +01:00
|
|
|
use tracing::{error, info, warn};
|
2024-04-16 22:21:55 +01:00
|
|
|
|
|
|
|
use crate::server::{
|
2024-05-04 12:37:35 +01:00
|
|
|
actors::repo::{RepoActor, StartMonitoring, ValidateRepo},
|
2024-04-21 18:47:07 +01:00
|
|
|
config::{BranchName, GitDir, RepoConfig, RepoDetails},
|
2024-05-08 17:59:47 +01:00
|
|
|
git,
|
2024-05-09 21:18:40 +01:00
|
|
|
gitforge::{self, forgejo::branch::ValidatedPositions, RepoCloneError, Repository},
|
2024-05-05 08:17:32 +01:00
|
|
|
types::{GitRef, MessageToken},
|
2024-04-16 22:21:55 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ForgeJo;
|
2024-05-04 12:37:35 +01:00
|
|
|
#[derive(Clone, Debug)]
|
2024-04-16 22:21:55 +01:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2024-04-18 19:15:26 +01:00
|
|
|
async fn branches_get_all(&self) -> Result<Vec<super::Branch>, gitforge::ForgeBranchError> {
|
2024-04-16 22:21:55 +01:00
|
|
|
branch::get_all(&self.repo_details, &self.net).await
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn file_contents_get(
|
|
|
|
&self,
|
|
|
|
branch: &BranchName,
|
|
|
|
file_path: &str,
|
2024-04-18 19:15:26 +01:00
|
|
|
) -> Result<String, gitforge::ForgeFileError> {
|
2024-04-16 22:21:55 +01:00
|
|
|
file::contents_get(&self.repo_details, &self.net, branch, file_path).await
|
|
|
|
}
|
|
|
|
|
2024-05-05 08:17:32 +01:00
|
|
|
async fn branches_validate_positions(
|
|
|
|
&self,
|
2024-05-09 21:53:50 +01:00
|
|
|
repository: Repository,
|
2024-05-05 08:17:32 +01:00
|
|
|
repo_config: RepoConfig,
|
|
|
|
addr: Addr<RepoActor>,
|
|
|
|
message_token: MessageToken,
|
|
|
|
) {
|
2024-05-09 21:53:50 +01:00
|
|
|
match branch::validate_positions(self, &repository, repo_config).await {
|
2024-05-04 12:37:35 +01:00
|
|
|
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;
|
2024-05-05 08:17:32 +01:00
|
|
|
addr.do_send(ValidateRepo::new(message_token));
|
2024-05-04 12:37:35 +01:00
|
|
|
}
|
|
|
|
}
|
2024-04-16 22:21:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn branch_reset(
|
|
|
|
&self,
|
2024-05-09 21:53:50 +01:00
|
|
|
repository: &Repository,
|
2024-04-16 22:21:55 +01:00
|
|
|
branch_name: BranchName,
|
|
|
|
to_commit: GitRef,
|
2024-04-18 19:15:26 +01:00
|
|
|
force: gitforge::Force,
|
|
|
|
) -> gitforge::BranchResetResult {
|
2024-05-09 21:53:50 +01:00
|
|
|
branch::fetch(repository, &self.repo_details)?;
|
|
|
|
git::reset(
|
|
|
|
repository,
|
|
|
|
&self.repo_details,
|
|
|
|
branch_name,
|
|
|
|
to_commit,
|
|
|
|
force,
|
|
|
|
)
|
2024-04-16 22:21:55 +01:00
|
|
|
}
|
|
|
|
|
2024-04-18 19:15:26 +01:00
|
|
|
async fn commit_status(&self, commit: &gitforge::Commit) -> gitforge::CommitStatus {
|
2024-04-16 22:21:55 +01:00
|
|
|
let repo_details = &self.repo_details;
|
|
|
|
let hostname = &repo_details.forge.hostname;
|
2024-04-20 20:49:38 +01:00
|
|
|
let repo_path = &repo_details.repo_path;
|
2024-04-16 22:21:55 +01:00
|
|
|
let api_token = &repo_details.forge.token;
|
|
|
|
use secrecy::ExposeSecret;
|
|
|
|
let token = api_token.expose_secret();
|
|
|
|
let url = network::NetUrl::new(format!(
|
2024-04-20 20:49:38 +01:00
|
|
|
"https://{hostname}/api/v1/repos/{repo_path}/commits/{commit}/status?token={token}"
|
2024-04-16 22:21:55 +01:00
|
|
|
));
|
|
|
|
|
|
|
|
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 {
|
2024-04-18 19:15:26 +01:00
|
|
|
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,
|
2024-04-16 22:21:55 +01:00
|
|
|
},
|
|
|
|
None => {
|
|
|
|
warn!("No status found for commit");
|
2024-04-18 19:15:26 +01:00
|
|
|
gitforge::CommitStatus::Pending // assume issue is transient and allow retry
|
2024-04-16 22:21:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
error!(?e, "Failed to get commit status");
|
2024-04-18 19:15:26 +01:00
|
|
|
gitforge::CommitStatus::Pending // assume issue is transient and allow retry
|
2024-04-16 22:21:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-19 18:56:42 +01:00
|
|
|
|
2024-05-09 21:18:40 +01:00
|
|
|
fn repo_clone(&self, gitdir: GitDir) -> Result<Repository, RepoCloneError> {
|
2024-05-09 21:53:50 +01:00
|
|
|
let repository = if !gitdir.exists() {
|
2024-05-04 12:37:35 +01:00
|
|
|
info!("Local copy not found - cloning...");
|
2024-05-09 22:23:57 +01:00
|
|
|
Repository::clone(&self.repo_details)?
|
2024-05-09 21:18:40 +01:00
|
|
|
} else {
|
2024-05-09 22:23:57 +01:00
|
|
|
Repository::open(gitdir.clone())?
|
2024-05-09 21:18:40 +01:00
|
|
|
};
|
2024-05-04 12:37:35 +01:00
|
|
|
info!("Validating...");
|
|
|
|
gitdir
|
2024-05-09 21:53:50 +01:00
|
|
|
.validate(&repository, &self.repo_details)
|
2024-05-04 12:37:35 +01:00
|
|
|
.map_err(|e| RepoCloneError::Validation(e.to_string()))
|
2024-05-09 21:18:40 +01:00
|
|
|
.inspect(|_| info!("Validation - OK"))?;
|
2024-05-09 21:53:50 +01:00
|
|
|
Ok(repository)
|
2024-04-19 18:56:42 +01:00
|
|
|
}
|
2024-04-16 22:21:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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,
|
|
|
|
}
|