Compare commits
2 commits
421e85cb0b
...
f3bc16d701
Author | SHA1 | Date | |
---|---|---|---|
f3bc16d701 | |||
850e990ab4 |
18 changed files with 239 additions and 163 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -945,8 +945,6 @@ dependencies = [
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assert2",
|
"assert2",
|
||||||
"async-trait",
|
|
||||||
"base64 0.22.1",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
"clap",
|
"clap",
|
||||||
"derive-with",
|
"derive-with",
|
||||||
|
@ -964,7 +962,6 @@ dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
"secrecy",
|
"secrecy",
|
||||||
"sendmail",
|
"sendmail",
|
||||||
"serde",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"standardwebhooks",
|
"standardwebhooks",
|
||||||
"test-log",
|
"test-log",
|
||||||
|
@ -982,7 +979,6 @@ name = "git-next-core"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix",
|
"actix",
|
||||||
"actix-rt",
|
|
||||||
"assert2",
|
"assert2",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"derive-with",
|
"derive-with",
|
||||||
|
@ -999,9 +995,9 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"test-log",
|
"test-log",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"time",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
|
||||||
"ulid",
|
"ulid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1011,9 +1007,6 @@ version = "0.13.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert2",
|
"assert2",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.22.1",
|
|
||||||
"bytes",
|
|
||||||
"derive_more",
|
|
||||||
"git-next-core",
|
"git-next-core",
|
||||||
"kxio",
|
"kxio",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -1021,9 +1014,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
|
||||||
"tracing",
|
"tracing",
|
||||||
"ulid",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1032,8 +1023,6 @@ version = "0.13.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert2",
|
"assert2",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.22.1",
|
|
||||||
"bytes",
|
|
||||||
"clap",
|
"clap",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"git-next-core",
|
"git-next-core",
|
||||||
|
@ -1046,9 +1035,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
|
||||||
"tracing",
|
"tracing",
|
||||||
"ulid",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -31,18 +31,12 @@ kxio = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true }
|
||||||
|
|
||||||
# git
|
|
||||||
async-trait = { workspace = true }
|
|
||||||
|
|
||||||
# Conventional Commit check
|
# Conventional Commit check
|
||||||
git-conventional = { workspace = true }
|
git-conventional = { workspace = true }
|
||||||
|
|
||||||
# TOML parsing
|
# TOML parsing
|
||||||
toml = { workspace = true }
|
toml = { workspace = true }
|
||||||
|
|
||||||
# base64 decoding
|
|
||||||
base64 = { workspace = true }
|
|
||||||
|
|
||||||
# Actors
|
# Actors
|
||||||
actix = { workspace = true }
|
actix = { workspace = true }
|
||||||
actix-rt = { workspace = true }
|
actix-rt = { workspace = true }
|
||||||
|
@ -54,7 +48,6 @@ anyhow = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
# Webhooks
|
# Webhooks
|
||||||
serde = { workspace = true }
|
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
ulid = { workspace = true }
|
ulid = { workspace = true }
|
||||||
time = { workspace = true }
|
time = { workspace = true }
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
//
|
//
|
||||||
use crate::alerts::{messages::NotifyUser, short_message};
|
use crate::alerts::short_message;
|
||||||
|
use git_next_core::git::UserNotification;
|
||||||
|
|
||||||
pub(super) fn send_desktop_notification(msg: &NotifyUser) {
|
pub(super) fn send_desktop_notification(user_notification: &UserNotification) {
|
||||||
let message = short_message(msg);
|
let message = short_message(user_notification);
|
||||||
if let Err(err) = notifica::notify("git-next", &message) {
|
if let Err(err) = notifica::notify("git-next", &message) {
|
||||||
tracing::warn!(?err, "failed to send desktop notification");
|
tracing::warn!(?err, "failed to send desktop notification");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
//
|
//
|
||||||
use git_next_core::server::{EmailConfig, SmtpConfig};
|
use git_next_core::{
|
||||||
|
git::UserNotification,
|
||||||
|
server::{EmailConfig, SmtpConfig},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::alerts::{full_message, messages::NotifyUser, short_message};
|
use crate::alerts::{full_message, short_message};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct EmailMessage {
|
struct EmailMessage {
|
||||||
|
@ -11,12 +14,12 @@ struct EmailMessage {
|
||||||
body: String,
|
body: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn send_email(msg: &NotifyUser, email_config: &EmailConfig) {
|
pub(super) fn send_email(user_notification: &UserNotification, email_config: &EmailConfig) {
|
||||||
let email_message = EmailMessage {
|
let email_message = EmailMessage {
|
||||||
from: email_config.from().to_string(),
|
from: email_config.from().to_string(),
|
||||||
to: email_config.to().to_string(),
|
to: email_config.to().to_string(),
|
||||||
subject: short_message(msg),
|
subject: short_message(user_notification),
|
||||||
body: full_message(msg),
|
body: full_message(user_notification),
|
||||||
};
|
};
|
||||||
match email_config.smtp() {
|
match email_config.smtp() {
|
||||||
Some(smtp) => send_email_smtp(email_message, smtp),
|
Some(smtp) => send_email_smtp(email_message, smtp),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
|
|
||||||
use tracing::{info, instrument, Instrument as _};
|
use tracing::{info, instrument, Instrument as _};
|
||||||
|
|
||||||
use crate::alerts::{
|
use crate::alerts::{
|
||||||
|
@ -18,19 +19,21 @@ impl Handler<NotifyUser> for AlertsActor {
|
||||||
};
|
};
|
||||||
let net = self.net.clone();
|
let net = self.net.clone();
|
||||||
let shout = shout.clone();
|
let shout = shout.clone();
|
||||||
|
if let Some(user_notification) = self.history.sendable(msg.unwrap()) {
|
||||||
async move {
|
async move {
|
||||||
if let Some(webhook_config) = shout.webhook() {
|
if let Some(webhook_config) = shout.webhook() {
|
||||||
send_webhook(&msg, webhook_config, &net).await;
|
send_webhook(&user_notification, webhook_config, &net).await;
|
||||||
}
|
}
|
||||||
if let Some(email_config) = shout.email() {
|
if let Some(email_config) = shout.email() {
|
||||||
send_email(&msg, email_config);
|
send_email(&user_notification, email_config);
|
||||||
}
|
}
|
||||||
if shout.desktop() {
|
if shout.desktop() {
|
||||||
send_desktop_notification(&msg);
|
send_desktop_notification(&user_notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.in_current_span()
|
.in_current_span()
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
.wait(ctx);
|
.wait(ctx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,47 @@
|
||||||
|
//
|
||||||
|
use git_next_core::git::UserNotification;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct History {}
|
pub struct History {
|
||||||
|
/// The maximum age of an item in the history.
|
||||||
|
///
|
||||||
|
/// Items older than this will be dropped.
|
||||||
|
max_age_seconds: Duration,
|
||||||
|
|
||||||
|
/// Maps a user notification to when it was last seen.
|
||||||
|
///
|
||||||
|
/// The user notification will not be sent until after max_age_seconds from last seen.
|
||||||
|
///
|
||||||
|
/// Each time we see a given user notification, the last seen time will be updated.
|
||||||
|
items: HashMap<UserNotification, Instant>,
|
||||||
|
}
|
||||||
|
impl History {
|
||||||
|
pub fn new(max_age_seconds: Duration) -> Self {
|
||||||
|
Self {
|
||||||
|
max_age_seconds,
|
||||||
|
items: HashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sendable(&mut self, user_notification: UserNotification) -> Option<UserNotification> {
|
||||||
|
let now = Instant::now();
|
||||||
|
self.prune(&now); // remove expired items first
|
||||||
|
let contains_key = self.items.contains_key(&user_notification);
|
||||||
|
self.items.insert(user_notification.clone(), now);
|
||||||
|
if contains_key {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(user_notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prune(&mut self, now: &Instant) {
|
||||||
|
if let Some(threshold) = now.checked_sub(self.max_age_seconds) {
|
||||||
|
self.items.retain(|_, last_seen| *last_seen > threshold)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,86 +1,6 @@
|
||||||
//
|
//
|
||||||
use derive_more::Deref as _;
|
|
||||||
use git_next_core::{git::UserNotification, message, server::Shout};
|
use git_next_core::{git::UserNotification, message, server::Shout};
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
message!(UpdateShout: Shout: "Updated Shout configuration");
|
message!(UpdateShout: Shout: "Updated Shout configuration");
|
||||||
|
|
||||||
message!(NotifyUser: UserNotification: "Request to send the message payload to the notification webhook");
|
message!(NotifyUser: UserNotification: "Request to send the message payload to the notification webhook");
|
||||||
impl NotifyUser {
|
|
||||||
pub fn as_json(&self, timestamp: time::OffsetDateTime) -> serde_json::Value {
|
|
||||||
let timestamp = timestamp.unix_timestamp().to_string();
|
|
||||||
match self.deref() {
|
|
||||||
UserNotification::CICheckFailed {
|
|
||||||
forge_alias,
|
|
||||||
repo_alias,
|
|
||||||
commit,
|
|
||||||
} => json!({
|
|
||||||
"type": "cicheck.failed",
|
|
||||||
"timestamp": timestamp,
|
|
||||||
"data": {
|
|
||||||
"forge_alias": forge_alias,
|
|
||||||
"repo_alias": repo_alias,
|
|
||||||
"commit": {
|
|
||||||
"sha": commit.sha(),
|
|
||||||
"message": commit.message()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
UserNotification::RepoConfigLoadFailure {
|
|
||||||
forge_alias,
|
|
||||||
repo_alias,
|
|
||||||
reason,
|
|
||||||
} => json!({
|
|
||||||
"type": "config.load.failed",
|
|
||||||
"timestamp": timestamp,
|
|
||||||
"data": {
|
|
||||||
"forge_alias": forge_alias,
|
|
||||||
"repo_alias": repo_alias,
|
|
||||||
"reason": reason
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
UserNotification::WebhookRegistration {
|
|
||||||
forge_alias,
|
|
||||||
repo_alias,
|
|
||||||
reason,
|
|
||||||
} => json!({
|
|
||||||
"type": "webhook.registration.failed",
|
|
||||||
"timestamp": timestamp,
|
|
||||||
"data": {
|
|
||||||
"forge_alias": forge_alias,
|
|
||||||
"repo_alias": repo_alias,
|
|
||||||
"reason": reason
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
UserNotification::DevNotBasedOnMain {
|
|
||||||
forge_alias,
|
|
||||||
repo_alias,
|
|
||||||
dev_branch,
|
|
||||||
main_branch,
|
|
||||||
dev_commit,
|
|
||||||
main_commit,
|
|
||||||
} => json!({
|
|
||||||
"type": "branch.dev.not-on-main",
|
|
||||||
"timestamp": timestamp,
|
|
||||||
"data": {
|
|
||||||
"forge_alias": forge_alias,
|
|
||||||
"repo_alias": repo_alias,
|
|
||||||
"branches": {
|
|
||||||
"dev": dev_branch,
|
|
||||||
"main": main_branch
|
|
||||||
},
|
|
||||||
"commits": {
|
|
||||||
"dev": {
|
|
||||||
"sha": dev_commit.sha(),
|
|
||||||
"message": dev_commit.message()
|
|
||||||
},
|
|
||||||
"main": {
|
|
||||||
"sha": main_commit.sha(),
|
|
||||||
"message": main_commit.message()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::ops::Deref as _;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
|
|
||||||
|
@ -8,7 +6,6 @@ use derive_more::derive::Constructor;
|
||||||
use git_next_core::{git::UserNotification, server::Shout};
|
use git_next_core::{git::UserNotification, server::Shout};
|
||||||
|
|
||||||
pub use history::History;
|
pub use history::History;
|
||||||
use messages::NotifyUser;
|
|
||||||
|
|
||||||
mod desktop;
|
mod desktop;
|
||||||
mod email;
|
mod email;
|
||||||
|
@ -17,10 +14,12 @@ mod history;
|
||||||
pub mod messages;
|
pub mod messages;
|
||||||
mod webhook;
|
mod webhook;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
#[derive(Debug, Constructor)]
|
#[derive(Debug, Constructor)]
|
||||||
pub struct AlertsActor {
|
pub struct AlertsActor {
|
||||||
shout: Option<Shout>, // config for sending alerts to users
|
shout: Option<Shout>, // config for sending alerts to users
|
||||||
#[allow(dead_code)] // TODO (#128) Prevent duplicate user notifications
|
|
||||||
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::network::Network,
|
||||||
}
|
}
|
||||||
|
@ -29,8 +28,8 @@ impl Actor for AlertsActor {
|
||||||
type Context = Context<Self>;
|
type Context = Context<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn short_message(msg: &NotifyUser) -> String {
|
fn short_message(user_notification: &UserNotification) -> String {
|
||||||
let tail = match msg.deref() {
|
let tail = match user_notification {
|
||||||
UserNotification::CICheckFailed {
|
UserNotification::CICheckFailed {
|
||||||
forge_alias,
|
forge_alias,
|
||||||
repo_alias,
|
repo_alias,
|
||||||
|
@ -58,8 +57,8 @@ fn short_message(msg: &NotifyUser) -> String {
|
||||||
format!("[git-next] {tail}")
|
format!("[git-next] {tail}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn full_message(msg: &NotifyUser) -> String {
|
fn full_message(user_notification: &UserNotification) -> String {
|
||||||
match msg.deref() {
|
match user_notification {
|
||||||
UserNotification::CICheckFailed {
|
UserNotification::CICheckFailed {
|
||||||
forge_alias,
|
forge_alias,
|
||||||
repo_alias,
|
repo_alias,
|
||||||
|
|
66
crates/cli/src/alerts/tests/history.rs
Normal file
66
crates/cli/src/alerts/tests/history.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use assert2::let_assert;
|
||||||
|
use git_next_core::git::UserNotification;
|
||||||
|
|
||||||
|
use crate::{alerts::History, repo::tests::given};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_history_is_empty_then_message_is_passed() {
|
||||||
|
let mut history = History::new(Duration::from_millis(1));
|
||||||
|
|
||||||
|
let user_notification = UserNotification::RepoConfigLoadFailure {
|
||||||
|
forge_alias: given::a_forge_alias(),
|
||||||
|
repo_alias: given::a_repo_alias(),
|
||||||
|
reason: given::a_name(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = history.sendable(user_notification);
|
||||||
|
|
||||||
|
assert!(result.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_history_has_expired_then_message_is_passed() {
|
||||||
|
let dur = Duration::from_millis(1);
|
||||||
|
let mut history = History::new(dur);
|
||||||
|
|
||||||
|
let user_notification = UserNotification::RepoConfigLoadFailure {
|
||||||
|
forge_alias: given::a_forge_alias(),
|
||||||
|
repo_alias: given::a_repo_alias(),
|
||||||
|
reason: given::a_name(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// add message to history
|
||||||
|
let result = history.sendable(user_notification);
|
||||||
|
let_assert!(Some(user_notification) = result);
|
||||||
|
|
||||||
|
std::thread::sleep(dur);
|
||||||
|
|
||||||
|
// after dur, message has expired, so is still valid
|
||||||
|
let result = history.sendable(user_notification);
|
||||||
|
assert!(result.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_history_has_unexpired_then_message_is_blocked() {
|
||||||
|
let dur = Duration::from_millis(1);
|
||||||
|
let mut history = History::new(dur);
|
||||||
|
|
||||||
|
let user_notification = UserNotification::RepoConfigLoadFailure {
|
||||||
|
forge_alias: given::a_forge_alias(),
|
||||||
|
repo_alias: given::a_repo_alias(),
|
||||||
|
reason: given::a_name(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// add message to history
|
||||||
|
let result = history.sendable(user_notification);
|
||||||
|
let_assert!(Some(user_notification) = result);
|
||||||
|
|
||||||
|
// no time passed
|
||||||
|
// std::thread::sleep(dur);
|
||||||
|
|
||||||
|
// after dur, message has expired, so is still valid
|
||||||
|
let result = history.sendable(user_notification);
|
||||||
|
assert!(result.is_none());
|
||||||
|
}
|
1
crates/cli/src/alerts/tests/mod.rs
Normal file
1
crates/cli/src/alerts/tests/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
mod history;
|
|
@ -1,12 +1,10 @@
|
||||||
//
|
//
|
||||||
use git_next_core::server::OutboundWebhook;
|
use git_next_core::{git::UserNotification, server::OutboundWebhook};
|
||||||
use secrecy::ExposeSecret as _;
|
use secrecy::ExposeSecret as _;
|
||||||
use standardwebhooks::Webhook;
|
use standardwebhooks::Webhook;
|
||||||
|
|
||||||
use crate::alerts::messages::NotifyUser;
|
|
||||||
|
|
||||||
pub(super) async fn send_webhook(
|
pub(super) async fn send_webhook(
|
||||||
msg: &NotifyUser,
|
user_notification: &UserNotification,
|
||||||
webhook_config: &OutboundWebhook,
|
webhook_config: &OutboundWebhook,
|
||||||
net: &kxio::network::Network,
|
net: &kxio::network::Network,
|
||||||
) {
|
) {
|
||||||
|
@ -16,18 +14,18 @@ pub(super) async fn send_webhook(
|
||||||
tracing::warn!("Invalid notification configuration (signer) - can't sent notification");
|
tracing::warn!("Invalid notification configuration (signer) - can't sent notification");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
do_send_webhook(msg, webhook, webhook_config, net).await;
|
do_send_webhook(user_notification, webhook, webhook_config, net).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_send_webhook(
|
async fn do_send_webhook(
|
||||||
msg: &NotifyUser,
|
user_notification: &UserNotification,
|
||||||
webhook: Webhook,
|
webhook: Webhook,
|
||||||
webhook_config: &OutboundWebhook,
|
webhook_config: &OutboundWebhook,
|
||||||
net: &kxio::network::Network,
|
net: &kxio::network::Network,
|
||||||
) {
|
) {
|
||||||
let message_id = format!("msg_{}", ulid::Ulid::new());
|
let message_id = format!("msg_{}", ulid::Ulid::new());
|
||||||
let timestamp = time::OffsetDateTime::now_utc();
|
let timestamp = time::OffsetDateTime::now_utc();
|
||||||
let payload = msg.as_json(timestamp);
|
let payload = user_notification.as_json(timestamp);
|
||||||
let timestamp = timestamp.unix_timestamp();
|
let timestamp = timestamp.unix_timestamp();
|
||||||
let to_sign = format!("{message_id}.{timestamp}.{payload}");
|
let to_sign = format!("{message_id}.{timestamp}.{payload}");
|
||||||
tracing::info!(?to_sign, "");
|
tracing::info!(?to_sign, "");
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub mod messages;
|
||||||
mod notifications;
|
mod notifications;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
pub mod tests;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct RepoActorLog(std::sync::Arc<std::sync::RwLock<Vec<String>>>);
|
pub struct RepoActorLog(std::sync::Arc<std::sync::RwLock<Vec<String>>>);
|
||||||
|
|
|
@ -14,14 +14,12 @@ github = []
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# logging
|
# logging
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
|
||||||
|
|
||||||
# fs/network
|
# fs/network
|
||||||
kxio = { workspace = true }
|
kxio = { workspace = true }
|
||||||
|
|
||||||
# Actors
|
# Actors
|
||||||
actix = { workspace = true }
|
actix = { workspace = true }
|
||||||
actix-rt = { workspace = true }
|
|
||||||
|
|
||||||
# TOML parsing
|
# TOML parsing
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
@ -37,6 +35,7 @@ async-trait = { workspace = true }
|
||||||
|
|
||||||
# Webhooks
|
# Webhooks
|
||||||
ulid = { workspace = true }
|
ulid = { workspace = true }
|
||||||
|
time = { workspace = true }
|
||||||
|
|
||||||
# boilerplate
|
# boilerplate
|
||||||
derive_more = { workspace = true }
|
derive_more = { workspace = true }
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use derive_more::derive::Display;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
crate::newtype!(BranchName: String, derive_more::Display, Default, Serialize: "The name of a Git branch");
|
crate::newtype!(BranchName: String, Display, Default, Hash, Serialize: "The name of a Git branch");
|
||||||
|
|
|
@ -3,6 +3,6 @@ use serde::Serialize;
|
||||||
|
|
||||||
use crate::newtype;
|
use crate::newtype;
|
||||||
|
|
||||||
newtype!(RepoAlias: String, Display, Default, PartialOrd, Ord, Serialize: r#"The alias of a repo.
|
newtype!(RepoAlias: String, Display, Default, Hash, PartialOrd, Ord, Serialize: r#"The alias of a repo.
|
||||||
|
|
||||||
This is the alias for the repo within `git-next-server.toml`."#);
|
This is the alias for the repo within `git-next-server.toml`."#);
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
//
|
//
|
||||||
use crate::{git::Commit, BranchName, ForgeAlias, RepoAlias};
|
use crate::{git::Commit, BranchName, ForgeAlias, RepoAlias};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub enum UserNotification {
|
pub enum UserNotification {
|
||||||
CICheckFailed {
|
CICheckFailed {
|
||||||
forge_alias: ForgeAlias,
|
forge_alias: ForgeAlias,
|
||||||
|
@ -27,3 +28,81 @@ pub enum UserNotification {
|
||||||
main_commit: Commit,
|
main_commit: Commit,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
impl UserNotification {
|
||||||
|
pub fn as_json(&self, timestamp: time::OffsetDateTime) -> serde_json::Value {
|
||||||
|
let timestamp = timestamp.unix_timestamp().to_string();
|
||||||
|
match self {
|
||||||
|
Self::CICheckFailed {
|
||||||
|
forge_alias,
|
||||||
|
repo_alias,
|
||||||
|
commit,
|
||||||
|
} => json!({
|
||||||
|
"type": "cicheck.failed",
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"data": {
|
||||||
|
"forge_alias": forge_alias,
|
||||||
|
"repo_alias": repo_alias,
|
||||||
|
"commit": {
|
||||||
|
"sha": commit.sha(),
|
||||||
|
"message": commit.message()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Self::RepoConfigLoadFailure {
|
||||||
|
forge_alias,
|
||||||
|
repo_alias,
|
||||||
|
reason,
|
||||||
|
} => json!({
|
||||||
|
"type": "config.load.failed",
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"data": {
|
||||||
|
"forge_alias": forge_alias,
|
||||||
|
"repo_alias": repo_alias,
|
||||||
|
"reason": reason
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Self::WebhookRegistration {
|
||||||
|
forge_alias,
|
||||||
|
repo_alias,
|
||||||
|
reason,
|
||||||
|
} => json!({
|
||||||
|
"type": "webhook.registration.failed",
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"data": {
|
||||||
|
"forge_alias": forge_alias,
|
||||||
|
"repo_alias": repo_alias,
|
||||||
|
"reason": reason
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Self::DevNotBasedOnMain {
|
||||||
|
forge_alias,
|
||||||
|
repo_alias,
|
||||||
|
dev_branch,
|
||||||
|
main_branch,
|
||||||
|
dev_commit,
|
||||||
|
main_commit,
|
||||||
|
} => json!({
|
||||||
|
"type": "branch.dev.not-on-main",
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"data": {
|
||||||
|
"forge_alias": forge_alias,
|
||||||
|
"repo_alias": repo_alias,
|
||||||
|
"branches": {
|
||||||
|
"dev": dev_branch,
|
||||||
|
"main": main_branch
|
||||||
|
},
|
||||||
|
"commits": {
|
||||||
|
"dev": {
|
||||||
|
"sha": dev_commit.sha(),
|
||||||
|
"message": dev_commit.message()
|
||||||
|
},
|
||||||
|
"main": {
|
||||||
|
"sha": main_commit.sha(),
|
||||||
|
"message": main_commit.message()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,9 +12,6 @@ git-next-core = { workspace = true }
|
||||||
# logging
|
# logging
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|
||||||
# base64 decoding
|
|
||||||
base64 = { workspace = true }
|
|
||||||
|
|
||||||
# git
|
# git
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
|
|
||||||
|
@ -24,18 +21,10 @@ kxio = { workspace = true }
|
||||||
# TOML parsing
|
# TOML parsing
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
toml = { workspace = true }
|
|
||||||
|
|
||||||
# Secrets and Password
|
# Secrets and Password
|
||||||
secrecy = { workspace = true }
|
secrecy = { workspace = true }
|
||||||
|
|
||||||
# Webhooks
|
|
||||||
bytes = { workspace = true }
|
|
||||||
ulid = { workspace = true }
|
|
||||||
|
|
||||||
# boilerplate
|
|
||||||
derive_more = { workspace = true }
|
|
||||||
|
|
||||||
# # Actors
|
# # Actors
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,6 @@ hmac = { workspace = true }
|
||||||
sha2 = { workspace = true }
|
sha2 = { workspace = true }
|
||||||
hex = { workspace = true }
|
hex = { workspace = true }
|
||||||
|
|
||||||
# base64 decoding
|
|
||||||
base64 = { workspace = true }
|
|
||||||
|
|
||||||
# git
|
# git
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
|
|
||||||
|
@ -32,15 +29,10 @@ kxio = { workspace = true }
|
||||||
# TOML parsing
|
# TOML parsing
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
toml = { workspace = true }
|
|
||||||
|
|
||||||
# Secrets and Password
|
# Secrets and Password
|
||||||
secrecy = { workspace = true }
|
secrecy = { workspace = true }
|
||||||
|
|
||||||
# Webhooks
|
|
||||||
bytes = { workspace = true }
|
|
||||||
ulid = { workspace = true }
|
|
||||||
|
|
||||||
# boilerplate
|
# boilerplate
|
||||||
derive_more = { workspace = true }
|
derive_more = { workspace = true }
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue