Compare commits

...

3 commits

Author SHA1 Message Date
134ed92f3d feat(tui): hightlight status message in colour
All checks were successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
2024-08-31 09:31:27 +01:00
a87241c3d1 fix(repo): avoid blocking threads when pausing
Some checks failed
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
Rust / build (push) Failing after 7m1s
2024-08-31 09:31:27 +01:00
a650996ecd fix(test): give actix more time to process message
All checks were successful
Rust / build (push) Successful in 10m4s
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Release Please / Release-plz (push) Successful in 1m1s
2024-08-31 09:31:27 +01:00
10 changed files with 147 additions and 39 deletions

64
Cargo.lock generated
View file

@ -320,6 +320,28 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bon"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3b71187ef9d11cfa48c023d574a00ec5e4850dcb145ef51619d99cc119486cb"
dependencies = [
"bon-macros",
]
[[package]]
name = "bon-macros"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f8407447d440da7b3de982f286d15e30e7646bef4ebca994eebebaa1690fd9c"
dependencies = [
"darling",
"ident_case",
"proc-macro2",
"quote",
"syn 2.0.76",
]
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.10.0" version = "1.10.0"
@ -592,6 +614,41 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "darling"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.76",
]
[[package]]
name = "darling_macro"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [
"darling_core",
"quote",
"syn 2.0.76",
]
[[package]] [[package]]
name = "dashmap" name = "dashmap"
version = "6.0.1" version = "6.0.1"
@ -1041,6 +1098,7 @@ dependencies = [
"actix-rt", "actix-rt",
"anyhow", "anyhow",
"assert2", "assert2",
"bon",
"bytes", "bytes",
"clap", "clap",
"color-eyre", "color-eyre",
@ -2350,6 +2408,12 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.5.0" version = "0.5.0"

View file

@ -79,6 +79,7 @@ time = "0.3"
standardwebhooks = "1.0" standardwebhooks = "1.0"
# boilerplate # boilerplate
bon = "2.0"
derive_more = { version = "1.0.0-beta", features = [ derive_more = { version = "1.0.0-beta", features = [
"as_ref", "as_ref",
"constructor", "constructor",

View file

@ -52,6 +52,7 @@ actix = { workspace = true }
actix-rt = { workspace = true } actix-rt = { workspace = true }
# boilerplate # boilerplate
bon = { workspace = true }
derive_more = { workspace = true } derive_more = { workspace = true }
derive-with = { workspace = true } derive-with = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }

View file

@ -53,7 +53,7 @@ impl Handler<AdvanceNext> for RepoActor {
let sleep_duration = self.sleep_duration; let sleep_duration = self.sleep_duration;
let log = self.log.clone(); let log = self.log.clone();
async move { async move {
std::thread::sleep(sleep_duration); actix_rt::time::sleep(sleep_duration).await;
do_send(&addr, ValidateRepo::new(message_token), log.as_ref()); do_send(&addr, ValidateRepo::new(message_token), log.as_ref());
} }
.in_current_span() .in_current_span()

View file

@ -2,7 +2,7 @@
use actix::prelude::*; use actix::prelude::*;
use git_next_core::git::{forge::commit::Status, graph, UserNotification}; use git_next_core::git::{forge::commit::Status, graph, UserNotification};
use tracing::{debug, Instrument as _}; use tracing::{debug, Instrument};
use crate::{ use crate::{
repo::{ repo::{
@ -37,8 +37,14 @@ impl Handler<ReceiveCIStatus> for RepoActor {
do_send(&addr, AdvanceMain::new(next), self.log.as_ref()); do_send(&addr, AdvanceMain::new(next), self.log.as_ref());
} }
Status::Pending => { Status::Pending => {
std::thread::sleep(sleep_duration); let log = self.log.clone();
do_send(&addr, ValidateRepo::new(message_token), self.log.as_ref()); async move {
actix_rt::time::sleep(sleep_duration).await;
do_send(&addr, ValidateRepo::new(message_token), log.as_ref());
}
.in_current_span()
.into_actor(self)
.wait(ctx);
} }
Status::Fail => { Status::Fail => {
tracing::warn!("Checks have failed"); tracing::warn!("Checks have failed");

View file

@ -51,15 +51,12 @@ async fn when_pending_should_recheck_ci_status() -> TestResult {
git::forge::commit::Status::Pending, git::forge::commit::Status::Pending,
))) )))
.await?; .await?;
actix_rt::time::sleep(Duration::from_millis(9)).await;
System::current().stop(); System::current().stop();
//then //then
tracing::debug!(?log, ""); tracing::debug!(?log, "");
log.read().map_err(|e| e.to_string()).map(|l| { log.require_message_containing("send: ValidateRepo")?;
assert!(l
.iter()
.any(|message| message.contains("send: ValidateRepo")));
})?;
Ok(()) Ok(())
} }

View file

@ -1,11 +1,17 @@
// //
use actix::Handler; use actix::Handler;
use ratatui::style::Color;
use crate::{ use crate::{
server::actor::messages::{RepoUpdate, ServerUpdate}, server::actor::messages::{RepoUpdate, ServerUpdate},
tui::{actor::ServerState, Tui}, tui::{actor::ServerState, Tui},
}; };
static OKAY: Color = Color::Green;
static PREP: Color = Color::Gray;
static ACTING: Color = Color::Yellow;
static WARN: Color = Color::LightYellow;
impl Handler<ServerUpdate> for Tui { impl Handler<ServerUpdate> for Tui {
type Result = (); type Result = ();
@ -35,50 +41,53 @@ impl Handler<ServerUpdate> for Tui {
RepoUpdate::Log { log } => { RepoUpdate::Log { log } => {
repo_state.update_log(log); repo_state.update_log(log);
} }
RepoUpdate::ValidateRepo => repo_state.update_message("polling..."), RepoUpdate::ValidateRepo => repo_state.update_message("polling...", ACTING),
RepoUpdate::Okay { main, next, dev } => { RepoUpdate::Okay { main, next, dev } => {
repo_state.clear_alert(); repo_state.clear_alert();
repo_state.update_message("okay"); repo_state.update_message("okay", OKAY);
*repo_state = repo_state.clone().ready(main, next, dev); *repo_state = repo_state.clone().ready(main, next, dev);
} }
RepoUpdate::Alert { alert } => { RepoUpdate::Alert { alert } => {
repo_state.alert(alert); repo_state.alert(alert);
} }
RepoUpdate::CheckingCI => { RepoUpdate::CheckingCI => {
repo_state.update_message("Checking CI status"); repo_state.update_message("Checking CI status", ACTING);
} }
RepoUpdate::AdvancingNext { commit, force: _ } => { RepoUpdate::AdvancingNext { commit, force: _ } => {
repo_state.update_message(format!("advancing next to {commit}")); repo_state
.update_message(format!("advancing next to {commit}"), ACTING);
} }
RepoUpdate::AdvancingMain { commit } => { RepoUpdate::AdvancingMain { commit } => {
repo_state.update_message(format!("advancing main to {commit}")); repo_state
.update_message(format!("advancing main to {commit}"), ACTING);
} }
RepoUpdate::Opening => { RepoUpdate::Opening => {
repo_state.update_message("opening..."); repo_state.update_message("opening...", PREP);
} }
RepoUpdate::Opened => { RepoUpdate::Opened => {
repo_state.update_message("opened"); repo_state.update_message("opened", PREP);
} }
RepoUpdate::LoadingConfigFromRepo => { RepoUpdate::LoadingConfigFromRepo => {
repo_state.update_message("loading config from repo..."); repo_state.update_message("loading config from repo...", PREP);
} }
RepoUpdate::ReceiveCIStatus { status } => { RepoUpdate::ReceiveCIStatus { status } => {
repo_state.update_message(format!("ci status: {status:?}")); repo_state.update_message(format!("ci status: {status:?}"), WARN);
} }
RepoUpdate::ReceiveRepoConfig { repo_config: _ } => { RepoUpdate::ReceiveRepoConfig { repo_config: _ } => {
repo_state.update_message("loaded config from repo"); repo_state.update_message("loaded config from repo", PREP);
} }
RepoUpdate::RegisteringWebhook => { RepoUpdate::RegisteringWebhook => {
repo_state.update_message("registering webhook..."); repo_state.update_message("registering webhook...", PREP);
} }
RepoUpdate::UnregisteringWebhook => { RepoUpdate::UnregisteringWebhook => {
repo_state.update_message("unregistering webhook..."); repo_state.update_message("unregistering webhook...", PREP);
} }
RepoUpdate::WebhookReceived { branch, push: _ } => { RepoUpdate::WebhookReceived { branch, push: _ } => {
repo_state.update_message(format!("webhook update: {branch:?}")); repo_state
.update_message(format!("webhook update: {branch:?}"), ACTING);
} }
RepoUpdate::RegisteredWebhook => { RepoUpdate::RegisteredWebhook => {
repo_state.update_message("registered webhook"); repo_state.update_message("registered webhook", PREP);
} }
} }
} }

View file

@ -2,9 +2,9 @@
use ratatui::{ use ratatui::{
layout::Alignment, layout::Alignment,
prelude::{Buffer, Rect}, prelude::{Buffer, Rect},
style::Stylize as _, style::{Color, Style, Stylize as _},
symbols::border, symbols::border,
text::Line, text::{Line, Span},
widgets::{block::Title, Block, Paragraph, StatefulWidget, Widget}, widgets::{block::Title, Block, Paragraph, StatefulWidget, Widget},
}; };
@ -127,7 +127,10 @@ impl From<ValidAppConfig> for ServerState {
repo_alias.clone(), repo_alias.clone(),
RepoState::Configured { RepoState::Configured {
repo_alias, repo_alias,
message: "configured".into(), message: RepoMessage::builder()
.text("configured".into())
.style(Style::default().fg(Color::LightGreen))
.build(),
alert: None, alert: None,
branches: rc.branches().clone(), branches: rc.branches().clone(),
log: git::graph::Log::default(), log: git::graph::Log::default(),
@ -137,7 +140,10 @@ impl From<ValidAppConfig> for ServerState {
repo_alias.clone(), repo_alias.clone(),
RepoState::Identified { RepoState::Identified {
repo_alias, repo_alias,
message: "identified".into(), message: RepoMessage::builder()
.text("identified".into())
.style(Style::default().fg(Color::Gray))
.build(),
alert: None, alert: None,
}, },
), ),
@ -182,23 +188,37 @@ pub struct ForgeState {
pub repos: BTreeMap<RepoAlias, RepoState>, pub repos: BTreeMap<RepoAlias, RepoState>,
} }
#[bon::builder]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RepoMessage {
text: String,
style: Style,
}
impl From<&RepoMessage> for Span<'_> {
fn from(value: &RepoMessage) -> Self {
Self::default()
.content(value.text.clone())
.style(value.style)
}
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum RepoState { pub enum RepoState {
Identified { Identified {
repo_alias: RepoAlias, repo_alias: RepoAlias,
message: String, message: RepoMessage,
alert: Option<String>, alert: Option<String>,
}, },
Configured { Configured {
repo_alias: RepoAlias, repo_alias: RepoAlias,
message: String, message: RepoMessage,
alert: Option<String>, alert: Option<String>,
branches: RepoBranches, branches: RepoBranches,
log: Log, log: Log,
}, },
Ready { Ready {
repo_alias: RepoAlias, repo_alias: RepoAlias,
message: String, message: RepoMessage,
alert: Option<String>, alert: Option<String>,
branches: RepoBranches, branches: RepoBranches,
view_state: ViewState, view_state: ViewState,
@ -241,14 +261,16 @@ impl RepoState {
} }
#[tracing::instrument] #[tracing::instrument]
pub fn update_message(&mut self, msg: impl Into<String> + std::fmt::Debug) { pub fn update_message(&mut self, msg: impl Into<String> + std::fmt::Debug, colour: Color) {
let msg: String = msg.into();
match self { match self {
Self::Identified { message, .. } Self::Identified { message, .. }
| Self::Configured { message, .. } | Self::Configured { message, .. }
| Self::Ready { message, .. } => { | Self::Ready { message, .. } => {
info!(?msg, "updating ui"); info!(?msg, "updating ui");
*message = msg; *message = RepoMessage::builder()
.text(msg.into())
.style(Style::default().fg(colour))
.build();
} }
} }
} }
@ -265,7 +287,7 @@ impl RepoState {
pub fn alert(&mut self, msg: impl Into<String> + std::fmt::Debug) { pub fn alert(&mut self, msg: impl Into<String> + std::fmt::Debug) {
let msg: String = msg.into(); let msg: String = msg.into();
tracing::info!(%msg, "new tui alert"); tracing::info!(%msg, "new tui alert");
self.update_message("ALERT"); self.update_message("ALERT", Color::Red);
match self { match self {
Self::Identified { alert, .. } Self::Identified { alert, .. }
| Self::Configured { alert, .. } | Self::Configured { alert, .. }

View file

@ -8,11 +8,13 @@ use ratatui::{
widgets::block::Title, widgets::block::Title,
}; };
use crate::tui::actor::RepoMessage;
pub struct Identity<'a> { pub struct Identity<'a> {
pub label: &'a str, pub label: &'a str,
pub repo_alias: &'a RepoAlias, pub repo_alias: &'a RepoAlias,
pub alert: Option<&'a str>, pub alert: Option<&'a str>,
pub message: &'a str, pub message: &'a RepoMessage,
pub repo_branches: Option<&'a RepoBranches>, pub repo_branches: Option<&'a RepoBranches>,
} }
impl<'a> Identity<'a> { impl<'a> Identity<'a> {
@ -20,7 +22,7 @@ impl<'a> Identity<'a> {
label: &'a str, label: &'a str,
repo_alias: &'a RepoAlias, repo_alias: &'a RepoAlias,
alert: Option<&'a str>, alert: Option<&'a str>,
message: &'a str, message: &'a RepoMessage,
repo_branches: Option<&'a RepoBranches>, repo_branches: Option<&'a RepoBranches>,
) -> Self { ) -> Self {
Self { Self {
@ -56,7 +58,10 @@ impl<'a> Identity<'a> {
vec![ vec![
Span::from(format!(" {repo_alias} ({label}) ")), Span::from(format!(" {repo_alias} ({label}) ")),
alert, alert,
Span::from(format!("({main}/{next}/{dev}) [{message}] ")), Span::from(format!("({main}/{next}/{dev}) [")),
message.into(),
// Span::from(message.},
Span::from("] "),
] ]
} }
} }

View file

@ -7,7 +7,10 @@ use git_next_core::{RepoAlias, RepoBranches};
use crate::{ use crate::{
git, git,
tui::{actor::RepoState, components::CommitLog}, tui::{
actor::{RepoMessage, RepoState},
components::CommitLog,
},
}; };
use identity::Identity; use identity::Identity;
@ -97,7 +100,7 @@ impl<'a> Widget for RepoWidget<'a> {
struct InnerRepoWidget<'a> { struct InnerRepoWidget<'a> {
pub label: &'a str, pub label: &'a str,
pub repo_alias: &'a RepoAlias, pub repo_alias: &'a RepoAlias,
pub message: &'a str, pub message: &'a RepoMessage,
pub alert: Option<&'a str>, pub alert: Option<&'a str>,
pub branches: Option<&'a RepoBranches>, pub branches: Option<&'a RepoBranches>,
pub log: Option<&'a git::graph::Log>, pub log: Option<&'a git::graph::Log>,