git-next/crates/forge-forgejo/src/lib.rs

116 lines
3.4 KiB
Rust
Raw Normal View History

//
#[cfg(test)]
mod tests;
mod webhook;
use std::borrow::ToOwned;
use git_next_core::{
self as core,
git::{self, forge::commit::Status},
server::RepoListenUrl,
ForgeNotification, RegisteredWebhook, WebhookAuth, WebhookId,
};
2024-05-23 16:50:36 +01:00
use kxio::net::Net;
#[derive(Clone, Debug)]
2024-05-23 19:36:05 +01:00
pub struct ForgeJo {
repo_details: git::RepoDetails,
net: Net,
}
2024-05-23 19:36:05 +01:00
impl ForgeJo {
#[must_use]
pub const fn new(repo_details: git::RepoDetails, net: Net) -> Self {
Self { repo_details, net }
}
}
#[async_trait::async_trait]
2024-05-23 19:36:05 +01:00
impl git::ForgeLike for ForgeJo {
fn duplicate(&self) -> Box<dyn git::ForgeLike> {
Box::new(self.clone())
}
fn name(&self) -> String {
"forgejo".to_string()
}
fn is_message_authorised(&self, msg: &ForgeNotification, expected: &WebhookAuth) -> bool {
let authorization = msg.header("authorization");
tracing::info!(?authorization, %expected, "is message authorised?");
authorization
.and_then(|header| header.strip_prefix("Basic ").map(ToOwned::to_owned))
.and_then(|value| WebhookAuth::try_new(value.as_str()).ok())
.is_some_and(|auth| &auth == expected)
}
fn parse_webhook_body(
&self,
body: &core::webhook::forge_notification::Body,
) -> git::forge::webhook::Result<core::webhook::Push> {
webhook::parse_body(body)
}
async fn commit_status(&self, commit: &git::Commit) -> git::forge::webhook::Result<Status> {
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 = format!(
"https://{hostname}/api/v1/repos/{repo_path}/commits/{commit}/status?token={token}"
);
let Ok(response) = self.net.get(url).send().await else {
return Ok(Status::Pending);
};
let combined_status = response.json::<CombinedStatus>().await.unwrap_or_default();
eprintln!("combined_status: {:?}", combined_status);
let status = match combined_status.state {
ForgejoState::Success => Status::Pass,
ForgejoState::Pending | ForgejoState::Blank => Status::Pending,
ForgejoState::Failure | ForgejoState::Error => Status::Fail,
};
Ok(status)
}
async fn list_webhooks(
&self,
repo_listen_url: &RepoListenUrl,
) -> git::forge::webhook::Result<Vec<WebhookId>> {
webhook::list(&self.repo_details, repo_listen_url, &self.net).await
}
async fn unregister_webhook(&self, webhook_id: &WebhookId) -> git::forge::webhook::Result<()> {
webhook::unregister(webhook_id, &self.repo_details, &self.net).await
}
async fn register_webhook(
&self,
repo_listen_url: &RepoListenUrl,
) -> git::forge::webhook::Result<RegisteredWebhook> {
webhook::register(&self.repo_details, repo_listen_url, &self.net).await
}
}
#[derive(Debug, Default, serde::Deserialize)]
struct CombinedStatus {
pub state: ForgejoState,
}
#[derive(Debug, Default, serde::Deserialize)]
enum ForgejoState {
#[serde(rename = "success")]
Success,
#[serde(rename = "pending")]
#[default]
Pending,
#[serde(rename = "failure")]
Failure,
#[serde(rename = "error")]
Error,
#[serde(rename = "")]
Blank,
}