d3dfedc95b
All checks were successful
Rust / build (map[name:nightly]) (push) Successful in 12m43s
Rust / build (map[name:stable]) (push) Successful in 18m7s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 7m43s
115 lines
3.4 KiB
Rust
115 lines
3.4 KiB
Rust
//
|
|
#[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,
|
|
};
|
|
|
|
use kxio::net::Net;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ForgeJo {
|
|
repo_details: git::RepoDetails,
|
|
net: Net,
|
|
}
|
|
impl ForgeJo {
|
|
#[must_use]
|
|
pub const fn new(repo_details: git::RepoDetails, net: Net) -> Self {
|
|
Self { repo_details, net }
|
|
}
|
|
}
|
|
#[async_trait::async_trait]
|
|
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,
|
|
}
|