Compare commits
1 commit
c11a6137d9
...
5ffe37707a
Author | SHA1 | Date | |
---|---|---|---|
|
5ffe37707a |
44 changed files with 1082 additions and 1278 deletions
1153
Cargo.lock
generated
1153
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -60,7 +60,7 @@ async-trait = "0.1"
|
||||||
git-url-parse = "0.4"
|
git-url-parse = "0.4"
|
||||||
|
|
||||||
# fs/network
|
# fs/network
|
||||||
kxio = "3.0"
|
kxio = { version = "1.2" }
|
||||||
|
|
||||||
# TOML parsing
|
# TOML parsing
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
|
@ -22,7 +22,7 @@ mod tests;
|
||||||
pub struct AlertsActor {
|
pub struct AlertsActor {
|
||||||
shout: Option<Shout>, // config for sending alerts to users
|
shout: Option<Shout>, // config for sending alerts to users
|
||||||
history: History, // record of alerts sent recently (e.g. 24 hours)
|
history: History, // record of alerts sent recently (e.g. 24 hours)
|
||||||
net: kxio::net::Net,
|
net: kxio::network::Network,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for AlertsActor {
|
impl Actor for AlertsActor {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
//
|
//
|
||||||
use git_next_core::{git::UserNotification, server::OutboundWebhook};
|
use git_next_core::{git::UserNotification, server::OutboundWebhook};
|
||||||
|
use kxio::network::{NetRequest, NetUrl, RequestBody, ResponseType};
|
||||||
use secrecy::ExposeSecret as _;
|
use secrecy::ExposeSecret as _;
|
||||||
use standardwebhooks::Webhook;
|
use standardwebhooks::Webhook;
|
||||||
|
|
||||||
pub(super) async fn send_webhook(
|
pub(super) async fn send_webhook(
|
||||||
user_notification: &UserNotification,
|
user_notification: &UserNotification,
|
||||||
webhook_config: &OutboundWebhook,
|
webhook_config: &OutboundWebhook,
|
||||||
net: &kxio::net::Net,
|
net: &kxio::network::Network,
|
||||||
) {
|
) {
|
||||||
let Ok(webhook) =
|
let Ok(webhook) =
|
||||||
Webhook::from_bytes(webhook_config.secret().expose_secret().as_bytes().into())
|
Webhook::from_bytes(webhook_config.secret().expose_secret().as_bytes().into())
|
||||||
|
@ -21,7 +22,7 @@ async fn do_send_webhook(
|
||||||
user_notification: &UserNotification,
|
user_notification: &UserNotification,
|
||||||
webhook: Webhook,
|
webhook: Webhook,
|
||||||
webhook_config: &OutboundWebhook,
|
webhook_config: &OutboundWebhook,
|
||||||
net: &kxio::net::Net,
|
net: &kxio::network::Network,
|
||||||
) {
|
) {
|
||||||
let message_id = format!("msg_{}", ulid::Ulid::new());
|
let message_id = format!("msg_{}", ulid::Ulid::new());
|
||||||
let timestamp = time::OffsetDateTime::now_utc();
|
let timestamp = time::OffsetDateTime::now_utc();
|
||||||
|
@ -34,17 +35,20 @@ async fn do_send_webhook(
|
||||||
.sign(&message_id, timestamp, payload.to_string().as_ref())
|
.sign(&message_id, timestamp, payload.to_string().as_ref())
|
||||||
.expect("signature");
|
.expect("signature");
|
||||||
tracing::info!(?signature, "");
|
tracing::info!(?signature, "");
|
||||||
net.post(webhook_config.url())
|
let url = webhook_config.url();
|
||||||
.body(payload.to_string())
|
|
||||||
.header("webhook-id", message_id)
|
let net_url = NetUrl::new(url.to_string());
|
||||||
.header("webhook-timestamp", timestamp.to_string())
|
let request = NetRequest::post(net_url)
|
||||||
.header("webhook-signature", signature)
|
.body(RequestBody::Json(payload))
|
||||||
.send()
|
.header("webhook-id", &message_id)
|
||||||
.await
|
.header("webhook-timestamp", ×tamp.to_string())
|
||||||
.map_or_else(
|
.header("webhook-signature", &signature)
|
||||||
|err| {
|
.response_type(ResponseType::None)
|
||||||
tracing::warn!(?err, "sending webhook");
|
.build();
|
||||||
},
|
net.post_json::<()>(request).await.map_or_else(
|
||||||
|_| (),
|
|err| {
|
||||||
);
|
tracing::warn!(?err, "sending webhook");
|
||||||
|
},
|
||||||
|
|_| (),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,13 @@ use git_next_forge_forgejo::ForgeJo;
|
||||||
#[cfg(feature = "github")]
|
#[cfg(feature = "github")]
|
||||||
use git_next_forge_github::Github;
|
use git_next_forge_github::Github;
|
||||||
|
|
||||||
use kxio::net::Net;
|
use kxio::network::Network;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Forge;
|
pub struct Forge;
|
||||||
|
|
||||||
impl Forge {
|
impl Forge {
|
||||||
pub fn create(repo_details: RepoDetails, net: Net) -> Box<dyn ForgeLike> {
|
pub fn create(repo_details: RepoDetails, net: Network) -> Box<dyn ForgeLike> {
|
||||||
match repo_details.forge.forge_type() {
|
match repo_details.forge.forge_type() {
|
||||||
#[cfg(feature = "forgejo")]
|
#[cfg(feature = "forgejo")]
|
||||||
git_next_core::ForgeType::ForgeJo => Box::new(ForgeJo::new(repo_details, net)),
|
git_next_core::ForgeType::ForgeJo => Box::new(ForgeJo::new(repo_details, net)),
|
||||||
|
|
|
@ -11,18 +11,18 @@ use git_next_core::{
|
||||||
#[cfg(feature = "forgejo")]
|
#[cfg(feature = "forgejo")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_forgejo_name() {
|
fn test_forgejo_name() {
|
||||||
let net = kxio::net::mock();
|
let net = Network::new_mock();
|
||||||
let repo_details = given_repo_details(git_next_core::ForgeType::ForgeJo);
|
let repo_details = given_repo_details(git_next_core::ForgeType::ForgeJo);
|
||||||
let forge = Forge::create(repo_details, net.into());
|
let forge = Forge::create(repo_details, net);
|
||||||
assert_eq!(forge.name(), "forgejo");
|
assert_eq!(forge.name(), "forgejo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "github")]
|
#[cfg(feature = "github")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_github_name() {
|
fn test_github_name() {
|
||||||
let net = kxio::net::mock();
|
let net = Network::new_mock();
|
||||||
let repo_details = given_repo_details(git_next_core::ForgeType::GitHub);
|
let repo_details = given_repo_details(git_next_core::ForgeType::GitHub);
|
||||||
let forge = Forge::create(repo_details, net.into());
|
let forge = Forge::create(repo_details, net);
|
||||||
assert_eq!(forge.name(), "github");
|
assert_eq!(forge.name(), "github");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,12 @@ use kxio::fs::FileSystem;
|
||||||
pub fn run(fs: &FileSystem) -> Result<()> {
|
pub fn run(fs: &FileSystem) -> Result<()> {
|
||||||
let pathbuf = fs.base().join(".git-next.toml");
|
let pathbuf = fs.base().join(".git-next.toml");
|
||||||
if fs
|
if fs
|
||||||
.path(&pathbuf)
|
.path_exists(&pathbuf)
|
||||||
.exists()
|
|
||||||
.with_context(|| format!("Checking for existing file: {pathbuf:?}"))?
|
.with_context(|| format!("Checking for existing file: {pathbuf:?}"))?
|
||||||
{
|
{
|
||||||
eprintln!("The configuration file already exists at {pathbuf:?} - not overwritting it.",);
|
eprintln!("The configuration file already exists at {pathbuf:?} - not overwritting it.",);
|
||||||
} else {
|
} else {
|
||||||
fs.file(&pathbuf)
|
fs.file_write(&pathbuf, include_str!("../default.toml"))
|
||||||
.write(include_str!("../default.toml"))
|
|
||||||
.with_context(|| format!("Writing file: {pathbuf:?}"))?;
|
.with_context(|| format!("Writing file: {pathbuf:?}"))?;
|
||||||
println!("Created a default configuration file at {pathbuf:?}");
|
println!("Created a default configuration file at {pathbuf:?}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use kxio::{fs, net};
|
use kxio::{fs, network::Network};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(version = clap::crate_version!(), author = clap::crate_authors!(), about = clap::crate_description!())]
|
#[clap(version = clap::crate_version!(), author = clap::crate_authors!(), about = clap::crate_description!())]
|
||||||
|
@ -48,7 +48,7 @@ enum Server {
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let fs = fs::new(PathBuf::default());
|
let fs = fs::new(PathBuf::default());
|
||||||
let net = net::new();
|
let net = Network::new_real();
|
||||||
let repository_factory = git::repository::factory::real();
|
let repository_factory = git::repository::factory::real();
|
||||||
let commands = Commands::parse();
|
let commands = Commands::parse();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
|
|
||||||
use tracing::{debug, warn, Instrument as _};
|
use tracing::{debug, Instrument as _};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
repo::{
|
repo::{
|
||||||
|
@ -26,13 +26,9 @@ impl Handler<CheckCIStatus> for RepoActor {
|
||||||
self.update_tui(RepoUpdate::CheckingCI);
|
self.update_tui(RepoUpdate::CheckingCI);
|
||||||
// get the status - pass, fail, pending (all others map to fail, e.g. error)
|
// get the status - pass, fail, pending (all others map to fail, e.g. error)
|
||||||
async move {
|
async move {
|
||||||
match forge.commit_status(&next).await {
|
let status = forge.commit_status(&next).await;
|
||||||
Ok(status) => {
|
debug!("got status: {status:?}");
|
||||||
debug!("got status: {status:?}");
|
do_send(&addr, ReceiveCIStatus::new((next, status)), log.as_ref());
|
||||||
do_send(&addr, ReceiveCIStatus::new((next, status)), log.as_ref());
|
|
||||||
}
|
|
||||||
Err(err) => warn!(?err, "fetching commit status"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.in_current_span()
|
.in_current_span()
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
|
|
|
@ -70,7 +70,6 @@ impl Handler<ReceiveCIStatus> for RepoActor {
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
}
|
}
|
||||||
Status::Error(_) => todo!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
server::{actor::messages::RepoUpdate, ServerActor},
|
server::{actor::messages::RepoUpdate, ServerActor},
|
||||||
};
|
};
|
||||||
use derive_more::Deref;
|
use derive_more::Deref;
|
||||||
use kxio::net::Net;
|
use kxio::network::Network;
|
||||||
use tracing::{info, instrument, warn, Instrument};
|
use tracing::{info, instrument, warn, Instrument};
|
||||||
|
|
||||||
use git_next_core::{
|
use git_next_core::{
|
||||||
|
@ -57,7 +57,7 @@ pub struct RepoActor {
|
||||||
last_dev_commit: Option<git::Commit>,
|
last_dev_commit: Option<git::Commit>,
|
||||||
repository_factory: Box<dyn RepositoryFactory>,
|
repository_factory: Box<dyn RepositoryFactory>,
|
||||||
open_repository: Option<Box<dyn OpenRepositoryLike>>,
|
open_repository: Option<Box<dyn OpenRepositoryLike>>,
|
||||||
net: Net,
|
net: Network,
|
||||||
forge: Box<dyn git::ForgeLike>,
|
forge: Box<dyn git::ForgeLike>,
|
||||||
log: Option<ActorLog>,
|
log: Option<ActorLog>,
|
||||||
notify_user_recipient: Option<Recipient<NotifyUser>>,
|
notify_user_recipient: Option<Recipient<NotifyUser>>,
|
||||||
|
@ -70,7 +70,7 @@ impl RepoActor {
|
||||||
forge: Box<dyn git::ForgeLike>,
|
forge: Box<dyn git::ForgeLike>,
|
||||||
listen_url: ListenUrl,
|
listen_url: ListenUrl,
|
||||||
generation: git::Generation,
|
generation: git::Generation,
|
||||||
net: Net,
|
net: Network,
|
||||||
repository_factory: Box<dyn RepositoryFactory>,
|
repository_factory: Box<dyn RepositoryFactory>,
|
||||||
sleep_duration: std::time::Duration,
|
sleep_duration: std::time::Duration,
|
||||||
notify_user_recipient: Option<Recipient<NotifyUser>>,
|
notify_user_recipient: Option<Recipient<NotifyUser>>,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
use git_next_core::server::ListenUrl;
|
||||||
|
|
||||||
//
|
//
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use git_next_core::server::ListenUrl;
|
|
||||||
|
|
||||||
pub fn has_all_valid_remote_defaults(
|
pub fn has_all_valid_remote_defaults(
|
||||||
open_repository: &mut MockOpenRepositoryLike,
|
open_repository: &mut MockOpenRepositoryLike,
|
||||||
repo_details: &RepoDetails,
|
repo_details: &RepoDetails,
|
||||||
|
@ -48,8 +48,8 @@ pub fn a_repo_alias() -> RepoAlias {
|
||||||
RepoAlias::new(a_name())
|
RepoAlias::new(a_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_network() -> kxio::net::MockNet {
|
pub fn a_network() -> kxio::network::MockNetwork {
|
||||||
kxio::net::mock()
|
kxio::network::MockNetwork::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_listen_url() -> ListenUrl {
|
pub fn a_listen_url() -> ListenUrl {
|
||||||
|
@ -152,8 +152,8 @@ pub fn a_commit_sha() -> Sha {
|
||||||
Sha::new(a_name())
|
Sha::new(a_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_filesystem() -> kxio::fs::TempFileSystem {
|
pub fn a_filesystem() -> kxio::fs::FileSystem {
|
||||||
kxio::fs::temp().expect("temp fs")
|
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repo_details(fs: &kxio::fs::FileSystem) -> RepoDetails {
|
pub fn repo_details(fs: &kxio::fs::FileSystem) -> RepoDetails {
|
||||||
|
@ -196,7 +196,7 @@ pub fn a_repo_actor(
|
||||||
repo_details: RepoDetails,
|
repo_details: RepoDetails,
|
||||||
repository_factory: Box<dyn RepositoryFactory>,
|
repository_factory: Box<dyn RepositoryFactory>,
|
||||||
forge: Box<dyn ForgeLike>,
|
forge: Box<dyn ForgeLike>,
|
||||||
net: kxio::net::Net,
|
net: kxio::network::Network,
|
||||||
) -> (RepoActor, ActorLog) {
|
) -> (RepoActor, ActorLog) {
|
||||||
let listen_url = given::a_listen_url();
|
let listen_url = given::a_listen_url();
|
||||||
let generation = Generation::default();
|
let generation = Generation::default();
|
||||||
|
|
|
@ -59,7 +59,7 @@ async fn should_open() -> TestResult {
|
||||||
let _ = opened_ref.write().map(|mut l| l.push(()));
|
let _ = opened_ref.write().map(|mut l| l.push(()));
|
||||||
Ok(Box::new(open_repository))
|
Ok(Box::new(open_repository))
|
||||||
});
|
});
|
||||||
fs.dir(&repo_details.gitdir).create()?;
|
fs.dir_create(&repo_details.gitdir)?;
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, _log) = when::start_actor(repository_factory, repo_details, given::a_forge());
|
let (addr, _log) = when::start_actor(repository_factory, repo_details, given::a_forge());
|
||||||
|
@ -94,7 +94,7 @@ async fn when_server_has_no_repo_config_should_send_load_from_repo() -> TestResu
|
||||||
|
|
||||||
let mut repository_factory = MockRepositoryFactory::new();
|
let mut repository_factory = MockRepositoryFactory::new();
|
||||||
expect::open_repository(&mut repository_factory, open_repository);
|
expect::open_repository(&mut repository_factory, open_repository);
|
||||||
fs.dir(&repo_details.gitdir).create()?;
|
fs.dir_create(&repo_details.gitdir)?;
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) = when::start_actor(repository_factory, repo_details, given::a_forge());
|
let (addr, log) = when::start_actor(repository_factory, repo_details, given::a_forge());
|
||||||
|
@ -123,7 +123,7 @@ async fn when_server_has_repo_config_should_send_register_webhook() -> TestResul
|
||||||
|
|
||||||
let mut repository_factory = MockRepositoryFactory::new();
|
let mut repository_factory = MockRepositoryFactory::new();
|
||||||
expect::open_repository(&mut repository_factory, open_repository);
|
expect::open_repository(&mut repository_factory, open_repository);
|
||||||
fs.dir(&repo_details.gitdir).create()?;
|
fs.dir_create(&repo_details.gitdir)?;
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) = when::start_actor(repository_factory, repo_details, given::a_forge());
|
let (addr, log) = when::start_actor(repository_factory, repo_details, given::a_forge());
|
||||||
|
@ -156,7 +156,7 @@ async fn opened_repo_with_no_default_push_should_not_proceed() -> TestResult {
|
||||||
|
|
||||||
let mut repository_factory = MockRepositoryFactory::new();
|
let mut repository_factory = MockRepositoryFactory::new();
|
||||||
expect::open_repository(&mut repository_factory, open_repository);
|
expect::open_repository(&mut repository_factory, open_repository);
|
||||||
fs.dir(&repo_details.gitdir).create()?;
|
fs.dir_create(&repo_details.gitdir)?;
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) = when::start_actor(repository_factory, repo_details, given::a_forge());
|
let (addr, log) = when::start_actor(repository_factory, repo_details, given::a_forge());
|
||||||
|
@ -180,7 +180,7 @@ async fn opened_repo_with_no_default_fetch_should_not_proceed() -> TestResult {
|
||||||
.return_once(|| Err(git::fetch::Error::NoFetchRemoteFound));
|
.return_once(|| Err(git::fetch::Error::NoFetchRemoteFound));
|
||||||
let mut repository_factory = MockRepositoryFactory::new();
|
let mut repository_factory = MockRepositoryFactory::new();
|
||||||
expect::open_repository(&mut repository_factory, open_repository);
|
expect::open_repository(&mut repository_factory, open_repository);
|
||||||
fs.dir(&repo_details.gitdir).create()?;
|
fs.dir_create(&repo_details.gitdir)?;
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) = when::start_actor(repository_factory, repo_details, given::a_forge());
|
let (addr, log) = when::start_actor(repository_factory, repo_details, given::a_forge());
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub fn commit_status(forge: &mut MockForgeLike, commit: Commit, status: Status)
|
||||||
commit_status_forge
|
commit_status_forge
|
||||||
.expect_commit_status()
|
.expect_commit_status()
|
||||||
.with(mockall::predicate::eq(commit))
|
.with(mockall::predicate::eq(commit))
|
||||||
.return_once(|_| Ok(status));
|
.return_once(|_| status);
|
||||||
forge
|
forge
|
||||||
.expect_duplicate()
|
.expect_duplicate()
|
||||||
.return_once(move || Box::new(commit_status_forge));
|
.return_once(move || Box::new(commit_status_forge));
|
||||||
|
|
|
@ -20,7 +20,7 @@ use git_next_core::{
|
||||||
ForgeAlias, ForgeConfig, GitDir, RepoAlias, ServerRepoConfig, StoragePathType,
|
ForgeAlias, ForgeConfig, GitDir, RepoAlias, ServerRepoConfig, StoragePathType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use kxio::{fs::FileSystem, net::Net};
|
use kxio::{fs::FileSystem, network::Network};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
|
@ -52,7 +52,7 @@ pub struct ServerActor {
|
||||||
generation: Generation,
|
generation: Generation,
|
||||||
webhook_actor_addr: Option<Addr<WebhookActor>>,
|
webhook_actor_addr: Option<Addr<WebhookActor>>,
|
||||||
fs: FileSystem,
|
fs: FileSystem,
|
||||||
net: Net,
|
net: Network,
|
||||||
alerts: Addr<AlertsActor>,
|
alerts: Addr<AlertsActor>,
|
||||||
repository_factory: Box<dyn RepositoryFactory>,
|
repository_factory: Box<dyn RepositoryFactory>,
|
||||||
sleep_duration: std::time::Duration,
|
sleep_duration: std::time::Duration,
|
||||||
|
@ -71,7 +71,7 @@ impl Actor for ServerActor {
|
||||||
impl ServerActor {
|
impl ServerActor {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
fs: FileSystem,
|
fs: FileSystem,
|
||||||
net: Net,
|
net: Network,
|
||||||
alerts: Addr<AlertsActor>,
|
alerts: Addr<AlertsActor>,
|
||||||
repo: Box<dyn RepositoryFactory>,
|
repo: Box<dyn RepositoryFactory>,
|
||||||
sleep_duration: std::time::Duration,
|
sleep_duration: std::time::Duration,
|
||||||
|
@ -100,14 +100,13 @@ impl ServerActor {
|
||||||
for (forge_name, _forge_config) in app_config.forges() {
|
for (forge_name, _forge_config) in app_config.forges() {
|
||||||
let forge_dir: PathBuf = (&forge_name).into();
|
let forge_dir: PathBuf = (&forge_name).into();
|
||||||
let path = server_dir.join(&forge_dir);
|
let path = server_dir.join(&forge_dir);
|
||||||
let path_handle = self.fs.path(&path);
|
if self.fs.path_exists(&path)? {
|
||||||
if path_handle.exists()? {
|
if !self.fs.path_is_dir(&path)? {
|
||||||
if !path_handle.is_dir()? {
|
|
||||||
return Err(Error::ForgeDirIsNotDirectory { path });
|
return Err(Error::ForgeDirIsNotDirectory { path });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tracing::info!(%forge_name, ?path_handle, "creating storage");
|
tracing::info!(%forge_name, ?path, "creating storage");
|
||||||
self.fs.dir(&path).create_all()?;
|
self.fs.dir_create_all(&path)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +213,7 @@ impl ServerActor {
|
||||||
let server_storage = app_config.storage().clone();
|
let server_storage = app_config.storage().clone();
|
||||||
let dir = server_storage.path();
|
let dir = server_storage.path();
|
||||||
if !dir.exists() {
|
if !dir.exists() {
|
||||||
if let Err(err) = self.fs.dir(dir).create() {
|
if let Err(err) = self.fs.dir_create(dir) {
|
||||||
error!(?err, ?dir, "Failed to create server storage");
|
error!(?err, ?dir, "Failed to create server storage");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,14 @@ use actix::prelude::*;
|
||||||
use crate::alerts::{AlertsActor, History};
|
use crate::alerts::{AlertsActor, History};
|
||||||
|
|
||||||
//
|
//
|
||||||
pub fn a_filesystem() -> kxio::fs::TempFileSystem {
|
pub fn a_filesystem() -> kxio::fs::FileSystem {
|
||||||
kxio::fs::temp().expect("temp fs")
|
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_network() -> kxio::net::MockNet {
|
pub fn a_network() -> kxio::network::MockNetwork {
|
||||||
kxio::net::mock()
|
kxio::network::MockNetwork::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn an_alerts_actor(net: kxio::net::Net) -> Addr<AlertsActor> {
|
pub fn an_alerts_actor(net: kxio::network::Network) -> Addr<AlertsActor> {
|
||||||
AlertsActor::new(None, History::new(Duration::from_millis(1)), net).start()
|
AlertsActor::new(None, History::new(Duration::from_millis(1)), net).start()
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ async fn when_webhook_url_has_trailing_slash_should_not_send() {
|
||||||
let duration = std::time::Duration::from_millis(1);
|
let duration = std::time::Duration::from_millis(1);
|
||||||
|
|
||||||
// sut
|
// sut
|
||||||
let server = ServerActor::new(fs.as_real(), net.into(), alerts, repo, duration);
|
let server = ServerActor::new(fs.clone(), net.into(), alerts, repo, duration);
|
||||||
|
|
||||||
// collaborators
|
// collaborators
|
||||||
let listen = Listen::new(
|
let listen = Listen::new(
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub use actor::ServerActor;
|
||||||
use git_next_core::git::RepositoryFactory;
|
use git_next_core::git::RepositoryFactory;
|
||||||
|
|
||||||
use color_eyre::{eyre::Context, Result};
|
use color_eyre::{eyre::Context, Result};
|
||||||
use kxio::{fs::FileSystem, net::Net};
|
use kxio::{fs::FileSystem, network::Network};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -34,14 +34,12 @@ pub fn init(fs: &FileSystem) -> Result<()> {
|
||||||
let file_name = "git-next-server.toml";
|
let file_name = "git-next-server.toml";
|
||||||
let pathbuf = PathBuf::from(file_name);
|
let pathbuf = PathBuf::from(file_name);
|
||||||
if fs
|
if fs
|
||||||
.path(&pathbuf)
|
.path_exists(&pathbuf)
|
||||||
.exists()
|
|
||||||
.with_context(|| format!("Checking for existing file: {pathbuf:?}"))?
|
.with_context(|| format!("Checking for existing file: {pathbuf:?}"))?
|
||||||
{
|
{
|
||||||
eprintln!("The configuration file already exists at {pathbuf:?} - not overwritting it.",);
|
eprintln!("The configuration file already exists at {pathbuf:?} - not overwritting it.",);
|
||||||
} else {
|
} else {
|
||||||
fs.file(&pathbuf)
|
fs.file_write(&pathbuf, include_str!("server-default.toml"))
|
||||||
.write(include_str!("server-default.toml"))
|
|
||||||
.with_context(|| format!("Writing file: {pathbuf:?}"))?;
|
.with_context(|| format!("Writing file: {pathbuf:?}"))?;
|
||||||
println!("Created a default configuration file at {pathbuf:?}",);
|
println!("Created a default configuration file at {pathbuf:?}",);
|
||||||
}
|
}
|
||||||
|
@ -52,7 +50,7 @@ pub fn init(fs: &FileSystem) -> Result<()> {
|
||||||
pub fn start(
|
pub fn start(
|
||||||
ui: bool,
|
ui: bool,
|
||||||
fs: FileSystem,
|
fs: FileSystem,
|
||||||
net: Net,
|
net: Network,
|
||||||
repo: Box<dyn RepositoryFactory>,
|
repo: Box<dyn RepositoryFactory>,
|
||||||
sleep_duration: std::time::Duration,
|
sleep_duration: std::time::Duration,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
|
|
@ -6,12 +6,12 @@ mod init {
|
||||||
fn should_not_update_file_if_it_exists() -> TestResult {
|
fn should_not_update_file_if_it_exists() -> TestResult {
|
||||||
let fs = kxio::fs::temp()?;
|
let fs = kxio::fs::temp()?;
|
||||||
let file = fs.base().join(".git-next.toml");
|
let file = fs.base().join(".git-next.toml");
|
||||||
fs.file(&file).write("contents")?;
|
fs.file_write(&file, "contents")?;
|
||||||
|
|
||||||
crate::init::run(&fs)?;
|
crate::init::run(&fs)?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fs.file(&file).reader()?.to_string(),
|
fs.file_read_to_string(&file)?,
|
||||||
"contents",
|
"contents",
|
||||||
"The file has been changed"
|
"The file has been changed"
|
||||||
);
|
);
|
||||||
|
@ -27,10 +27,10 @@ mod init {
|
||||||
|
|
||||||
let file = fs.base().join(".git-next.toml");
|
let file = fs.base().join(".git-next.toml");
|
||||||
|
|
||||||
assert!(fs.path(&file).exists()?, "The file has not been created");
|
assert!(fs.path_exists(&file)?, "The file has not been created");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fs.file(&file).reader()?.to_string(),
|
fs.file_read_to_string(&file)?,
|
||||||
include_str!("../default.toml"),
|
include_str!("../default.toml"),
|
||||||
"The file does not match the default template"
|
"The file does not match the default template"
|
||||||
);
|
);
|
||||||
|
@ -54,7 +54,7 @@ mod file_watcher {
|
||||||
async fn should_not_block_calling_thread() -> TestResult {
|
async fn should_not_block_calling_thread() -> TestResult {
|
||||||
let fs = kxio::fs::temp()?;
|
let fs = kxio::fs::temp()?;
|
||||||
let path = fs.base().join("file");
|
let path = fs.base().join("file");
|
||||||
fs.file(&path).write("foo")?;
|
fs.file_write(&path, "foo")?;
|
||||||
|
|
||||||
let listener = Listener;
|
let listener = Listener;
|
||||||
let l_addr = listener.start();
|
let l_addr = listener.start();
|
||||||
|
|
|
@ -57,7 +57,7 @@ impl AppConfig {
|
||||||
pub fn load(fs: &FileSystem) -> Result<Self> {
|
pub fn load(fs: &FileSystem) -> Result<Self> {
|
||||||
let file = fs.base().join("git-next-server.toml");
|
let file = fs.base().join("git-next-server.toml");
|
||||||
info!(?file, "");
|
info!(?file, "");
|
||||||
let str = fs.file(&file).reader()?.to_string();
|
let str = fs.file_read_to_string(&file)?;
|
||||||
Ok(toml::from_str(&str)?)
|
Ok(toml::from_str(&str)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -624,8 +624,10 @@ token = "{forge_token}"
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
println!("{file_contents}");
|
println!("{file_contents}");
|
||||||
fs.file(&fs.base().join("git-next-server.toml"))
|
fs.file_write(
|
||||||
.write(file_contents.as_str())?;
|
&fs.base().join("git-next-server.toml"),
|
||||||
|
file_contents.as_str(),
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,4 @@ pub enum Status {
|
||||||
Pass,
|
Pass,
|
||||||
Fail,
|
Fail,
|
||||||
Pending,
|
Pending,
|
||||||
Error(String),
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,7 @@ pub trait ForgeLike: std::fmt::Debug + Send + Sync {
|
||||||
) -> git::forge::webhook::Result<webhook::push::Push>;
|
) -> git::forge::webhook::Result<webhook::push::Push>;
|
||||||
|
|
||||||
/// Checks the results of any (e.g. CI) status checks for the commit.
|
/// Checks the results of any (e.g. CI) status checks for the commit.
|
||||||
async fn commit_status(
|
async fn commit_status(&self, commit: &git::Commit) -> git::forge::commit::Status;
|
||||||
&self,
|
|
||||||
commit: &git::Commit,
|
|
||||||
) -> git::forge::webhook::Result<git::forge::commit::Status>;
|
|
||||||
|
|
||||||
// Lists all the webhooks
|
// Lists all the webhooks
|
||||||
async fn list_webhooks(
|
async fn list_webhooks(
|
||||||
|
|
|
@ -4,16 +4,8 @@ pub type Result<T> = core::result::Result<T, Error>;
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("network")]
|
#[error("network")]
|
||||||
Network(#[from] kxio::net::Error),
|
Network(#[from] kxio::network::NetworkError),
|
||||||
|
|
||||||
#[error("reqwest")]
|
|
||||||
Reqwest(#[from] kxio::net::RequestError),
|
|
||||||
|
|
||||||
// #[error("header")]
|
|
||||||
// Header(#[from] http::header::InvalidHeaderValue),
|
|
||||||
|
|
||||||
// #[error("parse url")]
|
|
||||||
// UrlParse(#[from] url::ParseError),
|
|
||||||
#[error("failed to register: {0}")]
|
#[error("failed to register: {0}")]
|
||||||
FailedToRegister(String),
|
FailedToRegister(String),
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub enum Error {
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
#[error("network: {0}")]
|
#[error("network: {0}")]
|
||||||
Network(#[from] kxio::net::Error),
|
Network(#[from] kxio::network::NetworkError),
|
||||||
|
|
||||||
#[error("fetch: {0}")]
|
#[error("fetch: {0}")]
|
||||||
Fetch(#[from] git::fetch::Error),
|
Fetch(#[from] git::fetch::Error),
|
||||||
|
|
|
@ -142,8 +142,7 @@ impl RepoDetails {
|
||||||
let fs = self.gitdir.as_fs();
|
let fs = self.gitdir.as_fs();
|
||||||
// load config file
|
// load config file
|
||||||
let config_filename = &self.gitdir.join("config");
|
let config_filename = &self.gitdir.join("config");
|
||||||
let file = fs.file(config_filename);
|
let config_file = fs.file_read_to_string(config_filename)?;
|
||||||
let config_file = file.reader()?.to_string();
|
|
||||||
let mut config_lines = config_file
|
let mut config_lines = config_file
|
||||||
.lines()
|
.lines()
|
||||||
.map(ToOwned::to_owned)
|
.map(ToOwned::to_owned)
|
||||||
|
@ -166,7 +165,7 @@ impl RepoDetails {
|
||||||
}
|
}
|
||||||
tracing::debug!(?config_lines, "updated file");
|
tracing::debug!(?config_lines, "updated file");
|
||||||
// write config file back out
|
// write config file back out
|
||||||
file.write(config_lines.join("\n").as_str())?;
|
fs.file_write(config_filename, config_lines.join("\n").as_str())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,12 +172,10 @@ impl TestOpenRepository {
|
||||||
|
|
||||||
fn write_origin(gitdir: &GitDir, fs: &kxio::fs::FileSystem) {
|
fn write_origin(gitdir: &GitDir, fs: &kxio::fs::FileSystem) {
|
||||||
let config_file = fs.base().join(gitdir.to_path_buf()).join(".git/config");
|
let config_file = fs.base().join(gitdir.to_path_buf()).join(".git/config");
|
||||||
let file = fs.file(&config_file);
|
|
||||||
#[allow(clippy::expect_used)]
|
#[allow(clippy::expect_used)]
|
||||||
let contents = file
|
let contents = fs
|
||||||
.reader()
|
.file_read_to_string(&config_file)
|
||||||
.expect("read original .git/config")
|
.expect("read original .git/config");
|
||||||
.to_string();
|
|
||||||
let updated_contents = format!(
|
let updated_contents = format!(
|
||||||
r#"{contents}
|
r#"{contents}
|
||||||
[remote "origin"]
|
[remote "origin"]
|
||||||
|
@ -186,7 +184,7 @@ impl TestOpenRepository {
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
#[allow(clippy::expect_used)]
|
#[allow(clippy::expect_used)]
|
||||||
file.write(&updated_contents)
|
fs.file_write(&config_file, &updated_contents)
|
||||||
.expect("write updated .git/config");
|
.expect("write updated .git/config");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::ops::Deref as _;
|
|
||||||
|
|
||||||
use crate::CommitCount;
|
use crate::CommitCount;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -11,7 +9,7 @@ fn should_return_single_item_in_commit_log_when_not_searching() -> TestResult {
|
||||||
let_assert!(Ok(fs) = kxio::fs::temp());
|
let_assert!(Ok(fs) = kxio::fs::temp());
|
||||||
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
||||||
let forge_details = given::forge_details();
|
let forge_details = given::forge_details();
|
||||||
let test_repository = git::repository::test(fs.deref().clone(), forge_details);
|
let test_repository = git::repository::test(fs.clone(), forge_details);
|
||||||
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
|
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
|
||||||
let repo_config = &given::a_repo_config();
|
let repo_config = &given::a_repo_config();
|
||||||
let branches = repo_config.branches();
|
let branches = repo_config.branches();
|
||||||
|
@ -31,7 +29,7 @@ fn should_return_capacity_25_in_commit_log_when_searching_for_garbage() -> TestR
|
||||||
let forge_details = given::forge_details().with_max_dev_commits(Some(CommitCount::from(25)));
|
let forge_details = given::forge_details().with_max_dev_commits(Some(CommitCount::from(25)));
|
||||||
let_assert!(Some(max_dev_commits) = forge_details.max_dev_commits());
|
let_assert!(Some(max_dev_commits) = forge_details.max_dev_commits());
|
||||||
assert!(**max_dev_commits >= 25);
|
assert!(**max_dev_commits >= 25);
|
||||||
let test_repository = git::repository::test(fs.deref().clone(), forge_details);
|
let test_repository = git::repository::test(fs.clone(), forge_details);
|
||||||
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
|
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
|
||||||
for _ in [0; 25] {
|
for _ in [0; 25] {
|
||||||
then::create_a_commit_on_branch(&fs, &gitdir, &branch_name)?;
|
then::create_a_commit_on_branch(&fs, &gitdir, &branch_name)?;
|
||||||
|
@ -50,7 +48,7 @@ fn should_return_5_in_commit_log_when_searching_for_5th_item() -> TestResult {
|
||||||
let forge_details = given::forge_details().with_max_dev_commits(Some(CommitCount::from(10)));
|
let forge_details = given::forge_details().with_max_dev_commits(Some(CommitCount::from(10)));
|
||||||
let_assert!(Some(max_dev_commits) = forge_details.max_dev_commits());
|
let_assert!(Some(max_dev_commits) = forge_details.max_dev_commits());
|
||||||
assert!(**max_dev_commits > 5);
|
assert!(**max_dev_commits > 5);
|
||||||
let test_repository = git::repository::test(fs.deref().clone(), forge_details);
|
let test_repository = git::repository::test(fs.clone(), forge_details);
|
||||||
let_assert!(
|
let_assert!(
|
||||||
Ok(open_repository) = test_repository.open(&gitdir),
|
Ok(open_repository) = test_repository.open(&gitdir),
|
||||||
"open repository"
|
"open repository"
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::ops::Deref as _;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -13,7 +11,7 @@ fn should_return_file() -> TestResult {
|
||||||
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
||||||
let forge_details = given::forge_details();
|
let forge_details = given::forge_details();
|
||||||
|
|
||||||
let test_repository = git::repository::test(fs.deref().clone(), forge_details);
|
let test_repository = git::repository::test(fs.clone(), forge_details);
|
||||||
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
|
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
|
||||||
then::commit_named_file_to_branch(
|
then::commit_named_file_to_branch(
|
||||||
&file_name,
|
&file_name,
|
||||||
|
@ -37,7 +35,7 @@ fn should_error_on_missing_file() -> TestResult {
|
||||||
let_assert!(Ok(fs) = kxio::fs::temp());
|
let_assert!(Ok(fs) = kxio::fs::temp());
|
||||||
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
||||||
let forge_details = given::forge_details();
|
let forge_details = given::forge_details();
|
||||||
let test_repository = git::repository::test(fs.deref().clone(), forge_details);
|
let test_repository = git::repository::test(fs.clone(), forge_details);
|
||||||
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
|
let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
|
||||||
let repo_config = &given::a_repo_config();
|
let repo_config = &given::a_repo_config();
|
||||||
let branches = repo_config.branches();
|
let branches = repo_config.branches();
|
||||||
|
|
|
@ -23,7 +23,7 @@ fn open_where_storage_external_auth_matches() -> TestResult {
|
||||||
tracing::debug!(?result, "open");
|
tracing::debug!(?result, "open");
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
// verify origin in .git/config matches url
|
// verify origin in .git/config matches url
|
||||||
let config = fs.file(&fs.base().join("config")).reader()?.to_string();
|
let config = fs.file_read_to_string(&fs.base().join("config"))?;
|
||||||
tracing::debug!(config=?config.lines().collect::<Vec<_>>(), "auth");
|
tracing::debug!(config=?config.lines().collect::<Vec<_>>(), "auth");
|
||||||
assert!(config.lines().any(|line| line.contains(url)));
|
assert!(config.lines().any(|line| line.contains(url)));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -60,7 +60,7 @@ fn open_where_storage_external_auth_differs_error() -> TestResult {
|
||||||
));
|
));
|
||||||
|
|
||||||
// verify origin in .git/config is unchanged
|
// verify origin in .git/config is unchanged
|
||||||
let config = fs.file(&fs.base().join("config")).reader()?.to_string();
|
let config = fs.file_read_to_string(&fs.base().join("config"))?;
|
||||||
tracing::debug!(config=?config.lines().collect::<Vec<_>>(), "auth");
|
tracing::debug!(config=?config.lines().collect::<Vec<_>>(), "auth");
|
||||||
assert!(config.lines().any(|line| line.contains(original_url))); // the original urk
|
assert!(config.lines().any(|line| line.contains(original_url))); // the original urk
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -86,7 +86,7 @@ fn open_where_storage_internal_auth_matches() -> TestResult {
|
||||||
tracing::debug!(?result, "open");
|
tracing::debug!(?result, "open");
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
// verify origin in .git/config matches url
|
// verify origin in .git/config matches url
|
||||||
let config = fs.file(&fs.base().join("config")).reader()?.to_string();
|
let config = fs.file_read_to_string(&fs.base().join("config"))?;
|
||||||
tracing::debug!(config=?config.lines().collect::<Vec<_>>(), "auth");
|
tracing::debug!(config=?config.lines().collect::<Vec<_>>(), "auth");
|
||||||
assert!(
|
assert!(
|
||||||
config.lines().any(|line| line.contains(url)),
|
config.lines().any(|line| line.contains(url)),
|
||||||
|
@ -121,7 +121,7 @@ fn open_where_storage_internal_auth_differs_update_config() -> TestResult {
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
// verify origin in .git/config is unchanged
|
// verify origin in .git/config is unchanged
|
||||||
let config = fs.file(&fs.base().join("config")).reader()?.to_string();
|
let config = fs.file_read_to_string(&fs.base().join("config"))?;
|
||||||
tracing::debug!(config=?config.lines().collect::<Vec<_>>(), "auth");
|
tracing::debug!(config=?config.lines().collect::<Vec<_>>(), "auth");
|
||||||
assert!(
|
assert!(
|
||||||
config
|
config
|
||||||
|
|
|
@ -306,7 +306,7 @@ pub mod given {
|
||||||
webhook::Push::new(branch, sha.to_string(), message.to_string())
|
webhook::Push::new(branch, sha.to_string(), message.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_filesystem() -> kxio::fs::TempFileSystem {
|
pub fn a_filesystem() -> kxio::fs::FileSystem {
|
||||||
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
|
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,8 +337,7 @@ pub mod given {
|
||||||
let repo = gix::prepare_clone_bare(url, fs.base()).unwrap();
|
let repo = gix::prepare_clone_bare(url, fs.base()).unwrap();
|
||||||
repo.persist();
|
repo.persist();
|
||||||
// load config file
|
// load config file
|
||||||
let file = fs.file(&path.join("config"));
|
let config_file = fs.file_read_to_string(&path.join("config")).unwrap();
|
||||||
let config_file = file.reader().unwrap().to_string();
|
|
||||||
// add use are origin url
|
// add use are origin url
|
||||||
let mut config_lines = config_file.lines().collect::<Vec<_>>();
|
let mut config_lines = config_file.lines().collect::<Vec<_>>();
|
||||||
config_lines.push(r#"[remote "origin"]"#);
|
config_lines.push(r#"[remote "origin"]"#);
|
||||||
|
@ -346,7 +345,8 @@ pub mod given {
|
||||||
tracing::info!(?url, %url_line, "writing");
|
tracing::info!(?url, %url_line, "writing");
|
||||||
config_lines.push(&url_line);
|
config_lines.push(&url_line);
|
||||||
// write config file back out
|
// write config file back out
|
||||||
file.write(config_lines.join("\n").as_str()).unwrap();
|
fs.file_write(&path.join("config"), config_lines.join("\n").as_str())
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
|
@ -374,7 +374,7 @@ pub mod then {
|
||||||
let pathbuf = PathBuf::from(gitdir);
|
let pathbuf = PathBuf::from(gitdir);
|
||||||
let file = fs.base().join(pathbuf).join(file_name);
|
let file = fs.base().join(pathbuf).join(file_name);
|
||||||
#[allow(clippy::expect_used)]
|
#[allow(clippy::expect_used)]
|
||||||
fs.file(&file).write(contents)?;
|
fs.file_write(&file, contents)?;
|
||||||
// git add ${file}
|
// git add ${file}
|
||||||
git_add_file(gitdir, &file)?;
|
git_add_file(gitdir, &file)?;
|
||||||
// git commit -m"Added ${file}"
|
// git commit -m"Added ${file}"
|
||||||
|
@ -396,7 +396,7 @@ pub mod then {
|
||||||
let word = given::a_name();
|
let word = given::a_name();
|
||||||
let pathbuf = PathBuf::from(gitdir);
|
let pathbuf = PathBuf::from(gitdir);
|
||||||
let file = fs.base().join(pathbuf).join(&word);
|
let file = fs.base().join(pathbuf).join(&word);
|
||||||
fs.file(&file).write(&word)?;
|
fs.file_write(&file, &word)?;
|
||||||
// git add ${file}
|
// git add ${file}
|
||||||
git_add_file(gitdir, &file)?;
|
git_add_file(gitdir, &file)?;
|
||||||
// git commit -m"Added ${file}"
|
// git commit -m"Added ${file}"
|
||||||
|
@ -420,9 +420,9 @@ pub mod then {
|
||||||
let local_branch = gitrefs.join("heads").join(branch_name.to_string().as_str());
|
let local_branch = gitrefs.join("heads").join(branch_name.to_string().as_str());
|
||||||
let origin_heads = gitrefs.join("remotes").join("origin");
|
let origin_heads = gitrefs.join("remotes").join("origin");
|
||||||
let remote_branch = origin_heads.join(branch_name.to_string().as_str());
|
let remote_branch = origin_heads.join(branch_name.to_string().as_str());
|
||||||
let contents = fs.file(&local_branch).reader()?.to_string();
|
let contents = fs.file_read_to_string(&local_branch)?;
|
||||||
fs.dir(&origin_heads).create_all()?;
|
fs.dir_create_all(&origin_heads)?;
|
||||||
fs.file(&remote_branch).write(&contents)?;
|
fs.file_write(&remote_branch, &contents)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +514,7 @@ pub mod then {
|
||||||
.join("refs")
|
.join("refs")
|
||||||
.join("heads")
|
.join("heads")
|
||||||
.join(branch_name.to_string().as_str());
|
.join(branch_name.to_string().as_str());
|
||||||
let sha = fs.file(&main_ref).reader()?.to_string();
|
let sha = fs.file_read_to_string(&main_ref)?;
|
||||||
Ok(git::commit::Sha::new(sha.trim().to_string()))
|
Ok(git::commit::Sha::new(sha.trim().to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,6 @@ use crate::{
|
||||||
|
|
||||||
use assert2::let_assert;
|
use assert2::let_assert;
|
||||||
|
|
||||||
use std::ops::Deref as _;
|
|
||||||
|
|
||||||
mod repos {
|
mod repos {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -210,12 +208,12 @@ mod positions {
|
||||||
let fs = given::a_filesystem();
|
let fs = given::a_filesystem();
|
||||||
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
||||||
let forge_details = given::forge_details();
|
let forge_details = given::forge_details();
|
||||||
let mut test_repository = git::repository::test(fs.deref().clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.clone(), forge_details);
|
||||||
let repo_config = given::a_repo_config();
|
let repo_config = given::a_repo_config();
|
||||||
test_repository.on_fetch(OnFetch::new(
|
test_repository.on_fetch(OnFetch::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// /--- 4 next
|
// /--- 4 next
|
||||||
// 0 --- 1 --- 3 main
|
// 0 --- 1 --- 3 main
|
||||||
|
@ -261,12 +259,12 @@ mod positions {
|
||||||
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
|
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
|
||||||
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
||||||
let forge_details = given::forge_details();
|
let forge_details = given::forge_details();
|
||||||
let mut test_repository = git::repository::test(fs.deref().clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.clone(), forge_details);
|
||||||
let repo_config = given::a_repo_config();
|
let repo_config = given::a_repo_config();
|
||||||
test_repository.on_fetch(OnFetch::new(
|
test_repository.on_fetch(OnFetch::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// /--- 4 dev
|
// /--- 4 dev
|
||||||
// 0 --- 1 --- 3 main
|
// 0 --- 1 --- 3 main
|
||||||
|
@ -290,7 +288,7 @@ mod positions {
|
||||||
test_repository.on_fetch(OnFetch::new(
|
test_repository.on_fetch(OnFetch::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|_branches, _gitdir, _fs| {
|
|_branches, _gitdir, _fs| {
|
||||||
// don't change anything
|
// don't change anything
|
||||||
git::fetch::Result::Ok(())
|
git::fetch::Result::Ok(())
|
||||||
|
@ -299,7 +297,7 @@ mod positions {
|
||||||
test_repository.on_push(OnPush::new(
|
test_repository.on_push(OnPush::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|_repo_details, branch_name, gitref, force, repo_branches, gitdir, fs| {
|
|_repo_details, branch_name, gitref, force, repo_branches, gitdir, fs| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
branch_name,
|
branch_name,
|
||||||
|
@ -348,12 +346,12 @@ mod positions {
|
||||||
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
|
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
|
||||||
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
||||||
let forge_details = given::forge_details();
|
let forge_details = given::forge_details();
|
||||||
let mut test_repository = git::repository::test(fs.deref().clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.clone(), forge_details);
|
||||||
let repo_config = given::a_repo_config();
|
let repo_config = given::a_repo_config();
|
||||||
test_repository.on_fetch(OnFetch::new(
|
test_repository.on_fetch(OnFetch::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// /--- 4 dev
|
// /--- 4 dev
|
||||||
// 0 --- 1 --- 3 main
|
// 0 --- 1 --- 3 main
|
||||||
|
@ -377,7 +375,7 @@ mod positions {
|
||||||
test_repository.on_fetch(OnFetch::new(
|
test_repository.on_fetch(OnFetch::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|_branches, _gitdir, _fs| {
|
|_branches, _gitdir, _fs| {
|
||||||
// don't change anything
|
// don't change anything
|
||||||
git::fetch::Result::Ok(())
|
git::fetch::Result::Ok(())
|
||||||
|
@ -386,7 +384,7 @@ mod positions {
|
||||||
test_repository.on_push(OnPush::new(
|
test_repository.on_push(OnPush::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|_repo_details, _branch_name, _gitref, _force, _repo_branches, _gitdir, _fs| {
|
|_repo_details, _branch_name, _gitref, _force, _repo_branches, _gitdir, _fs| {
|
||||||
git::push::Result::Err(git::push::Error::Lock)
|
git::push::Result::Err(git::push::Error::Lock)
|
||||||
},
|
},
|
||||||
|
@ -422,12 +420,12 @@ mod positions {
|
||||||
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
|
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
|
||||||
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
||||||
let forge_details = given::forge_details();
|
let forge_details = given::forge_details();
|
||||||
let mut test_repository = git::repository::test(fs.deref().clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.clone(), forge_details);
|
||||||
let repo_config = given::a_repo_config();
|
let repo_config = given::a_repo_config();
|
||||||
test_repository.on_fetch(OnFetch::new(
|
test_repository.on_fetch(OnFetch::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// /--- 3 next
|
// /--- 3 next
|
||||||
// 0 --- 1 main & dev
|
// 0 --- 1 main & dev
|
||||||
|
@ -448,7 +446,7 @@ mod positions {
|
||||||
test_repository.on_fetch(OnFetch::new(
|
test_repository.on_fetch(OnFetch::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|_branches, _gitdir, _fs| {
|
|_branches, _gitdir, _fs| {
|
||||||
// don't change anything
|
// don't change anything
|
||||||
git::fetch::Result::Ok(())
|
git::fetch::Result::Ok(())
|
||||||
|
@ -457,7 +455,7 @@ mod positions {
|
||||||
test_repository.on_push(OnPush::new(
|
test_repository.on_push(OnPush::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|_repo_details, branch_name, gitref, force, repo_branches, gitdir, fs| {
|
|_repo_details, branch_name, gitref, force, repo_branches, gitdir, fs| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
branch_name,
|
branch_name,
|
||||||
|
@ -508,12 +506,12 @@ mod positions {
|
||||||
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
|
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
|
||||||
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
||||||
let forge_details = given::forge_details();
|
let forge_details = given::forge_details();
|
||||||
let mut test_repository = git::repository::test(fs.deref().clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.clone(), forge_details);
|
||||||
let repo_config = given::a_repo_config();
|
let repo_config = given::a_repo_config();
|
||||||
test_repository.on_fetch(OnFetch::new(
|
test_repository.on_fetch(OnFetch::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// /--- 3 next
|
// /--- 3 next
|
||||||
// 0 --- 1 main
|
// 0 --- 1 main
|
||||||
|
@ -535,7 +533,7 @@ mod positions {
|
||||||
test_repository.on_fetch(OnFetch::new(
|
test_repository.on_fetch(OnFetch::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|_branches, _gitdir, _fs| {
|
|_branches, _gitdir, _fs| {
|
||||||
// don't change anything
|
// don't change anything
|
||||||
git::fetch::Result::Ok(())
|
git::fetch::Result::Ok(())
|
||||||
|
@ -544,7 +542,7 @@ mod positions {
|
||||||
test_repository.on_push(OnPush::new(
|
test_repository.on_push(OnPush::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|_repo_details, branch_name, gitref, force, repo_branches, gitdir, fs| {
|
|_repo_details, branch_name, gitref, force, repo_branches, gitdir, fs| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
branch_name,
|
branch_name,
|
||||||
|
@ -606,12 +604,12 @@ mod positions {
|
||||||
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
|
let_assert!(Ok(fs) = kxio::fs::temp(), "temp fs");
|
||||||
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
let gitdir = GitDir::new(fs.base().to_path_buf(), StoragePathType::Internal);
|
||||||
let forge_details = given::forge_details();
|
let forge_details = given::forge_details();
|
||||||
let mut test_repository = git::repository::test(fs.deref().clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.clone(), forge_details);
|
||||||
let repo_config = given::a_repo_config();
|
let repo_config = given::a_repo_config();
|
||||||
test_repository.on_fetch(OnFetch::new(
|
test_repository.on_fetch(OnFetch::new(
|
||||||
repo_config.branches().clone(),
|
repo_config.branches().clone(),
|
||||||
gitdir.clone(),
|
gitdir.clone(),
|
||||||
fs.deref().clone(),
|
fs.clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// 0 --- 1 main
|
// 0 --- 1 main
|
||||||
// \--- 2 next
|
// \--- 2 next
|
||||||
|
|
|
@ -13,16 +13,17 @@ use git_next_core::{
|
||||||
ForgeNotification, RegisteredWebhook, WebhookAuth, WebhookId,
|
ForgeNotification, RegisteredWebhook, WebhookAuth, WebhookId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use kxio::net::Net;
|
use kxio::network::{self, Network};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ForgeJo {
|
pub struct ForgeJo {
|
||||||
repo_details: git::RepoDetails,
|
repo_details: git::RepoDetails,
|
||||||
net: Net,
|
net: Network,
|
||||||
}
|
}
|
||||||
impl ForgeJo {
|
impl ForgeJo {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new(repo_details: git::RepoDetails, net: Net) -> Self {
|
pub const fn new(repo_details: git::RepoDetails, net: Network) -> Self {
|
||||||
Self { repo_details, net }
|
Self { repo_details, net }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,28 +52,45 @@ impl git::ForgeLike for ForgeJo {
|
||||||
webhook::parse_body(body)
|
webhook::parse_body(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn commit_status(&self, commit: &git::Commit) -> git::forge::webhook::Result<Status> {
|
async fn commit_status(&self, commit: &git::Commit) -> Status {
|
||||||
let repo_details = &self.repo_details;
|
let repo_details = &self.repo_details;
|
||||||
let hostname = &repo_details.forge.hostname();
|
let hostname = &repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let api_token = &repo_details.forge.token();
|
let api_token = &repo_details.forge.token();
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
let token = api_token.expose_secret();
|
let token = api_token.expose_secret();
|
||||||
let url = format!(
|
let url = network::NetUrl::new(format!(
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/commits/{commit}/status?token={token}"
|
"https://{hostname}/api/v1/repos/{repo_path}/commits/{commit}/status?token={token}"
|
||||||
);
|
));
|
||||||
|
|
||||||
let Ok(response) = self.net.get(url).send().await else {
|
let request = network::NetRequest::new(
|
||||||
return Ok(Status::Pending);
|
network::RequestMethod::Get,
|
||||||
};
|
url,
|
||||||
let combined_status = response.json::<CombinedStatus>().await.unwrap_or_default();
|
network::NetRequestHeaders::new(),
|
||||||
eprintln!("combined_status: {:?}", combined_status);
|
network::RequestBody::None,
|
||||||
let status = match combined_status.state {
|
network::ResponseType::Json,
|
||||||
ForgejoState::Success => Status::Pass,
|
None,
|
||||||
ForgejoState::Pending | ForgejoState::Blank => Status::Pending,
|
network::NetRequestLogging::None,
|
||||||
ForgejoState::Failure | ForgejoState::Error => Status::Fail,
|
);
|
||||||
};
|
let result = self.net.get::<CombinedStatus>(request).await;
|
||||||
Ok(status)
|
match result {
|
||||||
|
Ok(response) => match response.response_body() {
|
||||||
|
Some(status) => match status.state {
|
||||||
|
ForgejoState::Success => Status::Pass,
|
||||||
|
ForgejoState::Pending | ForgejoState::Blank => Status::Pending,
|
||||||
|
ForgejoState::Failure | ForgejoState::Error => Status::Fail,
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
unreachable!(); // response.response_body() is always Some when
|
||||||
|
// request responseType::Json
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
warn!(?e, "Failed to get commit status");
|
||||||
|
Status::Pending // assume issue is transient and allow retry
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_webhooks(
|
async fn list_webhooks(
|
||||||
|
@ -94,17 +112,16 @@ impl git::ForgeLike for ForgeJo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
struct CombinedStatus {
|
struct CombinedStatus {
|
||||||
pub state: ForgejoState,
|
pub state: ForgejoState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
enum ForgejoState {
|
enum ForgejoState {
|
||||||
#[serde(rename = "success")]
|
#[serde(rename = "success")]
|
||||||
Success,
|
Success,
|
||||||
#[serde(rename = "pending")]
|
#[serde(rename = "pending")]
|
||||||
#[default]
|
|
||||||
Pending,
|
Pending,
|
||||||
#[serde(rename = "failure")]
|
#[serde(rename = "failure")]
|
||||||
Failure,
|
Failure,
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
//
|
//
|
||||||
#![allow(clippy::expect_used)] // used with mock net
|
|
||||||
|
|
||||||
use crate::ForgeJo;
|
use crate::ForgeJo;
|
||||||
use git_next_core::{
|
use git_next_core::{
|
||||||
git::{self, forge::commit::Status, ForgeLike as _},
|
git::{self, forge::commit::Status, ForgeLike as _},
|
||||||
server::{ListenUrl, RepoListenUrl},
|
server::ListenUrl,
|
||||||
BranchName, ForgeAlias, ForgeConfig, ForgeNotification, ForgeType, GitDir, Hostname, RepoAlias,
|
BranchName, ForgeAlias, ForgeConfig, ForgeNotification, ForgeType, GitDir, Hostname, RepoAlias,
|
||||||
RepoBranches, RepoPath, ServerRepoConfig, StoragePathType, WebhookAuth, WebhookId,
|
RepoBranches, RepoPath, ServerRepoConfig, StoragePathType, WebhookAuth, WebhookId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use assert2::let_assert;
|
use assert2::let_assert;
|
||||||
use kxio::net::{MockNet, StatusCode};
|
use kxio::network::{self, MockNetwork, StatusCode};
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -118,105 +116,77 @@ mod forgejo {
|
||||||
let fs = given::a_filesystem();
|
let fs = given::a_filesystem();
|
||||||
let repo_details = given::repo_details(&fs);
|
let repo_details = given::repo_details(&fs);
|
||||||
let commit = given::a_commit();
|
let commit = given::a_commit();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
given::a_commit_state("success", &net, &repo_details, &commit);
|
given::a_commit_state("success", &mut net, &repo_details, &commit);
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
assert_eq!(
|
assert_eq!(forge.commit_status(&commit).await, Status::Pass);
|
||||||
forge.commit_status(&commit).await.expect("status"),
|
|
||||||
Status::Pass
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn should_return_pending_for_pending() {
|
async fn should_return_pending_for_pending() {
|
||||||
let fs = given::a_filesystem();
|
let fs = given::a_filesystem();
|
||||||
let repo_details = given::repo_details(&fs);
|
let repo_details = given::repo_details(&fs);
|
||||||
let commit = given::a_commit();
|
let commit = given::a_commit();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
given::a_commit_state("pending", &net, &repo_details, &commit);
|
given::a_commit_state("pending", &mut net, &repo_details, &commit);
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
assert_eq!(
|
assert_eq!(forge.commit_status(&commit).await, Status::Pending);
|
||||||
forge.commit_status(&commit).await.expect("status"),
|
|
||||||
Status::Pending
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn should_return_fail_for_failure() {
|
async fn should_return_fail_for_failure() {
|
||||||
let fs = given::a_filesystem();
|
let fs = given::a_filesystem();
|
||||||
let repo_details = given::repo_details(&fs);
|
let repo_details = given::repo_details(&fs);
|
||||||
let commit = given::a_commit();
|
let commit = given::a_commit();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
given::a_commit_state("failure", &net, &repo_details, &commit);
|
given::a_commit_state("failure", &mut net, &repo_details, &commit);
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
assert_eq!(
|
assert_eq!(forge.commit_status(&commit).await, Status::Fail);
|
||||||
forge.commit_status(&commit).await.expect("status"),
|
|
||||||
Status::Fail
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn should_return_fail_for_error() {
|
async fn should_return_fail_for_error() {
|
||||||
let fs = given::a_filesystem();
|
let fs = given::a_filesystem();
|
||||||
let repo_details = given::repo_details(&fs);
|
let repo_details = given::repo_details(&fs);
|
||||||
let commit = given::a_commit();
|
let commit = given::a_commit();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
given::a_commit_state("error", &net, &repo_details, &commit);
|
given::a_commit_state("error", &mut net, &repo_details, &commit);
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
assert_eq!(
|
assert_eq!(forge.commit_status(&commit).await, Status::Fail);
|
||||||
forge.commit_status(&commit).await.expect("status"),
|
|
||||||
Status::Fail
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn should_return_pending_for_blank() {
|
async fn should_return_pending_for_blank() {
|
||||||
let fs = given::a_filesystem();
|
let fs = given::a_filesystem();
|
||||||
let repo_details = given::repo_details(&fs);
|
let repo_details = given::repo_details(&fs);
|
||||||
let commit = given::a_commit();
|
let commit = given::a_commit();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
given::a_commit_state("", &net, &repo_details, &commit);
|
given::a_commit_state("", &mut net, &repo_details, &commit);
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
assert_eq!(
|
assert_eq!(forge.commit_status(&commit).await, Status::Pending);
|
||||||
forge.commit_status(&commit).await.expect("status"),
|
|
||||||
Status::Pending
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn should_return_pending_for_no_statuses() {
|
async fn should_return_pending_for_no_statuses() {
|
||||||
let fs = given::a_filesystem();
|
let fs = given::a_filesystem();
|
||||||
let repo_details = given::repo_details(&fs);
|
let repo_details = given::repo_details(&fs);
|
||||||
let commit = given::a_commit();
|
let commit = given::a_commit();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
net.on()
|
net.add_get_response(
|
||||||
.get(given::a_commit_status_url(&repo_details, &commit))
|
&given::a_commit_status_url(&repo_details, &commit),
|
||||||
.respond(StatusCode::OK)
|
StatusCode::OK,
|
||||||
.body(
|
"",
|
||||||
json!({
|
|
||||||
"state": "" // blank => Pending
|
|
||||||
})
|
|
||||||
.to_string(),
|
|
||||||
)
|
|
||||||
.expect("mock");
|
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
|
||||||
assert_eq!(
|
|
||||||
forge.commit_status(&commit).await.expect("status"),
|
|
||||||
Status::Pending
|
|
||||||
);
|
);
|
||||||
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
|
assert_eq!(forge.commit_status(&commit).await, Status::Pending);
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn should_return_pending_for_network_error() {
|
async fn should_return_pending_for_network_error() {
|
||||||
let fs = given::a_filesystem();
|
let fs = given::a_filesystem();
|
||||||
let repo_details = given::repo_details(&fs);
|
let repo_details = given::repo_details(&fs);
|
||||||
let commit = given::a_commit();
|
let commit = given::a_commit();
|
||||||
let mock_net = given::a_network();
|
let mut net = given::a_network();
|
||||||
mock_net
|
net.add_get_error(
|
||||||
.on()
|
&given::a_commit_status_url(&repo_details, &commit),
|
||||||
.get(given::a_commit_status_url(&repo_details, &commit))
|
"boom today",
|
||||||
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
.body("book today")
|
|
||||||
.expect("mock");
|
|
||||||
let forge = given::a_forgejo_forge(&repo_details, mock_net);
|
|
||||||
assert_eq!(
|
|
||||||
forge.commit_status(&commit).await.expect("status"),
|
|
||||||
Status::Pending
|
|
||||||
);
|
);
|
||||||
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
|
assert_eq!(forge.commit_status(&commit).await, Status::Pending);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,15 +246,13 @@ mod forgejo {
|
||||||
let hostname = repo_details.forge.hostname();
|
let hostname = repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let token = repo_details.forge.token().expose_secret();
|
let token = repo_details.forge.token().expose_secret();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
|
|
||||||
net.on()
|
net.add_get_error(
|
||||||
.get(format!(
|
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?page=1&token={token}")
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks?page=1&token={token}"
|
.as_str(),
|
||||||
))
|
"error_message",
|
||||||
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
);
|
||||||
.body("error_message")
|
|
||||||
.expect("mock");
|
|
||||||
|
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -303,15 +271,16 @@ mod forgejo {
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let token = repo_details.forge.token().expose_secret();
|
let token = repo_details.forge.token().expose_secret();
|
||||||
let webhook_id = given::a_webhook_id();
|
let webhook_id = given::a_webhook_id();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
|
|
||||||
net.on()
|
net.add_delete_response(
|
||||||
.delete(format!(
|
format!(
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
||||||
))
|
)
|
||||||
.respond(StatusCode::OK)
|
.as_str(),
|
||||||
.body("")
|
StatusCode::OK,
|
||||||
.expect("mock");
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -326,15 +295,15 @@ mod forgejo {
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let token = repo_details.forge.token().expose_secret();
|
let token = repo_details.forge.token().expose_secret();
|
||||||
let webhook_id = given::a_webhook_id();
|
let webhook_id = given::a_webhook_id();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
|
|
||||||
net.on()
|
net.add_delete_error(
|
||||||
.delete(format!(
|
format!(
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
||||||
))
|
)
|
||||||
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
.as_str(),
|
||||||
.body("error")
|
"error",
|
||||||
.expect("mock");
|
);
|
||||||
|
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -374,13 +343,11 @@ mod forgejo {
|
||||||
with::get_webhooks_by_page(1, &[], &mut args);
|
with::get_webhooks_by_page(1, &[], &mut args);
|
||||||
// register the webhook will succeed
|
// register the webhook will succeed
|
||||||
let webhook_id = given::a_forgejo_webhook_id();
|
let webhook_id = given::a_forgejo_webhook_id();
|
||||||
net.on()
|
net.add_post_response(
|
||||||
.post(format!(
|
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}").as_str(),
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
StatusCode::OK,
|
||||||
))
|
json!({"id": webhook_id, "config":{}}).to_string().as_str(),
|
||||||
.respond(StatusCode::OK)
|
);
|
||||||
.body(json!({"id": webhook_id, "config":{}}).to_string())
|
|
||||||
.expect("mock");
|
|
||||||
|
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -448,13 +415,11 @@ mod forgejo {
|
||||||
with::unregister_webhook(&hooks[1], &mut args);
|
with::unregister_webhook(&hooks[1], &mut args);
|
||||||
// register the webhook will succeed
|
// register the webhook will succeed
|
||||||
let webhook_id = given::a_forgejo_webhook_id();
|
let webhook_id = given::a_forgejo_webhook_id();
|
||||||
net.on()
|
net.add_post_response(
|
||||||
.post(format!(
|
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}").as_str(),
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
StatusCode::OK,
|
||||||
))
|
json!({"id": webhook_id, "config":{}}).to_string().as_str(),
|
||||||
.respond(StatusCode::OK)
|
);
|
||||||
.body(json!({"id": webhook_id, "config":{}}).to_string())
|
|
||||||
.expect("mock");
|
|
||||||
|
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -489,19 +454,17 @@ mod forgejo {
|
||||||
// there are no existing matching webhooks
|
// there are no existing matching webhooks
|
||||||
with::get_webhooks_by_page(1, &[], &mut args);
|
with::get_webhooks_by_page(1, &[], &mut args);
|
||||||
// register the webhook will return empty response
|
// register the webhook will return empty response
|
||||||
net.on()
|
net.add_post_response(
|
||||||
.post(format!(
|
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}").as_str(),
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
StatusCode::OK,
|
||||||
))
|
json!({}).to_string().as_str(), // empty response
|
||||||
.respond(StatusCode::OK)
|
);
|
||||||
.body(json!({}).to_string()) // empty response)
|
|
||||||
.expect("mock");
|
|
||||||
|
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
|
|
||||||
let_assert!(Err(err) = forge.register_webhook(&repo_listen_url).await);
|
let_assert!(Err(err) = forge.register_webhook(&repo_listen_url).await);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(err, git::forge::webhook::Error::NetworkResponseEmpty),
|
matches!(err, git::forge::webhook::Error::FailedToRegister(_)),
|
||||||
"{err:?}"
|
"{err:?}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -530,13 +493,10 @@ mod forgejo {
|
||||||
// there are no existing matching webhooks
|
// there are no existing matching webhooks
|
||||||
with::get_webhooks_by_page(1, &[], &mut args);
|
with::get_webhooks_by_page(1, &[], &mut args);
|
||||||
// register the webhook will return empty response
|
// register the webhook will return empty response
|
||||||
net.on()
|
net.add_post_error(
|
||||||
.post(format!(
|
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}").as_str(),
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
"error",
|
||||||
))
|
);
|
||||||
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
.body("error")
|
|
||||||
.expect("mock");
|
|
||||||
|
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -548,6 +508,7 @@ mod forgejo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod with {
|
mod with {
|
||||||
|
use git_next_core::server::RepoListenUrl;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -559,31 +520,31 @@ mod forgejo {
|
||||||
let hostname = args.hostname;
|
let hostname = args.hostname;
|
||||||
let repo_path = args.repo_path;
|
let repo_path = args.repo_path;
|
||||||
let token = args.token;
|
let token = args.token;
|
||||||
args.net
|
args.net.add_get_response(
|
||||||
.on()
|
format!(
|
||||||
.get(format!(
|
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks?page={page}&token={token}"
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?page={page}&token={token}"
|
||||||
))
|
)
|
||||||
.respond(StatusCode::OK)
|
.as_str(),
|
||||||
.body(json!(response).to_string())
|
StatusCode::OK,
|
||||||
.expect("mock");
|
json!(response).to_string().as_str(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
pub fn unregister_webhook(hook1: &ReturnedWebhook, args: &mut WebhookArgs) {
|
pub fn unregister_webhook(hook1: &ReturnedWebhook, args: &mut WebhookArgs) {
|
||||||
let webhook_id = hook1.id;
|
let webhook_id = hook1.id;
|
||||||
let hostname = args.hostname;
|
let hostname = args.hostname;
|
||||||
let repo_path = args.repo_path;
|
let repo_path = args.repo_path;
|
||||||
let token = args.token;
|
let token = args.token;
|
||||||
args.net
|
args.net.add_delete_response(
|
||||||
.on()
|
format!(
|
||||||
.delete(format!(
|
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
||||||
))
|
)
|
||||||
.respond(StatusCode::OK)
|
.as_str(),
|
||||||
.body("")
|
StatusCode::OK,
|
||||||
.expect("mock");
|
"",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
pub struct WebhookArgs<'a> {
|
pub struct WebhookArgs<'a> {
|
||||||
pub net: &'a MockNet,
|
pub net: &'a mut network::MockNetwork,
|
||||||
pub hostname: &'a Hostname,
|
pub hostname: &'a Hostname,
|
||||||
pub repo_path: &'a RepoPath,
|
pub repo_path: &'a RepoPath,
|
||||||
pub token: &'a str,
|
pub token: &'a str,
|
||||||
|
@ -612,21 +573,21 @@ mod forgejo {
|
||||||
|
|
||||||
use git::RepoDetails;
|
use git::RepoDetails;
|
||||||
use git_next_core::server::RepoListenUrl;
|
use git_next_core::server::RepoListenUrl;
|
||||||
use kxio::net::{MockNet, StatusCode};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn a_commit_state(
|
pub fn a_commit_state(
|
||||||
state: impl AsRef<str>,
|
state: impl AsRef<str>,
|
||||||
net: &MockNet,
|
net: &mut MockNetwork,
|
||||||
repo_details: &git::RepoDetails,
|
repo_details: &git::RepoDetails,
|
||||||
commit: &git::Commit,
|
commit: &git::Commit,
|
||||||
) {
|
) {
|
||||||
net.on()
|
let response = json!({"state":state.as_ref()});
|
||||||
.get(a_commit_status_url(repo_details, commit))
|
net.add_get_response(
|
||||||
.respond(StatusCode::OK)
|
a_commit_status_url(repo_details, commit).as_str(),
|
||||||
.body((json!({"state":state.as_ref()})).to_string())
|
StatusCode::OK,
|
||||||
.expect("mock");
|
response.to_string().as_str(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_commit_status_url(
|
pub fn a_commit_status_url(
|
||||||
|
@ -683,10 +644,9 @@ mod forgejo {
|
||||||
|
|
||||||
pub fn a_forgejo_forge(
|
pub fn a_forgejo_forge(
|
||||||
repo_details: &git::RepoDetails,
|
repo_details: &git::RepoDetails,
|
||||||
net: impl Into<kxio::net::Net>,
|
net: impl Into<kxio::network::Network>,
|
||||||
) -> ForgeJo {
|
) -> ForgeJo {
|
||||||
let net: kxio::net::Net = net.into();
|
ForgeJo::new(repo_details.clone(), net.into())
|
||||||
ForgeJo::new(repo_details.clone(), net)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_forgejo_webhook_id() -> i64 {
|
pub fn a_forgejo_webhook_id() -> i64 {
|
||||||
|
@ -714,8 +674,8 @@ mod forgejo {
|
||||||
RepoAlias::new(a_name())
|
RepoAlias::new(a_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_network() -> kxio::net::MockNet {
|
pub fn a_network() -> kxio::network::MockNetwork {
|
||||||
kxio::net::mock()
|
kxio::network::MockNetwork::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_name() -> String {
|
pub fn a_name() -> String {
|
||||||
|
@ -798,8 +758,8 @@ mod forgejo {
|
||||||
git::commit::Sha::new(a_name())
|
git::commit::Sha::new(a_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_filesystem() -> kxio::fs::TempFileSystem {
|
pub fn a_filesystem() -> kxio::fs::FileSystem {
|
||||||
kxio::fs::temp().expect("temp fs")
|
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repo_details(fs: &kxio::fs::FileSystem) -> git::RepoDetails {
|
pub fn repo_details(fs: &kxio::fs::FileSystem) -> git::RepoDetails {
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
//
|
//
|
||||||
use git_next_core::{git, server::RepoListenUrl, WebhookId};
|
use git_next_core::{git, server::RepoListenUrl, WebhookId};
|
||||||
use kxio::net::Net;
|
|
||||||
|
use kxio::network;
|
||||||
|
|
||||||
use crate::webhook::Hook;
|
use crate::webhook::Hook;
|
||||||
|
|
||||||
pub async fn list(
|
pub async fn list(
|
||||||
repo_details: &git::RepoDetails,
|
repo_details: &git::RepoDetails,
|
||||||
repo_listen_url: &RepoListenUrl,
|
repo_listen_url: &RepoListenUrl,
|
||||||
net: &Net,
|
net: &network::Network,
|
||||||
) -> git::forge::webhook::Result<Vec<WebhookId>> {
|
) -> git::forge::webhook::Result<Vec<WebhookId>> {
|
||||||
let mut ids: Vec<WebhookId> = vec![];
|
let mut ids: Vec<WebhookId> = vec![];
|
||||||
let hostname = &repo_details.forge.hostname();
|
let hostname = &repo_details.forge.hostname();
|
||||||
|
@ -16,15 +17,22 @@ pub async fn list(
|
||||||
loop {
|
loop {
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
let token = &repo_details.forge.token().expose_secret();
|
let token = &repo_details.forge.token().expose_secret();
|
||||||
match net
|
let url =
|
||||||
.get(format!(
|
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?page={page}&token={token}");
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks?page={page}&token={token}"
|
let net_url = network::NetUrl::new(url);
|
||||||
))
|
let request = network::NetRequest::new(
|
||||||
.send()
|
network::RequestMethod::Get,
|
||||||
.await
|
net_url,
|
||||||
{
|
network::NetRequestHeaders::new(),
|
||||||
|
network::RequestBody::None,
|
||||||
|
network::ResponseType::Json,
|
||||||
|
None,
|
||||||
|
network::NetRequestLogging::None,
|
||||||
|
);
|
||||||
|
let result = net.get::<Vec<Hook>>(request).await;
|
||||||
|
match result {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
if let Ok(list) = response.json::<Vec<Hook>>().await {
|
if let Some(list) = response.response_body() {
|
||||||
if list.is_empty() {
|
if list.is_empty() {
|
||||||
return Ok(ids);
|
return Ok(ids);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
//
|
//
|
||||||
use git_next_core::{git, server::RepoListenUrl, RegisteredWebhook, WebhookAuth, WebhookId};
|
use git_next_core::{git, server::RepoListenUrl, RegisteredWebhook, WebhookAuth, WebhookId};
|
||||||
|
|
||||||
use kxio::net::Net;
|
use kxio::network;
|
||||||
use secrecy::ExposeSecret as _;
|
use secrecy::ExposeSecret as _;
|
||||||
use serde_json::json;
|
|
||||||
use tracing::{info, instrument, warn};
|
use tracing::{info, instrument, warn};
|
||||||
|
|
||||||
use crate::webhook;
|
use crate::webhook;
|
||||||
|
@ -13,7 +12,7 @@ use crate::webhook::Hook;
|
||||||
pub async fn register(
|
pub async fn register(
|
||||||
repo_details: &git::RepoDetails,
|
repo_details: &git::RepoDetails,
|
||||||
repo_listen_url: &RepoListenUrl,
|
repo_listen_url: &RepoListenUrl,
|
||||||
net: &Net,
|
net: &network::Network,
|
||||||
) -> git::forge::webhook::Result<RegisteredWebhook> {
|
) -> git::forge::webhook::Result<RegisteredWebhook> {
|
||||||
let Some(repo_config) = repo_details.repo_config.clone() else {
|
let Some(repo_config) = repo_details.repo_config.clone() else {
|
||||||
return Err(git::forge::webhook::Error::NoRepoConfig);
|
return Err(git::forge::webhook::Error::NoRepoConfig);
|
||||||
|
@ -28,27 +27,35 @@ pub async fn register(
|
||||||
let hostname = &repo_details.forge.hostname();
|
let hostname = &repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let token = repo_details.forge.token().expose_secret();
|
let token = repo_details.forge.token().expose_secret();
|
||||||
let url = format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}");
|
let url = network::NetUrl::new(format!(
|
||||||
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
||||||
|
));
|
||||||
|
let headers = network::NetRequestHeaders::new().with("Content-Type", "application/json");
|
||||||
let authorisation = WebhookAuth::generate();
|
let authorisation = WebhookAuth::generate();
|
||||||
match net
|
let body = network::json!({
|
||||||
.post(url)
|
"active": true,
|
||||||
.header("Content-Type", "application/json")
|
"authorization_header": authorisation.header_value(),
|
||||||
.body(json!({
|
"branch_filter": format!("{{{},{},{}}}", repo_config.branches().main(), repo_config.branches().next(), repo_config.branches().dev()),
|
||||||
"active": true,
|
"config": {
|
||||||
"authorization_header": authorisation.header_value(),
|
"content_type": "json",
|
||||||
"branch_filter": format!("{{{},{},{}}}", repo_config.branches().main(), repo_config.branches().next(), repo_config.branches().dev()),
|
"url": repo_listen_url.to_string(),
|
||||||
"config": {
|
},
|
||||||
"content_type": "json",
|
"events": [ "push" ],
|
||||||
"url": repo_listen_url.to_string(),
|
"type": "forgejo"
|
||||||
},
|
});
|
||||||
"events": [ "push" ],
|
let request = network::NetRequest::new(
|
||||||
"type": "forgejo"
|
network::RequestMethod::Post,
|
||||||
}).to_string())
|
url,
|
||||||
.send()
|
headers,
|
||||||
.await
|
network::RequestBody::Json(body),
|
||||||
{
|
network::ResponseType::Json,
|
||||||
|
None,
|
||||||
|
network::NetRequestLogging::None,
|
||||||
|
);
|
||||||
|
let result = net.post_json::<Hook>(request).await;
|
||||||
|
match result {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
let Ok(hook) = response.json::<Hook>().await else {
|
let Some(hook) = response.response_body() else {
|
||||||
#[cfg(not(tarpaulin_include))]
|
#[cfg(not(tarpaulin_include))]
|
||||||
// request response is Json so response_body never returns None
|
// request response is Json so response_body never returns None
|
||||||
return Err(git::forge::webhook::Error::NetworkResponseEmpty);
|
return Err(git::forge::webhook::Error::NetworkResponseEmpty);
|
||||||
|
@ -64,5 +71,4 @@ pub async fn register(
|
||||||
Err(git::forge::webhook::Error::FailedToRegister(e.to_string()))
|
Err(git::forge::webhook::Error::FailedToRegister(e.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,30 @@
|
||||||
//
|
//
|
||||||
use git_next_core::{git, WebhookId};
|
use git_next_core::{git, WebhookId};
|
||||||
|
|
||||||
use kxio::net::Net;
|
use kxio::network;
|
||||||
use secrecy::ExposeSecret as _;
|
use secrecy::ExposeSecret as _;
|
||||||
|
|
||||||
pub async fn unregister(
|
pub async fn unregister(
|
||||||
webhook_id: &WebhookId,
|
webhook_id: &WebhookId,
|
||||||
repo_details: &git::RepoDetails,
|
repo_details: &git::RepoDetails,
|
||||||
net: &Net,
|
net: &network::Network,
|
||||||
) -> git::forge::webhook::Result<()> {
|
) -> git::forge::webhook::Result<()> {
|
||||||
let hostname = &repo_details.forge.hostname();
|
let hostname = &repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let token = repo_details.forge.token().expose_secret();
|
let token = repo_details.forge.token().expose_secret();
|
||||||
match net
|
let url = network::NetUrl::new(format!(
|
||||||
.delete(format!(
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
));
|
||||||
))
|
let request = network::NetRequest::new(
|
||||||
.send()
|
network::RequestMethod::Delete,
|
||||||
.await
|
url,
|
||||||
{
|
network::NetRequestHeaders::new(),
|
||||||
|
network::RequestBody::None,
|
||||||
|
network::ResponseType::None,
|
||||||
|
None,
|
||||||
|
network::NetRequestLogging::None,
|
||||||
|
);
|
||||||
|
match net.delete(request).await {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!("Failed to unregister webhook");
|
tracing::warn!("Failed to unregister webhook");
|
||||||
Err(git::forge::webhook::Error::FailedToUnregister(
|
Err(git::forge::webhook::Error::FailedToUnregister(
|
||||||
|
|
|
@ -2,46 +2,53 @@
|
||||||
use crate::{self as github, GithubState};
|
use crate::{self as github, GithubState};
|
||||||
use git_next_core::git::{self, forge::commit::Status};
|
use git_next_core::git::{self, forge::commit::Status};
|
||||||
use github::GithubStatus;
|
use github::GithubStatus;
|
||||||
|
use kxio::network;
|
||||||
|
|
||||||
/// Checks the results of any (e.g. CI) status checks for the commit.
|
/// Checks the results of any (e.g. CI) status checks for the commit.
|
||||||
///
|
///
|
||||||
/// GitHub: <https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#list-commit-statuses-for-a-reference>
|
/// GitHub: <https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#list-commit-statuses-for-a-reference>
|
||||||
pub async fn status(
|
pub async fn status(github: &github::Github, commit: &git::Commit) -> git::forge::commit::Status {
|
||||||
github: &github::Github,
|
|
||||||
commit: &git::Commit,
|
|
||||||
) -> git::forge::webhook::Result<git::forge::commit::Status> {
|
|
||||||
let repo_details = &github.repo_details;
|
let repo_details = &github.repo_details;
|
||||||
let hostname = repo_details.forge.hostname();
|
let hostname = repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let url = format!("https://api.{hostname}/repos/{repo_path}/commits/{commit}/statuses");
|
let url = network::NetUrl::new(format!(
|
||||||
let Ok(response) = github
|
"https://api.{hostname}/repos/{repo_path}/commits/{commit}/statuses"
|
||||||
.net
|
));
|
||||||
.get(url)
|
let headers = github::webhook::headers(repo_details.forge.token());
|
||||||
.headers(github::webhook::headers(repo_details.forge.token()))
|
let request = network::NetRequest::new(
|
||||||
.body("")
|
network::RequestMethod::Get,
|
||||||
.send()
|
url,
|
||||||
.await
|
headers,
|
||||||
else {
|
network::RequestBody::None,
|
||||||
return Ok(Status::Pending);
|
network::ResponseType::Json,
|
||||||
};
|
None,
|
||||||
let statuses = response.json::<Vec<GithubStatus>>().await?;
|
network::NetRequestLogging::None,
|
||||||
let result = statuses
|
);
|
||||||
.into_iter()
|
let result = github.net.get::<Vec<GithubStatus>>(request).await;
|
||||||
.map(|status| match status.state {
|
match result {
|
||||||
GithubState::Success => Status::Pass,
|
Ok(response) => response
|
||||||
GithubState::Pending | GithubState::Blank => Status::Pending,
|
.response_body()
|
||||||
GithubState::Failure | GithubState::Error => Status::Fail,
|
.and_then(|statuses| {
|
||||||
})
|
statuses
|
||||||
.reduce(|l, r| match (l, r) {
|
.into_iter()
|
||||||
(Status::Pass, Status::Pass) => Status::Pass,
|
.map(|status| match status.state {
|
||||||
(_, Status::Fail) | (Status::Fail, _) => Status::Fail,
|
GithubState::Success => Status::Pass,
|
||||||
(_, Status::Pending) | (Status::Pending, _) => Status::Pending,
|
GithubState::Pending | GithubState::Blank => Status::Pending,
|
||||||
(Status::Error(e1), Status::Error(e2)) => Status::Error(format!("{e1} / {e2}")),
|
GithubState::Failure | GithubState::Error => Status::Fail,
|
||||||
(_, Status::Error(err)) | (Status::Error(err), _) => Status::Error(err),
|
})
|
||||||
})
|
.reduce(|l, r| match (l, r) {
|
||||||
.unwrap_or_else(|| {
|
(Status::Pass, Status::Pass) => Status::Pass,
|
||||||
tracing::warn!("No status checks configured for 'next' branch",);
|
(_, Status::Fail) | (Status::Fail, _) => Status::Fail,
|
||||||
Status::Pass
|
(_, Status::Pending) | (Status::Pending, _) => Status::Pending,
|
||||||
});
|
})
|
||||||
Ok(result)
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
tracing::warn!("No status checks configured for 'next' branch",);
|
||||||
|
Status::Pass
|
||||||
|
}),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(?e, "Failed to get commit status");
|
||||||
|
Status::Pending // assume issue is transient and allow retry
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ mod webhook;
|
||||||
|
|
||||||
use crate as github;
|
use crate as github;
|
||||||
use git_next_core::{
|
use git_next_core::{
|
||||||
self as core, git,
|
self as core,
|
||||||
|
git::{self, forge::commit::Status},
|
||||||
server::{self, RepoListenUrl},
|
server::{self, RepoListenUrl},
|
||||||
ForgeNotification, RegisteredWebhook, WebhookAuth, WebhookId,
|
ForgeNotification, RegisteredWebhook, WebhookAuth, WebhookId,
|
||||||
};
|
};
|
||||||
|
@ -17,7 +18,7 @@ use derive_more::Constructor;
|
||||||
#[derive(Clone, Debug, Constructor)]
|
#[derive(Clone, Debug, Constructor)]
|
||||||
pub struct Github {
|
pub struct Github {
|
||||||
repo_details: git::RepoDetails,
|
repo_details: git::RepoDetails,
|
||||||
net: kxio::net::Net,
|
net: kxio::network::Network,
|
||||||
}
|
}
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl git::ForgeLike for Github {
|
impl git::ForgeLike for Github {
|
||||||
|
@ -51,10 +52,7 @@ impl git::ForgeLike for Github {
|
||||||
github::webhook::parse_body(body)
|
github::webhook::parse_body(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn commit_status(
|
async fn commit_status(&self, commit: &git::Commit) -> Status {
|
||||||
&self,
|
|
||||||
commit: &git::Commit,
|
|
||||||
) -> git::forge::webhook::Result<git::forge::commit::Status> {
|
|
||||||
github::commit::status(self, commit).await
|
github::commit::status(self, commit).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
//
|
//
|
||||||
#![allow(clippy::expect_used)]
|
|
||||||
|
|
||||||
use crate::{Github, GithubState, GithubStatus};
|
use crate::{Github, GithubState, GithubStatus};
|
||||||
use git_next_core::{
|
use git_next_core::{
|
||||||
git::{self, forge::commit::Status, ForgeLike},
|
git::{self, forge::commit::Status, ForgeLike},
|
||||||
|
@ -11,7 +9,7 @@ use git_next_core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use assert2::let_assert;
|
use assert2::let_assert;
|
||||||
use kxio::net::{MockNet, StatusCode};
|
use kxio::network::{self, MockNetwork, StatusCode};
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -101,10 +99,10 @@ mod github {
|
||||||
let (states, expected) = $value;
|
let (states, expected) = $value;
|
||||||
let repo_details = given::repo_details();
|
let repo_details = given::repo_details();
|
||||||
let commit = given::a_commit();
|
let commit = given::a_commit();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
given::commit_states(&states, &net, &repo_details, &commit);
|
given::commit_states(&states, &mut net, &repo_details, &commit);
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
assert_eq!(forge.commit_status(&commit).await.expect("status"), expected);
|
assert_eq!(forge.commit_status(&commit).await, expected);
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
@ -127,37 +125,29 @@ mod github {
|
||||||
);
|
);
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn should_return_pass_for_no_statuses() {
|
async fn should_return_pending_for_no_statuses() {
|
||||||
let repo_details = given::repo_details();
|
let repo_details = given::repo_details();
|
||||||
let commit = given::a_commit();
|
let commit = given::a_commit();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
net.on()
|
net.add_get_response(
|
||||||
.get(given::a_commit_status_url(&repo_details, &commit))
|
&given::a_commit_status_url(&repo_details, &commit),
|
||||||
.respond(StatusCode::OK)
|
StatusCode::OK,
|
||||||
.body(json!([]).to_string())
|
"",
|
||||||
.expect("mock");
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
|
||||||
assert_eq!(
|
|
||||||
forge.commit_status(&commit).await.expect("status"),
|
|
||||||
Status::Pass // no CI checks configured
|
|
||||||
);
|
);
|
||||||
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
assert_eq!(forge.commit_status(&commit).await, Status::Pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn should_return_pending_for_network_error() {
|
async fn should_return_pending_for_network_error() {
|
||||||
let repo_details = given::repo_details();
|
let repo_details = given::repo_details();
|
||||||
let commit = given::a_commit();
|
let commit = given::a_commit();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
net.on()
|
net.add_get_error(
|
||||||
.get(given::a_commit_status_url(&repo_details, &commit))
|
&given::a_commit_status_url(&repo_details, &commit),
|
||||||
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
"boom today",
|
||||||
.body("boom today")
|
|
||||||
.expect("mock");
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
|
||||||
assert_eq!(
|
|
||||||
forge.commit_status(&commit).await.expect("status"),
|
|
||||||
Status::Pending
|
|
||||||
);
|
);
|
||||||
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
assert_eq!(forge.commit_status(&commit).await, Status::Pending);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,9 +164,9 @@ mod github {
|
||||||
let hook_id_1 = given::a_github_webhook_id();
|
let hook_id_1 = given::a_github_webhook_id();
|
||||||
let hook_id_2 = given::a_github_webhook_id();
|
let hook_id_2 = given::a_github_webhook_id();
|
||||||
let hook_id_3 = given::a_github_webhook_id();
|
let hook_id_3 = given::a_github_webhook_id();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
let args = with::WebhookArgs {
|
let mut args = with::WebhookArgs {
|
||||||
net: &net,
|
net: &mut net,
|
||||||
hostname,
|
hostname,
|
||||||
repo_path,
|
repo_path,
|
||||||
};
|
};
|
||||||
|
@ -188,10 +178,10 @@ mod github {
|
||||||
with::ReturnedWebhook::new(hook_id_2, &repo_listen_url),
|
with::ReturnedWebhook::new(hook_id_2, &repo_listen_url),
|
||||||
with::ReturnedWebhook::new(hook_id_3, &given::any_webhook_url()),
|
with::ReturnedWebhook::new(hook_id_3, &given::any_webhook_url()),
|
||||||
],
|
],
|
||||||
&args,
|
&mut args,
|
||||||
);
|
);
|
||||||
// page 2 with no items - stops pagination
|
// page 2 with no items - stops pagination
|
||||||
with::get_webhooks_by_page(2, &[], &args);
|
with::get_webhooks_by_page(2, &[], &mut args);
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -211,14 +201,11 @@ mod github {
|
||||||
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
||||||
let hostname = repo_details.forge.hostname();
|
let hostname = repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
net.on()
|
net.add_get_error(
|
||||||
.get(format!(
|
format!("https://api.{hostname}/repos/{repo_path}/hooks?page=1").as_str(),
|
||||||
"https://api.{hostname}/repos/{repo_path}/hooks?page=1"
|
"error_message",
|
||||||
))
|
);
|
||||||
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
.body("error_message")
|
|
||||||
.expect("mock");
|
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -235,14 +222,12 @@ mod github {
|
||||||
let hostname = repo_details.forge.hostname();
|
let hostname = repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let webhook_id = given::a_webhook_id();
|
let webhook_id = given::a_webhook_id();
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
net.on()
|
net.add_delete_response(
|
||||||
.delete(format!(
|
format!("https://api.{hostname}/repos/{repo_path}/hooks/{webhook_id}").as_str(),
|
||||||
"https://api.{hostname}/repos/{repo_path}/hooks/{webhook_id}"
|
StatusCode::OK,
|
||||||
))
|
"",
|
||||||
.respond(StatusCode::OK)
|
);
|
||||||
.body("")
|
|
||||||
.expect("mock");
|
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -257,19 +242,16 @@ mod github {
|
||||||
);
|
);
|
||||||
let hostname = repo_details.forge.hostname();
|
let hostname = repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
// unregister the webhook will return empty response
|
// unregister the webhook will return empty response
|
||||||
let webhook_id = given::a_webhook_id();
|
net.add_delete_error(
|
||||||
net.on()
|
format!("https://api.{hostname}/repos/{repo_path}/hooks").as_str(),
|
||||||
.delete(format!(
|
"error",
|
||||||
"https://api.{hostname}/repos/{repo_path}/hooks/{webhook_id}"
|
);
|
||||||
))
|
|
||||||
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
.mock()
|
|
||||||
.expect("mock");
|
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
let webhook_id = given::a_webhook_id();
|
||||||
let_assert!(Err(err) = forge.unregister_webhook(&webhook_id).await);
|
let_assert!(Err(err) = forge.unregister_webhook(&webhook_id).await);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(err, git::forge::webhook::Error::FailedToRegister(_)),
|
matches!(err, git::forge::webhook::Error::FailedToRegister(_)),
|
||||||
|
@ -292,24 +274,23 @@ mod github {
|
||||||
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
||||||
let hostname = repo_details.forge.hostname();
|
let hostname = repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
let args = with::WebhookArgs {
|
let mut args = with::WebhookArgs {
|
||||||
net: &net,
|
net: &mut net,
|
||||||
hostname,
|
hostname,
|
||||||
repo_path,
|
repo_path,
|
||||||
};
|
};
|
||||||
// there are no existing matching webhooks
|
// there are no existing matching webhooks
|
||||||
with::get_webhooks_by_page(1, &[], &args);
|
with::get_webhooks_by_page(1, &[], &mut args);
|
||||||
// register the webhook will succeed
|
// register the webhook will succeed
|
||||||
let webhook_id = given::a_github_webhook_id();
|
let webhook_id = given::a_github_webhook_id();
|
||||||
net.on()
|
net.add_post_response(
|
||||||
.post(format!("https://api.{hostname}/repos/{repo_path}/hooks"))
|
format!("https://api.{hostname}/repos/{repo_path}/hooks").as_str(),
|
||||||
.respond(StatusCode::OK)
|
StatusCode::OK,
|
||||||
.body(
|
json!({"id": webhook_id, "config":{"url": repo_listen_url.to_string()}})
|
||||||
json!({"id": webhook_id, "config":{"url": repo_listen_url.to_string()}})
|
.to_string()
|
||||||
.to_string(),
|
.as_str(),
|
||||||
)
|
);
|
||||||
.expect("mock");
|
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -348,9 +329,9 @@ mod github {
|
||||||
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
||||||
let hostname = repo_details.forge.hostname();
|
let hostname = repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
let args = with::WebhookArgs {
|
let mut args = with::WebhookArgs {
|
||||||
net: &net,
|
net: &mut net,
|
||||||
hostname,
|
hostname,
|
||||||
repo_path,
|
repo_path,
|
||||||
};
|
};
|
||||||
|
@ -360,21 +341,20 @@ mod github {
|
||||||
with::ReturnedWebhook::new(given::a_github_webhook_id(), &given::any_webhook_url());
|
with::ReturnedWebhook::new(given::a_github_webhook_id(), &given::any_webhook_url());
|
||||||
let hooks = [hook_1, hook_2, hook_3];
|
let hooks = [hook_1, hook_2, hook_3];
|
||||||
// there are three existing webhooks, two are matching webhooks
|
// there are three existing webhooks, two are matching webhooks
|
||||||
with::get_webhooks_by_page(1, &hooks, &args);
|
with::get_webhooks_by_page(1, &hooks, &mut args);
|
||||||
with::get_webhooks_by_page(2, &[], &args);
|
with::get_webhooks_by_page(2, &[], &mut args);
|
||||||
// should unregister 1 and 2, but not 3
|
// should unregister 1 and 2, but not 3
|
||||||
with::unregister_webhook(&hooks[0], &args);
|
with::unregister_webhook(&hooks[0], &mut args);
|
||||||
with::unregister_webhook(&hooks[1], &args);
|
with::unregister_webhook(&hooks[1], &mut args);
|
||||||
// register the webhook will succeed
|
// register the webhook will succeed
|
||||||
let webhook_id = given::a_github_webhook_id();
|
let webhook_id = given::a_github_webhook_id();
|
||||||
net.on()
|
net.add_post_response(
|
||||||
.post(format!("https://api.{hostname}/repos/{repo_path}/hooks"))
|
format!("https://api.{hostname}/repos/{repo_path}/hooks").as_str(),
|
||||||
.respond(StatusCode::OK)
|
StatusCode::OK,
|
||||||
.body(
|
json!({"id": webhook_id, "config":{"url": repo_listen_url.to_string()}})
|
||||||
json!({"id": webhook_id, "config":{"url": repo_listen_url.to_string()}})
|
.to_string()
|
||||||
.to_string(),
|
.as_str(),
|
||||||
)
|
);
|
||||||
.expect("mock");
|
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -395,26 +375,26 @@ mod github {
|
||||||
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
||||||
let hostname = repo_details.forge.hostname();
|
let hostname = repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
let args = with::WebhookArgs {
|
let mut args = with::WebhookArgs {
|
||||||
net: &net,
|
net: &mut net,
|
||||||
hostname,
|
hostname,
|
||||||
repo_path,
|
repo_path,
|
||||||
};
|
};
|
||||||
// there are no existing matching webhooks
|
// there are no existing matching webhooks
|
||||||
with::get_webhooks_by_page(1, &[], &args);
|
with::get_webhooks_by_page(1, &[], &mut args);
|
||||||
// register the webhook will return empty response
|
// register the webhook will return empty response
|
||||||
net.on()
|
net.add_post_response(
|
||||||
.post(format!("https://api.{hostname}/repos/{repo_path}/hooks"))
|
format!("https://api.{hostname}/repos/{repo_path}/hooks").as_str(),
|
||||||
.respond(StatusCode::OK)
|
StatusCode::OK,
|
||||||
.body(json!({}).to_string())
|
json!({}).to_string().as_str(), // empty response
|
||||||
.expect("mock");
|
);
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
let_assert!(Err(err) = forge.register_webhook(&repo_listen_url).await);
|
let_assert!(Err(err) = forge.register_webhook(&repo_listen_url).await);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(err, git::forge::webhook::Error::NetworkResponseEmpty),
|
matches!(err, git::forge::webhook::Error::FailedToRegister(_)),
|
||||||
"{err:?}"
|
"{err:?}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -429,20 +409,19 @@ mod github {
|
||||||
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
||||||
let hostname = repo_details.forge.hostname();
|
let hostname = repo_details.forge.hostname();
|
||||||
let repo_path = &repo_details.repo_path;
|
let repo_path = &repo_details.repo_path;
|
||||||
let net = given::a_network();
|
let mut net = given::a_network();
|
||||||
let args = with::WebhookArgs {
|
let mut args = with::WebhookArgs {
|
||||||
net: &net,
|
net: &mut net,
|
||||||
hostname,
|
hostname,
|
||||||
repo_path,
|
repo_path,
|
||||||
};
|
};
|
||||||
// there are no existing matching webhooks
|
// there are no existing matching webhooks
|
||||||
with::get_webhooks_by_page(1, &[], &args);
|
with::get_webhooks_by_page(1, &[], &mut args);
|
||||||
// register the webhook will return empty response
|
// register the webhook will return empty response
|
||||||
net.on()
|
net.add_post_error(
|
||||||
.post(format!("https://api.{hostname}/repos/{repo_path}/hooks"))
|
format!("https://api.{hostname}/repos/{repo_path}/hooks").as_str(),
|
||||||
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
"error",
|
||||||
.body("error")
|
);
|
||||||
.expect("mock");
|
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -459,34 +438,32 @@ mod github {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn get_webhooks_by_page(page: u8, response: &[ReturnedWebhook], args: &WebhookArgs) {
|
pub fn get_webhooks_by_page(
|
||||||
|
page: u8,
|
||||||
|
response: &[ReturnedWebhook],
|
||||||
|
args: &mut WebhookArgs,
|
||||||
|
) {
|
||||||
let hostname = args.hostname;
|
let hostname = args.hostname;
|
||||||
let repo_path = args.repo_path;
|
let repo_path = args.repo_path;
|
||||||
args.net
|
args.net.add_get_response(
|
||||||
.on()
|
format!("https://api.{hostname}/repos/{repo_path}/hooks?page={page}").as_str(),
|
||||||
.get(format!(
|
StatusCode::OK,
|
||||||
"https://api.{hostname}/repos/{repo_path}/hooks?page={page}"
|
json!(response).to_string().as_str(),
|
||||||
))
|
);
|
||||||
.respond(StatusCode::OK)
|
|
||||||
.body(json!(response).to_string())
|
|
||||||
.expect("mock");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unregister_webhook(hook1: &ReturnedWebhook, args: &WebhookArgs) {
|
pub fn unregister_webhook(hook1: &ReturnedWebhook, args: &mut WebhookArgs) {
|
||||||
let webhook_id = hook1.id;
|
let webhook_id = hook1.id;
|
||||||
let hostname = args.hostname;
|
let hostname = args.hostname;
|
||||||
let repo_path = args.repo_path;
|
let repo_path = args.repo_path;
|
||||||
args.net
|
args.net.add_delete_response(
|
||||||
.on()
|
format!("https://api.{hostname}/repos/{repo_path}/hooks/{webhook_id}").as_str(),
|
||||||
.delete(format!(
|
StatusCode::OK,
|
||||||
"https://api.{hostname}/repos/{repo_path}/hooks/{webhook_id}"
|
"",
|
||||||
))
|
);
|
||||||
.respond(StatusCode::OK)
|
|
||||||
.body("")
|
|
||||||
.expect("mock");
|
|
||||||
}
|
}
|
||||||
pub struct WebhookArgs<'a> {
|
pub struct WebhookArgs<'a> {
|
||||||
pub net: &'a kxio::net::MockNet,
|
pub net: &'a mut network::MockNetwork,
|
||||||
pub hostname: &'a Hostname,
|
pub hostname: &'a Hostname,
|
||||||
pub repo_path: &'a RepoPath,
|
pub repo_path: &'a RepoPath,
|
||||||
}
|
}
|
||||||
|
@ -520,23 +497,21 @@ mod github {
|
||||||
|
|
||||||
pub fn commit_states(
|
pub fn commit_states(
|
||||||
states: &[GithubState],
|
states: &[GithubState],
|
||||||
net: &MockNet,
|
net: &mut MockNetwork,
|
||||||
repo_details: &git::RepoDetails,
|
repo_details: &git::RepoDetails,
|
||||||
commit: &git::Commit,
|
commit: &git::Commit,
|
||||||
) {
|
) {
|
||||||
net.on()
|
let response = json!(states
|
||||||
.get(a_commit_status_url(repo_details, commit))
|
.iter()
|
||||||
.respond(StatusCode::OK)
|
.map(|state| GithubStatus {
|
||||||
.body(
|
state: state.to_owned()
|
||||||
json!(states
|
})
|
||||||
.iter()
|
.collect::<Vec<_>>());
|
||||||
.map(|state| GithubStatus {
|
net.add_get_response(
|
||||||
state: state.to_owned()
|
a_commit_status_url(repo_details, commit).as_str(),
|
||||||
})
|
StatusCode::OK,
|
||||||
.collect::<Vec<_>>())
|
response.to_string().as_str(),
|
||||||
.to_string(),
|
);
|
||||||
)
|
|
||||||
.expect("mock");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_commit_status_url(
|
pub fn a_commit_status_url(
|
||||||
|
@ -603,11 +578,10 @@ mod github {
|
||||||
|
|
||||||
pub fn a_github_forge(
|
pub fn a_github_forge(
|
||||||
repo_details: &git::RepoDetails,
|
repo_details: &git::RepoDetails,
|
||||||
net: impl Into<kxio::net::Net>,
|
net: impl Into<kxio::network::Network>,
|
||||||
) -> Github {
|
) -> Github {
|
||||||
Github::new(repo_details.clone(), net.into())
|
Github::new(repo_details.clone(), net.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repo_details() -> git::RepoDetails {
|
pub fn repo_details() -> git::RepoDetails {
|
||||||
git::RepoDetails::new(
|
git::RepoDetails::new(
|
||||||
git::Generation::default(),
|
git::Generation::default(),
|
||||||
|
@ -640,8 +614,8 @@ mod github {
|
||||||
pub fn a_repo_alias() -> RepoAlias {
|
pub fn a_repo_alias() -> RepoAlias {
|
||||||
RepoAlias::new(a_name())
|
RepoAlias::new(a_name())
|
||||||
}
|
}
|
||||||
pub fn a_network() -> kxio::net::MockNet {
|
pub fn a_network() -> kxio::network::MockNetwork {
|
||||||
kxio::net::mock()
|
kxio::network::MockNetwork::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_repo_listen_url(repo_details: &RepoDetails) -> RepoListenUrl {
|
pub fn a_repo_listen_url(repo_details: &RepoDetails) -> RepoListenUrl {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
use crate as github;
|
use crate as github;
|
||||||
use git_next_core::{git, server::RepoListenUrl, WebhookId};
|
use git_next_core::{git, server::RepoListenUrl, WebhookId};
|
||||||
|
|
||||||
|
use kxio::network;
|
||||||
|
|
||||||
// https://docs.github.com/en/rest/repos/webhooks?apiVersion=2022-11-28#list-repository-webhooks
|
// https://docs.github.com/en/rest/repos/webhooks?apiVersion=2022-11-28#list-repository-webhooks
|
||||||
pub async fn list(
|
pub async fn list(
|
||||||
github: &github::Github,
|
github: &github::Github,
|
||||||
|
@ -13,36 +15,40 @@ pub async fn list(
|
||||||
let net = &github.net;
|
let net = &github.net;
|
||||||
let mut page = 1;
|
let mut page = 1;
|
||||||
loop {
|
loop {
|
||||||
let result = net
|
let request = network::NetRequest::new(
|
||||||
.get(format!(
|
network::RequestMethod::Get,
|
||||||
|
network::NetUrl::new(format!(
|
||||||
"https://api.{hostname}/repos/{}/hooks?page={page}",
|
"https://api.{hostname}/repos/{}/hooks?page={page}",
|
||||||
repo_details.repo_path,
|
repo_details.repo_path,
|
||||||
))
|
)),
|
||||||
.headers(github::webhook::headers(repo_details.forge.token()))
|
github::webhook::headers(repo_details.forge.token()),
|
||||||
.send()
|
network::RequestBody::None,
|
||||||
.await;
|
network::ResponseType::Json,
|
||||||
|
None,
|
||||||
|
network::NetRequestLogging::None,
|
||||||
|
);
|
||||||
|
let result = net.get::<Vec<github::GithubHook>>(request).await;
|
||||||
match result {
|
match result {
|
||||||
Ok(response) => match response.json::<Vec<github::GithubHook>>().await {
|
Ok(response) => {
|
||||||
Err(err) => {
|
let Some(list) = response.response_body() else {
|
||||||
tracing::warn!(?err, "failed");
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
// request response is Json so response_body never returns None
|
||||||
return Err(git::forge::webhook::Error::NetworkResponseEmpty);
|
return Err(git::forge::webhook::Error::NetworkResponseEmpty);
|
||||||
|
};
|
||||||
|
if list.is_empty() {
|
||||||
|
return Ok(ids);
|
||||||
}
|
}
|
||||||
Ok(list) => {
|
for hook in list {
|
||||||
if list.is_empty() {
|
if hook
|
||||||
return Ok(ids);
|
.url()
|
||||||
|
.as_ref()
|
||||||
|
.starts_with(&repo_listen_url.to_string())
|
||||||
|
{
|
||||||
|
ids.push(hook.id());
|
||||||
}
|
}
|
||||||
for hook in list {
|
|
||||||
if hook
|
|
||||||
.url()
|
|
||||||
.as_ref()
|
|
||||||
.starts_with(&repo_listen_url.to_string())
|
|
||||||
{
|
|
||||||
ids.push(hook.id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
page += 1;
|
|
||||||
}
|
}
|
||||||
},
|
page += 1;
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(git::forge::webhook::Error::Network(e));
|
return Err(git::forge::webhook::Error::Network(e));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
use git_next_core::{git, webhook, ApiToken, BranchName};
|
use git_next_core::{git, webhook, ApiToken, BranchName};
|
||||||
|
|
||||||
|
@ -18,24 +16,19 @@ pub use unregister::unregister;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use authorisation::sign_body;
|
pub use authorisation::sign_body;
|
||||||
|
|
||||||
pub fn headers(token: &ApiToken) -> HashMap<String, String> {
|
pub fn headers(token: &ApiToken) -> kxio::network::NetRequestHeaders {
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
|
kxio::network::NetRequestHeaders::default()
|
||||||
HashMap::from([
|
.with("Accept", "application/vnd.github+json")
|
||||||
(
|
.with(
|
||||||
"Accept".to_string(),
|
"User-Agent",
|
||||||
"application/vnd.github+json".to_string(),
|
format!("git-next/server/{}", clap::crate_version!()).as_str(),
|
||||||
),
|
)
|
||||||
(
|
.with(
|
||||||
"User-Agent".to_string(),
|
"Authorization",
|
||||||
format!("git-next/server/{}", clap::crate_version!()),
|
format!("Bearer {}", token.expose_secret()).as_str(),
|
||||||
),
|
)
|
||||||
(
|
.with("X-GitHub-Api-Version", "2022-11-28")
|
||||||
"Authorization".to_string(),
|
|
||||||
format!("Bearer {}", token.expose_secret()),
|
|
||||||
),
|
|
||||||
("X-GitHub-Api-Version".to_string(), "2022-11-28".to_string()),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
//
|
//
|
||||||
use crate::{self as github, webhook};
|
use crate::{self as github, webhook};
|
||||||
use git_next_core::{git, server::RepoListenUrl, RegisteredWebhook, WebhookAuth, WebhookId};
|
use git_next_core::{git, server::RepoListenUrl, RegisteredWebhook, WebhookAuth, WebhookId};
|
||||||
use serde_json::json;
|
|
||||||
|
use kxio::network;
|
||||||
|
|
||||||
// https://docs.github.com/en/rest/repos/webhooks?apiVersion=2022-11-28#create-a-repository-webhook
|
// https://docs.github.com/en/rest/repos/webhooks?apiVersion=2022-11-28#create-a-repository-webhook
|
||||||
pub async fn register(
|
pub async fn register(
|
||||||
|
@ -22,48 +23,45 @@ pub async fn register(
|
||||||
let net = &github.net;
|
let net = &github.net;
|
||||||
let hostname = repo_details.forge.hostname();
|
let hostname = repo_details.forge.hostname();
|
||||||
let authorisation = WebhookAuth::generate();
|
let authorisation = WebhookAuth::generate();
|
||||||
match net
|
let request = network::NetRequest::new(
|
||||||
.post(format!(
|
network::RequestMethod::Post,
|
||||||
|
network::NetUrl::new(format!(
|
||||||
"https://api.{hostname}/repos/{}/hooks",
|
"https://api.{hostname}/repos/{}/hooks",
|
||||||
repo_details.repo_path
|
repo_details.repo_path
|
||||||
))
|
)),
|
||||||
.headers(github::webhook::headers(repo_details.forge.token()))
|
github::webhook::headers(repo_details.forge.token()),
|
||||||
.body(
|
network::RequestBody::Json(network::json!({
|
||||||
json!({
|
"name": "web",
|
||||||
"name": "web",
|
"active": true,
|
||||||
"active": true,
|
"events": ["push"],
|
||||||
"events": ["push"],
|
"config": {
|
||||||
"config": {
|
"url": repo_listen_url.to_string(),
|
||||||
"url": repo_listen_url.to_string(),
|
"content_type": "json",
|
||||||
"content_type": "json",
|
"secret": authorisation.to_string(),
|
||||||
"secret": authorisation.to_string(),
|
"insecure_ssl": "0",
|
||||||
"insecure_ssl": "0",
|
}
|
||||||
}
|
})),
|
||||||
})
|
network::ResponseType::Json,
|
||||||
.to_string(),
|
None,
|
||||||
)
|
network::NetRequestLogging::None,
|
||||||
.send()
|
);
|
||||||
.await
|
let result = net.post_json::<github::GithubHook>(request).await;
|
||||||
{
|
match result {
|
||||||
|
Ok(response) => {
|
||||||
|
let Some(hook) = response.response_body() else {
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
// request response is Json so response_body never returns None
|
||||||
|
return Err(git::forge::webhook::Error::NetworkResponseEmpty);
|
||||||
|
};
|
||||||
|
tracing::info!(webhook_id = %hook.id, "Webhook registered");
|
||||||
|
Ok(RegisteredWebhook::new(
|
||||||
|
WebhookId::new(format!("{}", hook.id)),
|
||||||
|
authorisation,
|
||||||
|
))
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!("Failed to register webhook");
|
tracing::warn!("Failed to register webhook");
|
||||||
Err(git::forge::webhook::Error::FailedToRegister(e.to_string()))
|
Err(git::forge::webhook::Error::FailedToRegister(e.to_string()))
|
||||||
}
|
}
|
||||||
Ok(response) => {
|
|
||||||
match response.json::<github::GithubHook>().await {
|
|
||||||
Err(_) => {
|
|
||||||
#[cfg(not(tarpaulin_include))]
|
|
||||||
// request response is Json so response_body never returns None
|
|
||||||
return Err(git::forge::webhook::Error::NetworkResponseEmpty);
|
|
||||||
}
|
|
||||||
Ok(hook) => {
|
|
||||||
tracing::info!(webhook_id = %hook.id, "Webhook registered");
|
|
||||||
Ok(RegisteredWebhook::new(
|
|
||||||
WebhookId::new(format!("{}", hook.id)),
|
|
||||||
authorisation,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
use crate as github;
|
use crate as github;
|
||||||
use git_next_core::{git, WebhookId};
|
use git_next_core::{git, WebhookId};
|
||||||
|
|
||||||
|
use kxio::network;
|
||||||
|
|
||||||
// https://docs.github.com/en/rest/repos/webhooks?apiVersion=2022-11-28#delete-a-repository-webhook
|
// https://docs.github.com/en/rest/repos/webhooks?apiVersion=2022-11-28#delete-a-repository-webhook
|
||||||
pub async fn unregister(
|
pub async fn unregister(
|
||||||
github: &github::Github,
|
github: &github::Github,
|
||||||
|
@ -10,13 +12,20 @@ pub async fn unregister(
|
||||||
let net = &github.net;
|
let net = &github.net;
|
||||||
let repo_details = &github.repo_details;
|
let repo_details = &github.repo_details;
|
||||||
let hostname = repo_details.forge.hostname();
|
let hostname = repo_details.forge.hostname();
|
||||||
net.delete(format!(
|
let request = network::NetRequest::new(
|
||||||
"https://api.{hostname}/repos/{}/hooks/{}",
|
network::RequestMethod::Delete,
|
||||||
repo_details.repo_path, webhook_id
|
network::NetUrl::new(format!(
|
||||||
))
|
"https://api.{hostname}/repos/{}/hooks/{}",
|
||||||
.headers(github::webhook::headers(repo_details.forge.token()))
|
repo_details.repo_path, webhook_id
|
||||||
.send()
|
)),
|
||||||
.await
|
github::webhook::headers(repo_details.forge.token()),
|
||||||
.map_err(|e| git::forge::webhook::Error::FailedToRegister(e.to_string()))
|
network::RequestBody::None,
|
||||||
.map(|_| ())
|
network::ResponseType::None,
|
||||||
|
None,
|
||||||
|
network::NetRequestLogging::None,
|
||||||
|
);
|
||||||
|
net.delete(request)
|
||||||
|
.await
|
||||||
|
.map_err(|e| git::forge::webhook::Error::FailedToRegister(e.to_string()))
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue