Compare commits
2 commits
5ffe37707a
...
c11a6137d9
Author | SHA1 | Date | |
---|---|---|---|
|
c11a6137d9 | ||
|
d3dfedc95b |
44 changed files with 1278 additions and 1082 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 = { version = "1.2" }
|
kxio = "3.0"
|
||||||
|
|
||||||
# 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::network::Network,
|
net: kxio::net::Net,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for AlertsActor {
|
impl Actor for AlertsActor {
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
//
|
//
|
||||||
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::network::Network,
|
net: &kxio::net::Net,
|
||||||
) {
|
) {
|
||||||
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())
|
||||||
|
@ -22,7 +21,7 @@ async fn do_send_webhook(
|
||||||
user_notification: &UserNotification,
|
user_notification: &UserNotification,
|
||||||
webhook: Webhook,
|
webhook: Webhook,
|
||||||
webhook_config: &OutboundWebhook,
|
webhook_config: &OutboundWebhook,
|
||||||
net: &kxio::network::Network,
|
net: &kxio::net::Net,
|
||||||
) {
|
) {
|
||||||
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();
|
||||||
|
@ -35,20 +34,17 @@ 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, "");
|
||||||
let url = webhook_config.url();
|
net.post(webhook_config.url())
|
||||||
|
.body(payload.to_string())
|
||||||
let net_url = NetUrl::new(url.to_string());
|
.header("webhook-id", message_id)
|
||||||
let request = NetRequest::post(net_url)
|
.header("webhook-timestamp", timestamp.to_string())
|
||||||
.body(RequestBody::Json(payload))
|
.header("webhook-signature", signature)
|
||||||
.header("webhook-id", &message_id)
|
.send()
|
||||||
.header("webhook-timestamp", ×tamp.to_string())
|
.await
|
||||||
.header("webhook-signature", &signature)
|
.map_or_else(
|
||||||
.response_type(ResponseType::None)
|
|err| {
|
||||||
.build();
|
tracing::warn!(?err, "sending webhook");
|
||||||
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::network::Network;
|
use kxio::net::Net;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Forge;
|
pub struct Forge;
|
||||||
|
|
||||||
impl Forge {
|
impl Forge {
|
||||||
pub fn create(repo_details: RepoDetails, net: Network) -> Box<dyn ForgeLike> {
|
pub fn create(repo_details: RepoDetails, net: Net) -> 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 = Network::new_mock();
|
let net = kxio::net::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);
|
let forge = Forge::create(repo_details, net.into());
|
||||||
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 = Network::new_mock();
|
let net = kxio::net::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);
|
let forge = Forge::create(repo_details, net.into());
|
||||||
assert_eq!(forge.name(), "github");
|
assert_eq!(forge.name(), "github");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,14 @@ 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_exists(&pathbuf)
|
.path(&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_write(&pathbuf, include_str!("../default.toml"))
|
fs.file(&pathbuf)
|
||||||
|
.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, network::Network};
|
use kxio::{fs, net};
|
||||||
|
|
||||||
#[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 = Network::new_real();
|
let net = net::new();
|
||||||
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, Instrument as _};
|
use tracing::{debug, warn, Instrument as _};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
repo::{
|
repo::{
|
||||||
|
@ -26,9 +26,13 @@ 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 {
|
||||||
let status = forge.commit_status(&next).await;
|
match forge.commit_status(&next).await {
|
||||||
debug!("got status: {status:?}");
|
Ok(status) => {
|
||||||
do_send(&addr, ReceiveCIStatus::new((next, status)), log.as_ref());
|
debug!("got status: {status:?}");
|
||||||
|
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,6 +70,7 @@ 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::network::Network;
|
use kxio::net::Net;
|
||||||
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: Network,
|
net: Net,
|
||||||
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: Network,
|
net: Net,
|
||||||
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::network::MockNetwork {
|
pub fn a_network() -> kxio::net::MockNet {
|
||||||
kxio::network::MockNetwork::new()
|
kxio::net::mock()
|
||||||
}
|
}
|
||||||
|
|
||||||
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::FileSystem {
|
pub fn a_filesystem() -> kxio::fs::TempFileSystem {
|
||||||
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
|
kxio::fs::temp().expect("temp fs")
|
||||||
}
|
}
|
||||||
|
|
||||||
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::network::Network,
|
net: kxio::net::Net,
|
||||||
) -> (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_create(&repo_details.gitdir)?;
|
fs.dir(&repo_details.gitdir).create()?;
|
||||||
|
|
||||||
//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_create(&repo_details.gitdir)?;
|
fs.dir(&repo_details.gitdir).create()?;
|
||||||
|
|
||||||
//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_create(&repo_details.gitdir)?;
|
fs.dir(&repo_details.gitdir).create()?;
|
||||||
|
|
||||||
//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_create(&repo_details.gitdir)?;
|
fs.dir(&repo_details.gitdir).create()?;
|
||||||
|
|
||||||
//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_create(&repo_details.gitdir)?;
|
fs.dir(&repo_details.gitdir).create()?;
|
||||||
|
|
||||||
//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(|_| status);
|
.return_once(|_| Ok(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, network::Network};
|
use kxio::{fs::FileSystem, net::Net};
|
||||||
|
|
||||||
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: Network,
|
net: Net,
|
||||||
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: Network,
|
net: Net,
|
||||||
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,13 +100,14 @@ 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);
|
||||||
if self.fs.path_exists(&path)? {
|
let path_handle = self.fs.path(&path);
|
||||||
if !self.fs.path_is_dir(&path)? {
|
if path_handle.exists()? {
|
||||||
|
if !path_handle.is_dir()? {
|
||||||
return Err(Error::ForgeDirIsNotDirectory { path });
|
return Err(Error::ForgeDirIsNotDirectory { path });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tracing::info!(%forge_name, ?path, "creating storage");
|
tracing::info!(%forge_name, ?path_handle, "creating storage");
|
||||||
self.fs.dir_create_all(&path)?;
|
self.fs.dir(&path).create_all()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +214,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_create(dir) {
|
if let Err(err) = self.fs.dir(dir).create() {
|
||||||
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::FileSystem {
|
pub fn a_filesystem() -> kxio::fs::TempFileSystem {
|
||||||
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
|
kxio::fs::temp().expect("temp fs")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_network() -> kxio::network::MockNetwork {
|
pub fn a_network() -> kxio::net::MockNet {
|
||||||
kxio::network::MockNetwork::new()
|
kxio::net::mock()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn an_alerts_actor(net: kxio::network::Network) -> Addr<AlertsActor> {
|
pub fn an_alerts_actor(net: kxio::net::Net) -> 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.clone(), net.into(), alerts, repo, duration);
|
let server = ServerActor::new(fs.as_real(), 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, network::Network};
|
use kxio::{fs::FileSystem, net::Net};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -34,12 +34,14 @@ 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_exists(&pathbuf)
|
.path(&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_write(&pathbuf, include_str!("server-default.toml"))
|
fs.file(&pathbuf)
|
||||||
|
.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:?}",);
|
||||||
}
|
}
|
||||||
|
@ -50,7 +52,7 @@ pub fn init(fs: &FileSystem) -> Result<()> {
|
||||||
pub fn start(
|
pub fn start(
|
||||||
ui: bool,
|
ui: bool,
|
||||||
fs: FileSystem,
|
fs: FileSystem,
|
||||||
net: Network,
|
net: Net,
|
||||||
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_write(&file, "contents")?;
|
fs.file(&file).write("contents")?;
|
||||||
|
|
||||||
crate::init::run(&fs)?;
|
crate::init::run(&fs)?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fs.file_read_to_string(&file)?,
|
fs.file(&file).reader()?.to_string(),
|
||||||
"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_exists(&file)?, "The file has not been created");
|
assert!(fs.path(&file).exists()?, "The file has not been created");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fs.file_read_to_string(&file)?,
|
fs.file(&file).reader()?.to_string(),
|
||||||
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_write(&path, "foo")?;
|
fs.file(&path).write("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_read_to_string(&file)?;
|
let str = fs.file(&file).reader()?.to_string();
|
||||||
Ok(toml::from_str(&str)?)
|
Ok(toml::from_str(&str)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -624,10 +624,8 @@ token = "{forge_token}"
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
println!("{file_contents}");
|
println!("{file_contents}");
|
||||||
fs.file_write(
|
fs.file(&fs.base().join("git-next-server.toml"))
|
||||||
&fs.base().join("git-next-server.toml"),
|
.write(file_contents.as_str())?;
|
||||||
file_contents.as_str(),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,5 @@ pub enum Status {
|
||||||
Pass,
|
Pass,
|
||||||
Fail,
|
Fail,
|
||||||
Pending,
|
Pending,
|
||||||
|
Error(String),
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,10 @@ 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(&self, commit: &git::Commit) -> git::forge::commit::Status;
|
async fn 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,8 +4,16 @@ 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::network::NetworkError),
|
Network(#[from] kxio::net::Error),
|
||||||
|
|
||||||
|
#[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::network::NetworkError),
|
Network(#[from] kxio::net::Error),
|
||||||
|
|
||||||
#[error("fetch: {0}")]
|
#[error("fetch: {0}")]
|
||||||
Fetch(#[from] git::fetch::Error),
|
Fetch(#[from] git::fetch::Error),
|
||||||
|
|
|
@ -142,7 +142,8 @@ 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 config_file = fs.file_read_to_string(config_filename)?;
|
let file = fs.file(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)
|
||||||
|
@ -165,7 +166,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
|
||||||
fs.file_write(config_filename, config_lines.join("\n").as_str())?;
|
file.write(config_lines.join("\n").as_str())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,10 +172,12 @@ 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 = fs
|
let contents = file
|
||||||
.file_read_to_string(&config_file)
|
.reader()
|
||||||
.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"]
|
||||||
|
@ -184,7 +186,7 @@ impl TestOpenRepository {
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
#[allow(clippy::expect_used)]
|
#[allow(clippy::expect_used)]
|
||||||
fs.file_write(&config_file, &updated_contents)
|
file.write(&updated_contents)
|
||||||
.expect("write updated .git/config");
|
.expect("write updated .git/config");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::ops::Deref as _;
|
||||||
|
|
||||||
use crate::CommitCount;
|
use crate::CommitCount;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -9,7 +11,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.clone(), forge_details);
|
let test_repository = git::repository::test(fs.deref().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();
|
||||||
|
@ -29,7 +31,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.clone(), forge_details);
|
let test_repository = git::repository::test(fs.deref().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)?;
|
||||||
|
@ -48,7 +50,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.clone(), forge_details);
|
let test_repository = git::repository::test(fs.deref().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,3 +1,5 @@
|
||||||
|
use std::ops::Deref as _;
|
||||||
|
|
||||||
//
|
//
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -11,7 +13,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.clone(), forge_details);
|
let test_repository = git::repository::test(fs.deref().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,
|
||||||
|
@ -35,7 +37,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.clone(), forge_details);
|
let test_repository = git::repository::test(fs.deref().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_read_to_string(&fs.base().join("config"))?;
|
let config = fs.file(&fs.base().join("config")).reader()?.to_string();
|
||||||
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_read_to_string(&fs.base().join("config"))?;
|
let config = fs.file(&fs.base().join("config")).reader()?.to_string();
|
||||||
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_read_to_string(&fs.base().join("config"))?;
|
let config = fs.file(&fs.base().join("config")).reader()?.to_string();
|
||||||
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_read_to_string(&fs.base().join("config"))?;
|
let config = fs.file(&fs.base().join("config")).reader()?.to_string();
|
||||||
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::FileSystem {
|
pub fn a_filesystem() -> kxio::fs::TempFileSystem {
|
||||||
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
|
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +337,8 @@ 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 config_file = fs.file_read_to_string(&path.join("config")).unwrap();
|
let file = fs.file(&path.join("config"));
|
||||||
|
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"]"#);
|
||||||
|
@ -345,8 +346,7 @@ 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
|
||||||
fs.file_write(&path.join("config"), config_lines.join("\n").as_str())
|
file.write(config_lines.join("\n").as_str()).unwrap();
|
||||||
.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_write(&file, contents)?;
|
fs.file(&file).write(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_write(&file, &word)?;
|
fs.file(&file).write(&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_read_to_string(&local_branch)?;
|
let contents = fs.file(&local_branch).reader()?.to_string();
|
||||||
fs.dir_create_all(&origin_heads)?;
|
fs.dir(&origin_heads).create_all()?;
|
||||||
fs.file_write(&remote_branch, &contents)?;
|
fs.file(&remote_branch).write(&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_read_to_string(&main_ref)?;
|
let sha = fs.file(&main_ref).reader()?.to_string();
|
||||||
Ok(git::commit::Sha::new(sha.trim().to_string()))
|
Ok(git::commit::Sha::new(sha.trim().to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ use crate::{
|
||||||
|
|
||||||
use assert2::let_assert;
|
use assert2::let_assert;
|
||||||
|
|
||||||
|
use std::ops::Deref as _;
|
||||||
|
|
||||||
mod repos {
|
mod repos {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -208,12 +210,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.clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.deref().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.clone(),
|
fs.deref().clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// /--- 4 next
|
// /--- 4 next
|
||||||
// 0 --- 1 --- 3 main
|
// 0 --- 1 --- 3 main
|
||||||
|
@ -259,12 +261,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.clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.deref().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.clone(),
|
fs.deref().clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// /--- 4 dev
|
// /--- 4 dev
|
||||||
// 0 --- 1 --- 3 main
|
// 0 --- 1 --- 3 main
|
||||||
|
@ -288,7 +290,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.clone(),
|
fs.deref().clone(),
|
||||||
|_branches, _gitdir, _fs| {
|
|_branches, _gitdir, _fs| {
|
||||||
// don't change anything
|
// don't change anything
|
||||||
git::fetch::Result::Ok(())
|
git::fetch::Result::Ok(())
|
||||||
|
@ -297,7 +299,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.clone(),
|
fs.deref().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,
|
||||||
|
@ -346,12 +348,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.clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.deref().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.clone(),
|
fs.deref().clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// /--- 4 dev
|
// /--- 4 dev
|
||||||
// 0 --- 1 --- 3 main
|
// 0 --- 1 --- 3 main
|
||||||
|
@ -375,7 +377,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.clone(),
|
fs.deref().clone(),
|
||||||
|_branches, _gitdir, _fs| {
|
|_branches, _gitdir, _fs| {
|
||||||
// don't change anything
|
// don't change anything
|
||||||
git::fetch::Result::Ok(())
|
git::fetch::Result::Ok(())
|
||||||
|
@ -384,7 +386,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.clone(),
|
fs.deref().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)
|
||||||
},
|
},
|
||||||
|
@ -420,12 +422,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.clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.deref().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.clone(),
|
fs.deref().clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// /--- 3 next
|
// /--- 3 next
|
||||||
// 0 --- 1 main & dev
|
// 0 --- 1 main & dev
|
||||||
|
@ -446,7 +448,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.clone(),
|
fs.deref().clone(),
|
||||||
|_branches, _gitdir, _fs| {
|
|_branches, _gitdir, _fs| {
|
||||||
// don't change anything
|
// don't change anything
|
||||||
git::fetch::Result::Ok(())
|
git::fetch::Result::Ok(())
|
||||||
|
@ -455,7 +457,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.clone(),
|
fs.deref().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,
|
||||||
|
@ -506,12 +508,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.clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.deref().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.clone(),
|
fs.deref().clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// /--- 3 next
|
// /--- 3 next
|
||||||
// 0 --- 1 main
|
// 0 --- 1 main
|
||||||
|
@ -533,7 +535,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.clone(),
|
fs.deref().clone(),
|
||||||
|_branches, _gitdir, _fs| {
|
|_branches, _gitdir, _fs| {
|
||||||
// don't change anything
|
// don't change anything
|
||||||
git::fetch::Result::Ok(())
|
git::fetch::Result::Ok(())
|
||||||
|
@ -542,7 +544,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.clone(),
|
fs.deref().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,
|
||||||
|
@ -604,12 +606,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.clone(), forge_details);
|
let mut test_repository = git::repository::test(fs.deref().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.clone(),
|
fs.deref().clone(),
|
||||||
|branches, gitdir, fs| {
|
|branches, gitdir, fs| {
|
||||||
// 0 --- 1 main
|
// 0 --- 1 main
|
||||||
// \--- 2 next
|
// \--- 2 next
|
||||||
|
|
|
@ -13,17 +13,16 @@ use git_next_core::{
|
||||||
ForgeNotification, RegisteredWebhook, WebhookAuth, WebhookId,
|
ForgeNotification, RegisteredWebhook, WebhookAuth, WebhookId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use kxio::network::{self, Network};
|
use kxio::net::Net;
|
||||||
use tracing::warn;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ForgeJo {
|
pub struct ForgeJo {
|
||||||
repo_details: git::RepoDetails,
|
repo_details: git::RepoDetails,
|
||||||
net: Network,
|
net: Net,
|
||||||
}
|
}
|
||||||
impl ForgeJo {
|
impl ForgeJo {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new(repo_details: git::RepoDetails, net: Network) -> Self {
|
pub const fn new(repo_details: git::RepoDetails, net: Net) -> Self {
|
||||||
Self { repo_details, net }
|
Self { repo_details, net }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,45 +51,28 @@ impl git::ForgeLike for ForgeJo {
|
||||||
webhook::parse_body(body)
|
webhook::parse_body(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn commit_status(&self, commit: &git::Commit) -> Status {
|
async fn commit_status(&self, commit: &git::Commit) -> git::forge::webhook::Result<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 = network::NetUrl::new(format!(
|
let url = 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 request = network::NetRequest::new(
|
|
||||||
network::RequestMethod::Get,
|
|
||||||
url,
|
|
||||||
network::NetRequestHeaders::new(),
|
|
||||||
network::RequestBody::None,
|
|
||||||
network::ResponseType::Json,
|
|
||||||
None,
|
|
||||||
network::NetRequestLogging::None,
|
|
||||||
);
|
);
|
||||||
let result = self.net.get::<CombinedStatus>(request).await;
|
|
||||||
match result {
|
let Ok(response) = self.net.get(url).send().await else {
|
||||||
Ok(response) => match response.response_body() {
|
return Ok(Status::Pending);
|
||||||
Some(status) => match status.state {
|
};
|
||||||
ForgejoState::Success => Status::Pass,
|
let combined_status = response.json::<CombinedStatus>().await.unwrap_or_default();
|
||||||
ForgejoState::Pending | ForgejoState::Blank => Status::Pending,
|
eprintln!("combined_status: {:?}", combined_status);
|
||||||
ForgejoState::Failure | ForgejoState::Error => Status::Fail,
|
let status = match combined_status.state {
|
||||||
},
|
ForgejoState::Success => Status::Pass,
|
||||||
None => {
|
ForgejoState::Pending | ForgejoState::Blank => Status::Pending,
|
||||||
#[cfg(not(tarpaulin_include))]
|
ForgejoState::Failure | ForgejoState::Error => Status::Fail,
|
||||||
unreachable!(); // response.response_body() is always Some when
|
};
|
||||||
// request responseType::Json
|
Ok(status)
|
||||||
}
|
|
||||||
},
|
|
||||||
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(
|
||||||
|
@ -112,16 +94,17 @@ impl git::ForgeLike for ForgeJo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, Default, serde::Deserialize)]
|
||||||
struct CombinedStatus {
|
struct CombinedStatus {
|
||||||
pub state: ForgejoState,
|
pub state: ForgejoState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, Default, 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,14 +1,16 @@
|
||||||
//
|
//
|
||||||
|
#![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,
|
server::{ListenUrl, RepoListenUrl},
|
||||||
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::network::{self, MockNetwork, StatusCode};
|
use kxio::net::{MockNet, StatusCode};
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -116,77 +118,105 @@ 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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
given::a_commit_state("success", &mut net, &repo_details, &commit);
|
given::a_commit_state("success", &net, &repo_details, &commit);
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
assert_eq!(forge.commit_status(&commit).await, Status::Pass);
|
assert_eq!(
|
||||||
|
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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
given::a_commit_state("pending", &mut net, &repo_details, &commit);
|
given::a_commit_state("pending", &net, &repo_details, &commit);
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
assert_eq!(forge.commit_status(&commit).await, Status::Pending);
|
assert_eq!(
|
||||||
|
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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
given::a_commit_state("failure", &mut net, &repo_details, &commit);
|
given::a_commit_state("failure", &net, &repo_details, &commit);
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
assert_eq!(forge.commit_status(&commit).await, Status::Fail);
|
assert_eq!(
|
||||||
|
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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
given::a_commit_state("error", &mut net, &repo_details, &commit);
|
given::a_commit_state("error", &net, &repo_details, &commit);
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
assert_eq!(forge.commit_status(&commit).await, Status::Fail);
|
assert_eq!(
|
||||||
|
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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
given::a_commit_state("", &mut net, &repo_details, &commit);
|
given::a_commit_state("", &net, &repo_details, &commit);
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
assert_eq!(forge.commit_status(&commit).await, Status::Pending);
|
assert_eq!(
|
||||||
|
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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
net.add_get_response(
|
net.on()
|
||||||
&given::a_commit_status_url(&repo_details, &commit),
|
.get(given::a_commit_status_url(&repo_details, &commit))
|
||||||
StatusCode::OK,
|
.respond(StatusCode::OK)
|
||||||
"",
|
.body(
|
||||||
);
|
json!({
|
||||||
|
"state": "" // blank => Pending
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.expect("mock");
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
assert_eq!(forge.commit_status(&commit).await, Status::Pending);
|
assert_eq!(
|
||||||
|
forge.commit_status(&commit).await.expect("status"),
|
||||||
|
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 mut net = given::a_network();
|
let mock_net = given::a_network();
|
||||||
net.add_get_error(
|
mock_net
|
||||||
&given::a_commit_status_url(&repo_details, &commit),
|
.on()
|
||||||
"boom today",
|
.get(given::a_commit_status_url(&repo_details, &commit))
|
||||||
|
.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,13 +276,15 @@ 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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
|
|
||||||
net.add_get_error(
|
net.on()
|
||||||
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?page=1&token={token}")
|
.get(format!(
|
||||||
.as_str(),
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?page=1&token={token}"
|
||||||
"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);
|
||||||
|
|
||||||
|
@ -271,16 +303,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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
|
|
||||||
net.add_delete_response(
|
net.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}"
|
||||||
)
|
))
|
||||||
.as_str(),
|
.respond(StatusCode::OK)
|
||||||
StatusCode::OK,
|
.body("")
|
||||||
"",
|
.expect("mock");
|
||||||
);
|
|
||||||
|
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -295,15 +326,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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
|
|
||||||
net.add_delete_error(
|
net.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}"
|
||||||
)
|
))
|
||||||
.as_str(),
|
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
"error",
|
.body("error")
|
||||||
);
|
.expect("mock");
|
||||||
|
|
||||||
let forge = given::a_forgejo_forge(&repo_details, net);
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -343,11 +374,13 @@ 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.add_post_response(
|
net.on()
|
||||||
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}").as_str(),
|
.post(format!(
|
||||||
StatusCode::OK,
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
||||||
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);
|
||||||
|
|
||||||
|
@ -415,11 +448,13 @@ 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.add_post_response(
|
net.on()
|
||||||
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}").as_str(),
|
.post(format!(
|
||||||
StatusCode::OK,
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
||||||
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);
|
||||||
|
|
||||||
|
@ -454,17 +489,19 @@ 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.add_post_response(
|
net.on()
|
||||||
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}").as_str(),
|
.post(format!(
|
||||||
StatusCode::OK,
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
||||||
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::FailedToRegister(_)),
|
matches!(err, git::forge::webhook::Error::NetworkResponseEmpty),
|
||||||
"{err:?}"
|
"{err:?}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -493,10 +530,13 @@ 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.add_post_error(
|
net.on()
|
||||||
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}").as_str(),
|
.post(format!(
|
||||||
"error",
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
||||||
);
|
))
|
||||||
|
.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);
|
||||||
|
|
||||||
|
@ -508,7 +548,6 @@ mod forgejo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod with {
|
mod with {
|
||||||
use git_next_core::server::RepoListenUrl;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -520,31 +559,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.add_get_response(
|
args.net
|
||||||
format!(
|
.on()
|
||||||
|
.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}"
|
||||||
)
|
))
|
||||||
.as_str(),
|
.respond(StatusCode::OK)
|
||||||
StatusCode::OK,
|
.body(json!(response).to_string())
|
||||||
json!(response).to_string().as_str(),
|
.expect("mock");
|
||||||
);
|
|
||||||
}
|
}
|
||||||
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.add_delete_response(
|
args.net
|
||||||
format!(
|
.on()
|
||||||
|
.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}"
|
||||||
)
|
))
|
||||||
.as_str(),
|
.respond(StatusCode::OK)
|
||||||
StatusCode::OK,
|
.body("")
|
||||||
"",
|
.expect("mock");
|
||||||
);
|
|
||||||
}
|
}
|
||||||
pub struct WebhookArgs<'a> {
|
pub struct WebhookArgs<'a> {
|
||||||
pub net: &'a mut network::MockNetwork,
|
pub net: &'a MockNet,
|
||||||
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,
|
||||||
|
@ -573,21 +612,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: &mut MockNetwork,
|
net: &MockNet,
|
||||||
repo_details: &git::RepoDetails,
|
repo_details: &git::RepoDetails,
|
||||||
commit: &git::Commit,
|
commit: &git::Commit,
|
||||||
) {
|
) {
|
||||||
let response = json!({"state":state.as_ref()});
|
net.on()
|
||||||
net.add_get_response(
|
.get(a_commit_status_url(repo_details, commit))
|
||||||
a_commit_status_url(repo_details, commit).as_str(),
|
.respond(StatusCode::OK)
|
||||||
StatusCode::OK,
|
.body((json!({"state":state.as_ref()})).to_string())
|
||||||
response.to_string().as_str(),
|
.expect("mock");
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_commit_status_url(
|
pub fn a_commit_status_url(
|
||||||
|
@ -644,9 +683,10 @@ mod forgejo {
|
||||||
|
|
||||||
pub fn a_forgejo_forge(
|
pub fn a_forgejo_forge(
|
||||||
repo_details: &git::RepoDetails,
|
repo_details: &git::RepoDetails,
|
||||||
net: impl Into<kxio::network::Network>,
|
net: impl Into<kxio::net::Net>,
|
||||||
) -> ForgeJo {
|
) -> ForgeJo {
|
||||||
ForgeJo::new(repo_details.clone(), net.into())
|
let net: kxio::net::Net = net.into();
|
||||||
|
ForgeJo::new(repo_details.clone(), net)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_forgejo_webhook_id() -> i64 {
|
pub fn a_forgejo_webhook_id() -> i64 {
|
||||||
|
@ -674,8 +714,8 @@ mod forgejo {
|
||||||
RepoAlias::new(a_name())
|
RepoAlias::new(a_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_network() -> kxio::network::MockNetwork {
|
pub fn a_network() -> kxio::net::MockNet {
|
||||||
kxio::network::MockNetwork::new()
|
kxio::net::mock()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_name() -> String {
|
pub fn a_name() -> String {
|
||||||
|
@ -758,8 +798,8 @@ mod forgejo {
|
||||||
git::commit::Sha::new(a_name())
|
git::commit::Sha::new(a_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_filesystem() -> kxio::fs::FileSystem {
|
pub fn a_filesystem() -> kxio::fs::TempFileSystem {
|
||||||
kxio::fs::temp().unwrap_or_else(|e| panic!("{}", e))
|
kxio::fs::temp().expect("temp fs")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repo_details(fs: &kxio::fs::FileSystem) -> git::RepoDetails {
|
pub fn repo_details(fs: &kxio::fs::FileSystem) -> git::RepoDetails {
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
//
|
//
|
||||||
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: &network::Network,
|
net: &Net,
|
||||||
) -> 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();
|
||||||
|
@ -17,22 +16,15 @@ 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();
|
||||||
let url =
|
match net
|
||||||
format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?page={page}&token={token}");
|
.get(format!(
|
||||||
let net_url = network::NetUrl::new(url);
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?page={page}&token={token}"
|
||||||
let request = network::NetRequest::new(
|
))
|
||||||
network::RequestMethod::Get,
|
.send()
|
||||||
net_url,
|
.await
|
||||||
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 Some(list) = response.response_body() {
|
if let Ok(list) = response.json::<Vec<Hook>>().await {
|
||||||
if list.is_empty() {
|
if list.is_empty() {
|
||||||
return Ok(ids);
|
return Ok(ids);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
//
|
//
|
||||||
use git_next_core::{git, server::RepoListenUrl, RegisteredWebhook, WebhookAuth, WebhookId};
|
use git_next_core::{git, server::RepoListenUrl, RegisteredWebhook, WebhookAuth, WebhookId};
|
||||||
|
|
||||||
use kxio::network;
|
use kxio::net::Net;
|
||||||
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;
|
||||||
|
@ -12,7 +13,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: &network::Network,
|
net: &Net,
|
||||||
) -> 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);
|
||||||
|
@ -27,35 +28,27 @@ 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 = network::NetUrl::new(format!(
|
let url = format!("https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}");
|
||||||
"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();
|
||||||
let body = network::json!({
|
match net
|
||||||
"active": true,
|
.post(url)
|
||||||
"authorization_header": authorisation.header_value(),
|
.header("Content-Type", "application/json")
|
||||||
"branch_filter": format!("{{{},{},{}}}", repo_config.branches().main(), repo_config.branches().next(), repo_config.branches().dev()),
|
.body(json!({
|
||||||
"config": {
|
"active": true,
|
||||||
"content_type": "json",
|
"authorization_header": authorisation.header_value(),
|
||||||
"url": repo_listen_url.to_string(),
|
"branch_filter": format!("{{{},{},{}}}", repo_config.branches().main(), repo_config.branches().next(), repo_config.branches().dev()),
|
||||||
},
|
"config": {
|
||||||
"events": [ "push" ],
|
"content_type": "json",
|
||||||
"type": "forgejo"
|
"url": repo_listen_url.to_string(),
|
||||||
});
|
},
|
||||||
let request = network::NetRequest::new(
|
"events": [ "push" ],
|
||||||
network::RequestMethod::Post,
|
"type": "forgejo"
|
||||||
url,
|
}).to_string())
|
||||||
headers,
|
.send()
|
||||||
network::RequestBody::Json(body),
|
.await
|
||||||
network::ResponseType::Json,
|
{
|
||||||
None,
|
|
||||||
network::NetRequestLogging::None,
|
|
||||||
);
|
|
||||||
let result = net.post_json::<Hook>(request).await;
|
|
||||||
match result {
|
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
let Some(hook) = response.response_body() else {
|
let Ok(hook) = response.json::<Hook>().await 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);
|
||||||
|
@ -71,4 +64,5 @@ pub async fn register(
|
||||||
Err(git::forge::webhook::Error::FailedToRegister(e.to_string()))
|
Err(git::forge::webhook::Error::FailedToRegister(e.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,24 @@
|
||||||
//
|
//
|
||||||
use git_next_core::{git, WebhookId};
|
use git_next_core::{git, WebhookId};
|
||||||
|
|
||||||
use kxio::network;
|
use kxio::net::Net;
|
||||||
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: &network::Network,
|
net: &Net,
|
||||||
) -> 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();
|
||||||
let url = network::NetUrl::new(format!(
|
match net
|
||||||
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
.delete(format!(
|
||||||
));
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
||||||
let request = network::NetRequest::new(
|
))
|
||||||
network::RequestMethod::Delete,
|
.send()
|
||||||
url,
|
.await
|
||||||
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,53 +2,46 @@
|
||||||
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(github: &github::Github, commit: &git::Commit) -> git::forge::commit::Status {
|
pub async fn 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 = network::NetUrl::new(format!(
|
let url = format!("https://api.{hostname}/repos/{repo_path}/commits/{commit}/statuses");
|
||||||
"https://api.{hostname}/repos/{repo_path}/commits/{commit}/statuses"
|
let Ok(response) = github
|
||||||
));
|
.net
|
||||||
let headers = github::webhook::headers(repo_details.forge.token());
|
.get(url)
|
||||||
let request = network::NetRequest::new(
|
.headers(github::webhook::headers(repo_details.forge.token()))
|
||||||
network::RequestMethod::Get,
|
.body("")
|
||||||
url,
|
.send()
|
||||||
headers,
|
.await
|
||||||
network::RequestBody::None,
|
else {
|
||||||
network::ResponseType::Json,
|
return Ok(Status::Pending);
|
||||||
None,
|
};
|
||||||
network::NetRequestLogging::None,
|
let statuses = response.json::<Vec<GithubStatus>>().await?;
|
||||||
);
|
let result = statuses
|
||||||
let result = github.net.get::<Vec<GithubStatus>>(request).await;
|
.into_iter()
|
||||||
match result {
|
.map(|status| match status.state {
|
||||||
Ok(response) => response
|
GithubState::Success => Status::Pass,
|
||||||
.response_body()
|
GithubState::Pending | GithubState::Blank => Status::Pending,
|
||||||
.and_then(|statuses| {
|
GithubState::Failure | GithubState::Error => Status::Fail,
|
||||||
statuses
|
})
|
||||||
.into_iter()
|
.reduce(|l, r| match (l, r) {
|
||||||
.map(|status| match status.state {
|
(Status::Pass, Status::Pass) => Status::Pass,
|
||||||
GithubState::Success => Status::Pass,
|
(_, Status::Fail) | (Status::Fail, _) => Status::Fail,
|
||||||
GithubState::Pending | GithubState::Blank => Status::Pending,
|
(_, Status::Pending) | (Status::Pending, _) => Status::Pending,
|
||||||
GithubState::Failure | GithubState::Error => Status::Fail,
|
(Status::Error(e1), Status::Error(e2)) => Status::Error(format!("{e1} / {e2}")),
|
||||||
})
|
(_, Status::Error(err)) | (Status::Error(err), _) => Status::Error(err),
|
||||||
.reduce(|l, r| match (l, r) {
|
})
|
||||||
(Status::Pass, Status::Pass) => Status::Pass,
|
.unwrap_or_else(|| {
|
||||||
(_, Status::Fail) | (Status::Fail, _) => Status::Fail,
|
tracing::warn!("No status checks configured for 'next' branch",);
|
||||||
(_, Status::Pending) | (Status::Pending, _) => Status::Pending,
|
Status::Pass
|
||||||
})
|
});
|
||||||
})
|
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,8 +7,7 @@ mod webhook;
|
||||||
|
|
||||||
use crate as github;
|
use crate as github;
|
||||||
use git_next_core::{
|
use git_next_core::{
|
||||||
self as core,
|
self as core, git,
|
||||||
git::{self, forge::commit::Status},
|
|
||||||
server::{self, RepoListenUrl},
|
server::{self, RepoListenUrl},
|
||||||
ForgeNotification, RegisteredWebhook, WebhookAuth, WebhookId,
|
ForgeNotification, RegisteredWebhook, WebhookAuth, WebhookId,
|
||||||
};
|
};
|
||||||
|
@ -18,7 +17,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::network::Network,
|
net: kxio::net::Net,
|
||||||
}
|
}
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl git::ForgeLike for Github {
|
impl git::ForgeLike for Github {
|
||||||
|
@ -52,7 +51,10 @@ impl git::ForgeLike for Github {
|
||||||
github::webhook::parse_body(body)
|
github::webhook::parse_body(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn commit_status(&self, commit: &git::Commit) -> Status {
|
async fn 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,4 +1,6 @@
|
||||||
//
|
//
|
||||||
|
#![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},
|
||||||
|
@ -9,7 +11,7 @@ use git_next_core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use assert2::let_assert;
|
use assert2::let_assert;
|
||||||
use kxio::network::{self, MockNetwork, StatusCode};
|
use kxio::net::{MockNet, StatusCode};
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -99,10 +101,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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
given::commit_states(&states, &mut net, &repo_details, &commit);
|
given::commit_states(&states, &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, expected);
|
assert_eq!(forge.commit_status(&commit).await.expect("status"), expected);
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
@ -125,29 +127,37 @@ mod github {
|
||||||
);
|
);
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn should_return_pending_for_no_statuses() {
|
async fn should_return_pass_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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
net.add_get_response(
|
net.on()
|
||||||
&given::a_commit_status_url(&repo_details, &commit),
|
.get(given::a_commit_status_url(&repo_details, &commit))
|
||||||
StatusCode::OK,
|
.respond(StatusCode::OK)
|
||||||
"",
|
.body(json!([]).to_string())
|
||||||
);
|
.expect("mock");
|
||||||
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, Status::Pending);
|
assert_eq!(
|
||||||
|
forge.commit_status(&commit).await.expect("status"),
|
||||||
|
Status::Pass // no CI checks configured
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
net.add_get_error(
|
net.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("boom today")
|
||||||
|
.expect("mock");
|
||||||
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, Status::Pending);
|
assert_eq!(
|
||||||
|
forge.commit_status(&commit).await.expect("status"),
|
||||||
|
Status::Pending
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,9 +174,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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
let mut args = with::WebhookArgs {
|
let args = with::WebhookArgs {
|
||||||
net: &mut net,
|
net: &net,
|
||||||
hostname,
|
hostname,
|
||||||
repo_path,
|
repo_path,
|
||||||
};
|
};
|
||||||
|
@ -178,10 +188,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()),
|
||||||
],
|
],
|
||||||
&mut args,
|
&args,
|
||||||
);
|
);
|
||||||
// page 2 with no items - stops pagination
|
// page 2 with no items - stops pagination
|
||||||
with::get_webhooks_by_page(2, &[], &mut args);
|
with::get_webhooks_by_page(2, &[], &args);
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -201,11 +211,14 @@ 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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
net.add_get_error(
|
net.on()
|
||||||
format!("https://api.{hostname}/repos/{repo_path}/hooks?page=1").as_str(),
|
.get(format!(
|
||||||
"error_message",
|
"https://api.{hostname}/repos/{repo_path}/hooks?page=1"
|
||||||
);
|
))
|
||||||
|
.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);
|
||||||
|
|
||||||
|
@ -222,12 +235,14 @@ 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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
net.add_delete_response(
|
net.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");
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -242,16 +257,19 @@ 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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
// unregister the webhook will return empty response
|
// unregister the webhook will return empty response
|
||||||
net.add_delete_error(
|
let webhook_id = given::a_webhook_id();
|
||||||
format!("https://api.{hostname}/repos/{repo_path}/hooks").as_str(),
|
net.on()
|
||||||
"error",
|
.delete(format!(
|
||||||
);
|
"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(_)),
|
||||||
|
@ -274,23 +292,24 @@ 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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
let mut args = with::WebhookArgs {
|
let args = with::WebhookArgs {
|
||||||
net: &mut net,
|
net: &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, &[], &mut args);
|
with::get_webhooks_by_page(1, &[], &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.add_post_response(
|
net.on()
|
||||||
format!("https://api.{hostname}/repos/{repo_path}/hooks").as_str(),
|
.post(format!("https://api.{hostname}/repos/{repo_path}/hooks"))
|
||||||
StatusCode::OK,
|
.respond(StatusCode::OK)
|
||||||
json!({"id": webhook_id, "config":{"url": repo_listen_url.to_string()}})
|
.body(
|
||||||
.to_string()
|
json!({"id": webhook_id, "config":{"url": repo_listen_url.to_string()}})
|
||||||
.as_str(),
|
.to_string(),
|
||||||
);
|
)
|
||||||
|
.expect("mock");
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -329,9 +348,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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
let mut args = with::WebhookArgs {
|
let args = with::WebhookArgs {
|
||||||
net: &mut net,
|
net: &net,
|
||||||
hostname,
|
hostname,
|
||||||
repo_path,
|
repo_path,
|
||||||
};
|
};
|
||||||
|
@ -341,20 +360,21 @@ 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, &mut args);
|
with::get_webhooks_by_page(1, &hooks, &args);
|
||||||
with::get_webhooks_by_page(2, &[], &mut args);
|
with::get_webhooks_by_page(2, &[], &args);
|
||||||
// should unregister 1 and 2, but not 3
|
// should unregister 1 and 2, but not 3
|
||||||
with::unregister_webhook(&hooks[0], &mut args);
|
with::unregister_webhook(&hooks[0], &args);
|
||||||
with::unregister_webhook(&hooks[1], &mut args);
|
with::unregister_webhook(&hooks[1], &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.add_post_response(
|
net.on()
|
||||||
format!("https://api.{hostname}/repos/{repo_path}/hooks").as_str(),
|
.post(format!("https://api.{hostname}/repos/{repo_path}/hooks"))
|
||||||
StatusCode::OK,
|
.respond(StatusCode::OK)
|
||||||
json!({"id": webhook_id, "config":{"url": repo_listen_url.to_string()}})
|
.body(
|
||||||
.to_string()
|
json!({"id": webhook_id, "config":{"url": repo_listen_url.to_string()}})
|
||||||
.as_str(),
|
.to_string(),
|
||||||
);
|
)
|
||||||
|
.expect("mock");
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -375,26 +395,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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
let mut args = with::WebhookArgs {
|
let args = with::WebhookArgs {
|
||||||
net: &mut net,
|
net: &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, &[], &mut args);
|
with::get_webhooks_by_page(1, &[], &args);
|
||||||
// register the webhook will return empty response
|
// register the webhook will return empty response
|
||||||
net.add_post_response(
|
net.on()
|
||||||
format!("https://api.{hostname}/repos/{repo_path}/hooks").as_str(),
|
.post(format!("https://api.{hostname}/repos/{repo_path}/hooks"))
|
||||||
StatusCode::OK,
|
.respond(StatusCode::OK)
|
||||||
json!({}).to_string().as_str(), // empty response
|
.body(json!({}).to_string())
|
||||||
);
|
.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::FailedToRegister(_)),
|
matches!(err, git::forge::webhook::Error::NetworkResponseEmpty),
|
||||||
"{err:?}"
|
"{err:?}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -409,19 +429,20 @@ 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 mut net = given::a_network();
|
let net = given::a_network();
|
||||||
let mut args = with::WebhookArgs {
|
let args = with::WebhookArgs {
|
||||||
net: &mut net,
|
net: &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, &[], &mut args);
|
with::get_webhooks_by_page(1, &[], &args);
|
||||||
// register the webhook will return empty response
|
// register the webhook will return empty response
|
||||||
net.add_post_error(
|
net.on()
|
||||||
format!("https://api.{hostname}/repos/{repo_path}/hooks").as_str(),
|
.post(format!("https://api.{hostname}/repos/{repo_path}/hooks"))
|
||||||
"error",
|
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
);
|
.body("error")
|
||||||
|
.expect("mock");
|
||||||
|
|
||||||
let forge = given::a_github_forge(&repo_details, net);
|
let forge = given::a_github_forge(&repo_details, net);
|
||||||
|
|
||||||
|
@ -438,32 +459,34 @@ mod github {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn get_webhooks_by_page(
|
pub fn get_webhooks_by_page(page: u8, response: &[ReturnedWebhook], args: &WebhookArgs) {
|
||||||
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.add_get_response(
|
args.net
|
||||||
format!("https://api.{hostname}/repos/{repo_path}/hooks?page={page}").as_str(),
|
.on()
|
||||||
StatusCode::OK,
|
.get(format!(
|
||||||
json!(response).to_string().as_str(),
|
"https://api.{hostname}/repos/{repo_path}/hooks?page={page}"
|
||||||
);
|
))
|
||||||
|
.respond(StatusCode::OK)
|
||||||
|
.body(json!(response).to_string())
|
||||||
|
.expect("mock");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unregister_webhook(hook1: &ReturnedWebhook, args: &mut WebhookArgs) {
|
pub fn unregister_webhook(hook1: &ReturnedWebhook, args: &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.add_delete_response(
|
args.net
|
||||||
format!("https://api.{hostname}/repos/{repo_path}/hooks/{webhook_id}").as_str(),
|
.on()
|
||||||
StatusCode::OK,
|
.delete(format!(
|
||||||
"",
|
"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 mut network::MockNetwork,
|
pub net: &'a kxio::net::MockNet,
|
||||||
pub hostname: &'a Hostname,
|
pub hostname: &'a Hostname,
|
||||||
pub repo_path: &'a RepoPath,
|
pub repo_path: &'a RepoPath,
|
||||||
}
|
}
|
||||||
|
@ -497,21 +520,23 @@ mod github {
|
||||||
|
|
||||||
pub fn commit_states(
|
pub fn commit_states(
|
||||||
states: &[GithubState],
|
states: &[GithubState],
|
||||||
net: &mut MockNetwork,
|
net: &MockNet,
|
||||||
repo_details: &git::RepoDetails,
|
repo_details: &git::RepoDetails,
|
||||||
commit: &git::Commit,
|
commit: &git::Commit,
|
||||||
) {
|
) {
|
||||||
let response = json!(states
|
net.on()
|
||||||
.iter()
|
.get(a_commit_status_url(repo_details, commit))
|
||||||
.map(|state| GithubStatus {
|
.respond(StatusCode::OK)
|
||||||
state: state.to_owned()
|
.body(
|
||||||
})
|
json!(states
|
||||||
.collect::<Vec<_>>());
|
.iter()
|
||||||
net.add_get_response(
|
.map(|state| GithubStatus {
|
||||||
a_commit_status_url(repo_details, commit).as_str(),
|
state: state.to_owned()
|
||||||
StatusCode::OK,
|
})
|
||||||
response.to_string().as_str(),
|
.collect::<Vec<_>>())
|
||||||
);
|
.to_string(),
|
||||||
|
)
|
||||||
|
.expect("mock");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_commit_status_url(
|
pub fn a_commit_status_url(
|
||||||
|
@ -578,10 +603,11 @@ mod github {
|
||||||
|
|
||||||
pub fn a_github_forge(
|
pub fn a_github_forge(
|
||||||
repo_details: &git::RepoDetails,
|
repo_details: &git::RepoDetails,
|
||||||
net: impl Into<kxio::network::Network>,
|
net: impl Into<kxio::net::Net>,
|
||||||
) -> 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(),
|
||||||
|
@ -614,8 +640,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::network::MockNetwork {
|
pub fn a_network() -> kxio::net::MockNet {
|
||||||
kxio::network::MockNetwork::new()
|
kxio::net::mock()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_repo_listen_url(repo_details: &RepoDetails) -> RepoListenUrl {
|
pub fn a_repo_listen_url(repo_details: &RepoDetails) -> RepoListenUrl {
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
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,
|
||||||
|
@ -15,40 +13,36 @@ pub async fn list(
|
||||||
let net = &github.net;
|
let net = &github.net;
|
||||||
let mut page = 1;
|
let mut page = 1;
|
||||||
loop {
|
loop {
|
||||||
let request = network::NetRequest::new(
|
let result = net
|
||||||
network::RequestMethod::Get,
|
.get(format!(
|
||||||
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,
|
||||||
)),
|
))
|
||||||
github::webhook::headers(repo_details.forge.token()),
|
.headers(github::webhook::headers(repo_details.forge.token()))
|
||||||
network::RequestBody::None,
|
.send()
|
||||||
network::ResponseType::Json,
|
.await;
|
||||||
None,
|
|
||||||
network::NetRequestLogging::None,
|
|
||||||
);
|
|
||||||
let result = net.get::<Vec<github::GithubHook>>(request).await;
|
|
||||||
match result {
|
match result {
|
||||||
Ok(response) => {
|
Ok(response) => match response.json::<Vec<github::GithubHook>>().await {
|
||||||
let Some(list) = response.response_body() else {
|
Err(err) => {
|
||||||
#[cfg(not(tarpaulin_include))]
|
tracing::warn!(?err, "failed");
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
for hook in list {
|
Ok(list) => {
|
||||||
if hook
|
if list.is_empty() {
|
||||||
.url()
|
return Ok(ids);
|
||||||
.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,3 +1,5 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
//
|
//
|
||||||
use git_next_core::{git, webhook, ApiToken, BranchName};
|
use git_next_core::{git, webhook, ApiToken, BranchName};
|
||||||
|
|
||||||
|
@ -16,19 +18,24 @@ pub use unregister::unregister;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use authorisation::sign_body;
|
pub use authorisation::sign_body;
|
||||||
|
|
||||||
pub fn headers(token: &ApiToken) -> kxio::network::NetRequestHeaders {
|
pub fn headers(token: &ApiToken) -> HashMap<String, String> {
|
||||||
use secrecy::ExposeSecret;
|
use secrecy::ExposeSecret;
|
||||||
kxio::network::NetRequestHeaders::default()
|
|
||||||
.with("Accept", "application/vnd.github+json")
|
HashMap::from([
|
||||||
.with(
|
(
|
||||||
"User-Agent",
|
"Accept".to_string(),
|
||||||
format!("git-next/server/{}", clap::crate_version!()).as_str(),
|
"application/vnd.github+json".to_string(),
|
||||||
)
|
),
|
||||||
.with(
|
(
|
||||||
"Authorization",
|
"User-Agent".to_string(),
|
||||||
format!("Bearer {}", token.expose_secret()).as_str(),
|
format!("git-next/server/{}", clap::crate_version!()),
|
||||||
)
|
),
|
||||||
.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,8 +1,7 @@
|
||||||
//
|
//
|
||||||
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(
|
||||||
|
@ -23,45 +22,48 @@ 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();
|
||||||
let request = network::NetRequest::new(
|
match net
|
||||||
network::RequestMethod::Post,
|
.post(format!(
|
||||||
network::NetUrl::new(format!(
|
|
||||||
"https://api.{hostname}/repos/{}/hooks",
|
"https://api.{hostname}/repos/{}/hooks",
|
||||||
repo_details.repo_path
|
repo_details.repo_path
|
||||||
)),
|
))
|
||||||
github::webhook::headers(repo_details.forge.token()),
|
.headers(github::webhook::headers(repo_details.forge.token()))
|
||||||
network::RequestBody::Json(network::json!({
|
.body(
|
||||||
"name": "web",
|
json!({
|
||||||
"active": true,
|
"name": "web",
|
||||||
"events": ["push"],
|
"active": true,
|
||||||
"config": {
|
"events": ["push"],
|
||||||
"url": repo_listen_url.to_string(),
|
"config": {
|
||||||
"content_type": "json",
|
"url": repo_listen_url.to_string(),
|
||||||
"secret": authorisation.to_string(),
|
"content_type": "json",
|
||||||
"insecure_ssl": "0",
|
"secret": authorisation.to_string(),
|
||||||
}
|
"insecure_ssl": "0",
|
||||||
})),
|
}
|
||||||
network::ResponseType::Json,
|
})
|
||||||
None,
|
.to_string(),
|
||||||
network::NetRequestLogging::None,
|
)
|
||||||
);
|
.send()
|
||||||
let result = net.post_json::<github::GithubHook>(request).await;
|
.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,8 +2,6 @@
|
||||||
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,
|
||||||
|
@ -12,20 +10,13 @@ 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();
|
||||||
let request = network::NetRequest::new(
|
net.delete(format!(
|
||||||
network::RequestMethod::Delete,
|
"https://api.{hostname}/repos/{}/hooks/{}",
|
||||||
network::NetUrl::new(format!(
|
repo_details.repo_path, webhook_id
|
||||||
"https://api.{hostname}/repos/{}/hooks/{}",
|
))
|
||||||
repo_details.repo_path, webhook_id
|
.headers(github::webhook::headers(repo_details.forge.token()))
|
||||||
)),
|
.send()
|
||||||
github::webhook::headers(repo_details.forge.token()),
|
.await
|
||||||
network::RequestBody::None,
|
.map_err(|e| git::forge::webhook::Error::FailedToRegister(e.to_string()))
|
||||||
network::ResponseType::None,
|
.map(|_| ())
|
||||||
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