From 9a2fa2e8a5b8a27c20e903d3a793c8b985927ff3 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Fri, 2 Aug 2024 22:56:03 +0100 Subject: [PATCH] feat: add support for desktop notifications Closes: kemitix/git-next#119 --- Cargo.lock | 273 +++++++++++++++++- Cargo.toml | 3 + crates/cli/Cargo.toml | 3 + crates/cli/README.md | 9 + .../actor/handlers/notify_user/desktop.rs | 9 + .../actor/handlers/notify_user/email.rs | 103 +------ .../server/actor/handlers/notify_user/mod.rs | 97 +++++++ crates/cli/src/server/server-default.toml | 1 + crates/core/src/config/server.rs | 5 + crates/core/src/config/tests.rs | 3 +- 10 files changed, 401 insertions(+), 105 deletions(-) create mode 100644 crates/cli/src/server/actor/handlers/notify_user/desktop.rs diff --git a/Cargo.lock b/Cargo.lock index fcff96c..3525cd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,7 +81,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.14", "once_cell", "version_check", "zerocopy", @@ -102,6 +102,21 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "anstream" version = "0.6.13" @@ -162,6 +177,18 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "arrayvec" version = "0.7.4" @@ -224,6 +251,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -248,6 +281,23 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "block-buffer" version = "0.10.4" @@ -304,6 +354,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "chumsky" version = "0.9.3" @@ -393,6 +457,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "core-foundation" version = "0.9.4" @@ -512,6 +582,16 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "deranged" version = "0.3.11" @@ -570,6 +650,17 @@ dependencies = [ "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]] name = "doc-comment" version = "0.3.3" @@ -805,6 +896,17 @@ dependencies = [ "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]] name = "getrandom" version = "0.2.14" @@ -814,7 +916,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -856,6 +958,7 @@ dependencies = [ "kxio", "lettre", "mockall", + "notifica", "notify", "pretty_assertions", "rand", @@ -2138,6 +2241,29 @@ dependencies = [ "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]] name = "idna" version = "0.5.0" @@ -2332,6 +2458,15 @@ version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "linux-raw-sys" version = "0.4.13" @@ -2354,6 +2489,27 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "matchers" version = "0.1.0" @@ -2428,7 +2584,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -2504,6 +2660,17 @@ dependencies = [ "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]] name = "notify" version = "6.1.1" @@ -2523,6 +2690,16 @@ dependencies = [ "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]] name = "nu-ansi-term" version = "0.46.0" @@ -2539,6 +2716,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "num_cpus" version = "1.16.0" @@ -2558,6 +2744,35 @@ dependencies = [ "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]] name = "object" version = "0.32.2" @@ -2832,7 +3047,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.14", ] [[package]] @@ -2855,6 +3070,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "redox_syscall" version = "0.4.1" @@ -2873,6 +3094,17 @@ dependencies = [ "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]] name = "regex" version = "1.10.4" @@ -2973,13 +3205,25 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.14", "libc", "spin", "untrusted", "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]] name = "rustc-demangle" version = "0.1.23" @@ -3770,7 +4014,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259" dependencies = [ - "getrandom", + "getrandom 0.2.14", "rand", "web-time", ] @@ -3781,7 +4025,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c8a2469e56e6e5095c82ccd3afb98dad95f7af7929aab6d8ba8d6e0f73657da" dependencies = [ - "arrayvec", + "arrayvec 0.7.4", ] [[package]] @@ -3921,6 +4165,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -4230,6 +4480,15 @@ dependencies = [ "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]] name = "xml-rs" version = "0.8.20" diff --git a/Cargo.toml b/Cargo.toml index aa33ec1..f89a6af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,6 +96,9 @@ tokio = { version = "1.37", features = ["rt", "macros"] } lettre = "0.11" sendmail = "2.0" +# desktop notifications +notifica = "3.0" + # Testing assert2 = "0.3" pretty_assertions = "1.4" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index a8b4934..6b79b6f 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -70,6 +70,9 @@ notify = { workspace = true } lettre = { workspace = true } sendmail = { workspace = true } +# desktop notifications +notifica = { workspace = true } + [dev-dependencies] # Testing assert2 = { workspace = true } diff --git a/crates/cli/README.md b/crates/cli/README.md index 3e56713..cbfce33 100644 --- a/crates/cli/README.md +++ b/crates/cli/README.md @@ -29,6 +29,8 @@ See [Behaviour](#behaviour) to learn how we do this. - Rust 1.76.0 or later - https://www.rust-lang.org - pgk-config - libssl-dev +- libdbus-1-dev (ubuntu/debian) +- dbus-devel (fedora) ### 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. ```toml +[shoult] +desktop = true + [shout.webhook] url = "https//localhost:9090" secret = "secret-password" @@ -139,6 +144,10 @@ username = "git-next@example.com" password = "MySecretEmailPassword42" ``` +##### desktop + +When specified as `true`, desktop notifications will be sent for some events. + ##### webhook Will send a POST request for some events. diff --git a/crates/cli/src/server/actor/handlers/notify_user/desktop.rs b/crates/cli/src/server/actor/handlers/notify_user/desktop.rs new file mode 100644 index 0000000..dec1f44 --- /dev/null +++ b/crates/cli/src/server/actor/handlers/notify_user/desktop.rs @@ -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"); + } +} diff --git a/crates/cli/src/server/actor/handlers/notify_user/email.rs b/crates/cli/src/server/actor/handlers/notify_user/email.rs index d011eed..163405e 100644 --- a/crates/cli/src/server/actor/handlers/notify_user/email.rs +++ b/crates/cli/src/server/actor/handlers/notify_user/email.rs @@ -1,12 +1,10 @@ -use std::ops::Deref as _; - -use git_next_core::{ - git::UserNotification, - server::{EmailConfig, SmtpConfig}, -}; +// +use git_next_core::server::{EmailConfig, SmtpConfig}; use crate::repo::messages::NotifyUser; +use super::{full_message, short_message}; + #[derive(Debug)] struct EmailMessage { from: String, @@ -19,8 +17,8 @@ pub(super) fn send_email(msg: &NotifyUser, email_config: &EmailConfig) { let email_message = EmailMessage { from: email_config.from().to_string(), to: email_config.to().to_string(), - subject: email_subject(msg), - body: email_body(msg), + subject: short_message(msg), + body: full_message(msg), }; match email_config.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"); })?) } - -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") - } - } -} diff --git a/crates/cli/src/server/actor/handlers/notify_user/mod.rs b/crates/cli/src/server/actor/handlers/notify_user/mod.rs index c7333a6..d28f482 100644 --- a/crates/cli/src/server/actor/handlers/notify_user/mod.rs +++ b/crates/cli/src/server/actor/handlers/notify_user/mod.rs @@ -1,10 +1,15 @@ // +mod desktop; mod email; mod webhook; +use std::ops::Deref as _; + use actix::prelude::*; +use desktop::send_desktop_notification; use email::send_email; +use git_next_core::git::UserNotification; use tracing::Instrument; use webhook::send_webhook; @@ -26,9 +31,101 @@ impl Handler for ServerActor { if let Some(email_config) = shout_config.email() { send_email(&msg, email_config); } + if shout_config.desktop() { + send_desktop_notification(&msg); + } } .in_current_span() .into_actor(self) .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") + } + } +} diff --git a/crates/cli/src/server/server-default.toml b/crates/cli/src/server/server-default.toml index e4be5a5..25a7f41 100644 --- a/crates/cli/src/server/server-default.toml +++ b/crates/cli/src/server/server-default.toml @@ -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 # webhook = { url = "https//localhost:9090", secret = "secret-password" } +# desktop = true # enable desktop notifications # [shout.email] # from = "git-next@example.com" diff --git a/crates/core/src/config/server.rs b/crates/core/src/config/server.rs index 13bb1f4..a6aa532 100644 --- a/crates/core/src/config/server.rs +++ b/crates/core/src/config/server.rs @@ -209,6 +209,7 @@ impl ServerStorage { pub struct Shout { webhook: Option, email: Option, + desktop: bool, } impl Shout { pub const fn webhook(&self) -> Option<&OutboundWebhook> { @@ -226,6 +227,10 @@ impl Shout { pub const fn email(&self) -> Option<&EmailConfig> { self.email.as_ref() } + + pub const fn desktop(&self) -> bool { + self.desktop + } } #[derive( diff --git a/crates/core/src/config/tests.rs b/crates/core/src/config/tests.rs index 152dc43..87cb5b2 100644 --- a/crates/core/src/config/tests.rs +++ b/crates/core/src/config/tests.rs @@ -527,6 +527,7 @@ url = "{listen_url}" [shout] webhook = {{ url = "{shout_webhook_url}", secret = "{shout_webhook_secret}" }} +desktop = true [shout.email] from = "{shout_email_from}" @@ -773,7 +774,7 @@ mod given { ServerStorage::new(a_name().into()) } 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 { [(a_name(), a_forge_config())].into()