feat: add support for desktop notifications

Closes: kemitix/git-next#119
This commit is contained in:
Paul Campbell 2024-08-02 22:56:03 +01:00
parent 2b77eae508
commit 9a2fa2e8a5
10 changed files with 401 additions and 105 deletions

273
Cargo.lock generated
View file

@ -81,7 +81,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"getrandom", "getrandom 0.2.14",
"once_cell", "once_cell",
"version_check", "version_check",
"zerocopy", "zerocopy",
@ -102,6 +102,21 @@ version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.13" version = "0.6.13"
@ -162,6 +177,18 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "arrayref"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a"
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.7.4" version = "0.7.4"
@ -224,6 +251,12 @@ dependencies = [
"rustc-demangle", "rustc-demangle",
] ]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.21.7" version = "0.21.7"
@ -248,6 +281,23 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "blake2b_simd"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
dependencies = [
"arrayref",
"arrayvec 0.5.2",
"constant_time_eq",
]
[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.10.4" version = "0.10.4"
@ -304,6 +354,20 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets 0.52.5",
]
[[package]] [[package]]
name = "chumsky" name = "chumsky"
version = "0.9.3" version = "0.9.3"
@ -393,6 +457,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.4" version = "0.9.4"
@ -512,6 +582,16 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
[[package]]
name = "dbus"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48b5f0f36f1eebe901b0e6bee369a77ed3396334bf3f09abd46454a576f71819"
dependencies = [
"libc",
"libdbus-sys",
]
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.3.11" version = "0.3.11"
@ -570,6 +650,17 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "dirs"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]] [[package]]
name = "doc-comment" name = "doc-comment"
version = "0.3.3" version = "0.3.3"
@ -805,6 +896,17 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.14" version = "0.2.14"
@ -814,7 +916,7 @@ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
"libc", "libc",
"wasi", "wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen", "wasm-bindgen",
] ]
@ -856,6 +958,7 @@ dependencies = [
"kxio", "kxio",
"lettre", "lettre",
"mockall", "mockall",
"notifica",
"notify", "notify",
"pretty_assertions", "pretty_assertions",
"rand", "rand",
@ -2138,6 +2241,29 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.5.0" version = "0.5.0"
@ -2332,6 +2458,15 @@ version = "0.2.154"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
[[package]]
name = "libdbus-sys"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72"
dependencies = [
"pkg-config",
]
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.13" version = "0.4.13"
@ -2354,6 +2489,27 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "mac-notification-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb6b71a9a89cd38b395d994214297447e8e63b1ba5708a9a2b0b1048ceda76"
dependencies = [
"cc",
"chrono",
"dirs",
"objc-foundation",
]
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "matchers" name = "matchers"
version = "0.1.0" version = "0.1.0"
@ -2428,7 +2584,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
"wasi", "wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
@ -2504,6 +2660,17 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "notifica"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4e81fcdf9755383979b66adf525a66a8f621b55882a820552b201839b0ce3f7"
dependencies = [
"mac-notification-sys",
"notify-rust",
"winrt",
]
[[package]] [[package]]
name = "notify" name = "notify"
version = "6.1.1" version = "6.1.1"
@ -2523,6 +2690,16 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "notify-rust"
version = "3.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8da29142be3f71b2165a6b3991c26045b674edbf04cdfc42f323094fc3e4b5a"
dependencies = [
"dbus",
"mac-notification-sys",
]
[[package]] [[package]]
name = "nu-ansi-term" name = "nu-ansi-term"
version = "0.46.0" version = "0.46.0"
@ -2539,6 +2716,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.16.0" version = "1.16.0"
@ -2558,6 +2744,35 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
dependencies = [
"objc",
]
[[package]] [[package]]
name = "object" name = "object"
version = "0.32.2" version = "0.32.2"
@ -2832,7 +3047,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [ dependencies = [
"getrandom", "getrandom 0.2.14",
] ]
[[package]] [[package]]
@ -2855,6 +3070,12 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.4.1" version = "0.4.1"
@ -2873,6 +3094,17 @@ dependencies = [
"bitflags 2.5.0", "bitflags 2.5.0",
] ]
[[package]]
name = "redox_users"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
dependencies = [
"getrandom 0.1.16",
"redox_syscall 0.1.57",
"rust-argon2",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.4" version = "1.10.4"
@ -2973,13 +3205,25 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",
"getrandom", "getrandom 0.2.14",
"libc", "libc",
"spin", "spin",
"untrusted", "untrusted",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "rust-argon2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
dependencies = [
"base64 0.13.1",
"blake2b_simd",
"constant_time_eq",
"crossbeam-utils",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.23" version = "0.1.23"
@ -3770,7 +4014,7 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259" checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259"
dependencies = [ dependencies = [
"getrandom", "getrandom 0.2.14",
"rand", "rand",
"web-time", "web-time",
] ]
@ -3781,7 +4025,7 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c8a2469e56e6e5095c82ccd3afb98dad95f7af7929aab6d8ba8d6e0f73657da" checksum = "7c8a2469e56e6e5095c82ccd3afb98dad95f7af7929aab6d8ba8d6e0f73657da"
dependencies = [ dependencies = [
"arrayvec", "arrayvec 0.7.4",
] ]
[[package]] [[package]]
@ -3921,6 +4165,12 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@ -4230,6 +4480,15 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "winrt"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c248f437add7df81d305a345e9d143c8c0a9de00a51e46b42453c337181d16c9"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "xml-rs" name = "xml-rs"
version = "0.8.20" version = "0.8.20"

View file

@ -96,6 +96,9 @@ tokio = { version = "1.37", features = ["rt", "macros"] }
lettre = "0.11" lettre = "0.11"
sendmail = "2.0" sendmail = "2.0"
# desktop notifications
notifica = "3.0"
# Testing # Testing
assert2 = "0.3" assert2 = "0.3"
pretty_assertions = "1.4" pretty_assertions = "1.4"

View file

@ -70,6 +70,9 @@ notify = { workspace = true }
lettre = { workspace = true } lettre = { workspace = true }
sendmail = { workspace = true } sendmail = { workspace = true }
# desktop notifications
notifica = { workspace = true }
[dev-dependencies] [dev-dependencies]
# Testing # Testing
assert2 = { workspace = true } assert2 = { workspace = true }

View file

@ -29,6 +29,8 @@ See [Behaviour](#behaviour) to learn how we do this.
- Rust 1.76.0 or later - https://www.rust-lang.org - Rust 1.76.0 or later - https://www.rust-lang.org
- pgk-config - pgk-config
- libssl-dev - libssl-dev
- libdbus-1-dev (ubuntu/debian)
- dbus-devel (fedora)
### x86_64-unknown-linux-gnu ### x86_64-unknown-linux-gnu
@ -125,6 +127,9 @@ forge is running on.
The server should be able to notify the user when manual intervention is required. The server should be able to notify the user when manual intervention is required.
```toml ```toml
[shoult]
desktop = true
[shout.webhook] [shout.webhook]
url = "https//localhost:9090" url = "https//localhost:9090"
secret = "secret-password" secret = "secret-password"
@ -139,6 +144,10 @@ username = "git-next@example.com"
password = "MySecretEmailPassword42" password = "MySecretEmailPassword42"
``` ```
##### desktop
When specified as `true`, desktop notifications will be sent for some events.
##### webhook ##### webhook
Will send a POST request for some events. Will send a POST request for some events.

View file

@ -0,0 +1,9 @@
//
use crate::{repo::messages::NotifyUser, server::actor::handlers::notify_user::short_message};
pub(super) fn send_desktop_notification(msg: &NotifyUser) {
let message = short_message(msg);
if let Err(err) = notifica::notify("git-next", &message) {
tracing::warn!(?err, "failed to send desktop notification");
}
}

View file

@ -1,12 +1,10 @@
use std::ops::Deref as _; //
use git_next_core::server::{EmailConfig, SmtpConfig};
use git_next_core::{
git::UserNotification,
server::{EmailConfig, SmtpConfig},
};
use crate::repo::messages::NotifyUser; use crate::repo::messages::NotifyUser;
use super::{full_message, short_message};
#[derive(Debug)] #[derive(Debug)]
struct EmailMessage { struct EmailMessage {
from: String, from: String,
@ -19,8 +17,8 @@ pub(super) fn send_email(msg: &NotifyUser, 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: email_subject(msg), subject: short_message(msg),
body: email_body(msg), body: full_message(msg),
}; };
match email_config.smtp() { match email_config.smtp() {
Some(smtp) => send_email_smtp(email_message, smtp), Some(smtp) => send_email_smtp(email_message, smtp),
@ -70,92 +68,3 @@ fn do_send_email_smtp(email_message: EmailMessage, smtp: &SmtpConfig) -> Result<
tracing::info!(?response, "email sent via smtp"); tracing::info!(?response, "email sent via smtp");
})?) })?)
} }
fn email_subject(msg: &NotifyUser) -> String {
let tail = match msg.deref() {
UserNotification::CICheckFailed {
forge_alias,
repo_alias,
commit,
} => format!("CI Check Failed: {forge_alias}/{repo_alias}: {commit}"),
UserNotification::RepoConfigLoadFailure {
forge_alias,
repo_alias,
reason: _,
} => format!("Invalid Repo Config: {forge_alias}/{repo_alias}"),
UserNotification::WebhookRegistration {
forge_alias,
repo_alias,
reason: _,
} => format!("Failed Webhook Registration: {forge_alias}/{repo_alias}"),
UserNotification::DevNotBasedOnMain {
forge_alias,
repo_alias,
dev_branch: _,
main_branch: _,
dev_commit: _,
main_commit: _,
} => format!("Dev not based on Main: {forge_alias}/{repo_alias}"),
};
format!("[git-next] {tail}")
}
fn email_body(msg: &NotifyUser) -> String {
match msg.deref() {
UserNotification::CICheckFailed {
forge_alias,
repo_alias,
commit,
} => {
let sha = commit.sha();
let message = commit.message();
[
"CI Checks had Failed".to_string(),
format!("Forge: {forge_alias}\nRepo : {repo_alias}"),
format!("Commit:\n - {sha}\n - {message}"),
]
.join("\n\n")
}
UserNotification::RepoConfigLoadFailure {
forge_alias,
repo_alias,
reason,
} => [
"Failed to read or parse the .git-next.toml file from repo".to_string(),
format!(" - {reason}"),
format!("Forge: {forge_alias}\nRepo : {repo_alias}"),
]
.join("\n\n"),
UserNotification::WebhookRegistration {
forge_alias,
repo_alias,
reason,
} => [
"Failed to register webhook with the forge".to_string(),
format!(" - {reason}"),
format!("Forge: {forge_alias}\nRepo : {repo_alias}"),
]
.join("\n\n"),
UserNotification::DevNotBasedOnMain {
forge_alias,
repo_alias,
dev_branch,
main_branch,
dev_commit,
main_commit,
} => {
let dev_sha = dev_commit.sha();
let dev_message = dev_commit.message();
let main_sha = main_commit.sha();
let main_message = main_commit.message();
[
format!("The branch '{dev_branch}' is not based on the branch '{main_branch}'."),
format!("TODO: Rebase '{dev_branch}' onto '{main_branch}'."),
format!("Forge: {forge_alias}\nRepo : {repo_alias}"),
format!("{dev_branch}:\n - {dev_sha}\n - {dev_message}"),
format!("{main_branch}:\n - {main_sha}\n - {main_message}"),
]
.join("\n\n")
}
}
}

View file

@ -1,10 +1,15 @@
// //
mod desktop;
mod email; mod email;
mod webhook; mod webhook;
use std::ops::Deref as _;
use actix::prelude::*; use actix::prelude::*;
use desktop::send_desktop_notification;
use email::send_email; use email::send_email;
use git_next_core::git::UserNotification;
use tracing::Instrument; use tracing::Instrument;
use webhook::send_webhook; use webhook::send_webhook;
@ -26,9 +31,101 @@ impl Handler<NotifyUser> for ServerActor {
if let Some(email_config) = shout_config.email() { if let Some(email_config) = shout_config.email() {
send_email(&msg, email_config); send_email(&msg, email_config);
} }
if shout_config.desktop() {
send_desktop_notification(&msg);
}
} }
.in_current_span() .in_current_span()
.into_actor(self) .into_actor(self)
.wait(ctx); .wait(ctx);
} }
} }
fn short_message(msg: &NotifyUser) -> String {
let tail = match msg.deref() {
UserNotification::CICheckFailed {
forge_alias,
repo_alias,
commit,
} => format!("CI Check Failed: {forge_alias}/{repo_alias}: {commit}"),
UserNotification::RepoConfigLoadFailure {
forge_alias,
repo_alias,
reason: _,
} => format!("Invalid Repo Config: {forge_alias}/{repo_alias}"),
UserNotification::WebhookRegistration {
forge_alias,
repo_alias,
reason: _,
} => format!("Failed Webhook Registration: {forge_alias}/{repo_alias}"),
UserNotification::DevNotBasedOnMain {
forge_alias,
repo_alias,
dev_branch: _,
main_branch: _,
dev_commit: _,
main_commit: _,
} => format!("Dev not based on Main: {forge_alias}/{repo_alias}"),
};
format!("[git-next] {tail}")
}
fn full_message(msg: &NotifyUser) -> String {
match msg.deref() {
UserNotification::CICheckFailed {
forge_alias,
repo_alias,
commit,
} => {
let sha = commit.sha();
let message = commit.message();
[
"CI Checks had Failed".to_string(),
format!("Forge: {forge_alias}\nRepo : {repo_alias}"),
format!("Commit:\n - {sha}\n - {message}"),
]
.join("\n\n")
}
UserNotification::RepoConfigLoadFailure {
forge_alias,
repo_alias,
reason,
} => [
"Failed to read or parse the .git-next.toml file from repo".to_string(),
format!(" - {reason}"),
format!("Forge: {forge_alias}\nRepo : {repo_alias}"),
]
.join("\n\n"),
UserNotification::WebhookRegistration {
forge_alias,
repo_alias,
reason,
} => [
"Failed to register webhook with the forge".to_string(),
format!(" - {reason}"),
format!("Forge: {forge_alias}\nRepo : {repo_alias}"),
]
.join("\n\n"),
UserNotification::DevNotBasedOnMain {
forge_alias,
repo_alias,
dev_branch,
main_branch,
dev_commit,
main_commit,
} => {
let dev_sha = dev_commit.sha();
let dev_message = dev_commit.message();
let main_sha = main_commit.sha();
let main_message = main_commit.message();
[
format!("The branch '{dev_branch}' is not based on the branch '{main_branch}'."),
format!("TODO: Rebase '{dev_branch}' onto '{main_branch}'."),
format!("Forge: {forge_alias}\nRepo : {repo_alias}"),
format!("{dev_branch}:\n - {dev_sha}\n - {dev_message}"),
format!("{main_branch}:\n - {main_sha}\n - {main_message}"),
]
.join("\n\n")
}
}
}

View file

@ -8,6 +8,7 @@ url = "https://localhost:8080" # don't include any query path or a trailing slas
[shout] # where updates from git-next should be sent to alert the user [shout] # where updates from git-next should be sent to alert the user
# webhook = { url = "https//localhost:9090", secret = "secret-password" } # webhook = { url = "https//localhost:9090", secret = "secret-password" }
# desktop = true # enable desktop notifications
# [shout.email] # [shout.email]
# from = "git-next@example.com" # from = "git-next@example.com"

View file

@ -209,6 +209,7 @@ impl ServerStorage {
pub struct Shout { pub struct Shout {
webhook: Option<OutboundWebhook>, webhook: Option<OutboundWebhook>,
email: Option<EmailConfig>, email: Option<EmailConfig>,
desktop: bool,
} }
impl Shout { impl Shout {
pub const fn webhook(&self) -> Option<&OutboundWebhook> { pub const fn webhook(&self) -> Option<&OutboundWebhook> {
@ -226,6 +227,10 @@ impl Shout {
pub const fn email(&self) -> Option<&EmailConfig> { pub const fn email(&self) -> Option<&EmailConfig> {
self.email.as_ref() self.email.as_ref()
} }
pub const fn desktop(&self) -> bool {
self.desktop
}
} }
#[derive( #[derive(

View file

@ -527,6 +527,7 @@ url = "{listen_url}"
[shout] [shout]
webhook = {{ url = "{shout_webhook_url}", secret = "{shout_webhook_secret}" }} webhook = {{ url = "{shout_webhook_url}", secret = "{shout_webhook_secret}" }}
desktop = true
[shout.email] [shout.email]
from = "{shout_email_from}" from = "{shout_email_from}"
@ -773,7 +774,7 @@ mod given {
ServerStorage::new(a_name().into()) ServerStorage::new(a_name().into())
} }
pub fn a_shout() -> Shout { pub fn a_shout() -> Shout {
Shout::new(Some(an_outbound_webhook()), Some(an_email_config())) Shout::new(Some(an_outbound_webhook()), Some(an_email_config()), true)
} }
pub fn some_forge_configs() -> BTreeMap<String, ForgeConfig> { pub fn some_forge_configs() -> BTreeMap<String, ForgeConfig> {
[(a_name(), a_forge_config())].into() [(a_name(), a_forge_config())].into()