Compare commits

..

4 Commits

Author SHA1 Message Date
Tom Alexander
606832f505 Only print status when activities change or enough time has passed. 2026-04-02 18:32:47 -04:00
Tom Alexander
9083a09455 Add repair flag to build. 2026-04-02 18:00:54 -04:00
Tom Alexander
1cc53ba631 Display progress. 2026-03-31 22:46:51 -04:00
Tom Alexander
1f5c080138 Log nix messages. 2026-03-31 18:54:29 -04:00
6 changed files with 126 additions and 14 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/target
/work
/example_logs
TODO.org

View File

@@ -2,6 +2,7 @@ use std::fmt::Display;
use std::num::TryFromIntError;
use std::str::Utf8Error;
use std::string::FromUtf8Error;
use std::time::SystemTimeError;
use crate::nix_util::ActivityIdAlreadyInTreeError;
use crate::nix_util::ActivityIdNotInTreeError;
@@ -24,6 +25,7 @@ pub(crate) enum CustomError {
TryFromIntError(#[allow(dead_code)] TryFromIntError),
ActivityIdNotInTreeError(#[allow(dead_code)] ActivityIdNotInTreeError),
ActivityIdAlreadyInTreeError(#[allow(dead_code)] ActivityIdAlreadyInTreeError),
SystemTime(#[allow(dead_code)] SystemTimeError),
}
impl Display for CustomError {
@@ -127,3 +129,9 @@ impl From<ActivityIdAlreadyInTreeError> for CustomError {
CustomError::ActivityIdAlreadyInTreeError(value)
}
}
impl From<SystemTimeError> for CustomError {
fn from(value: SystemTimeError) -> Self {
CustomError::SystemTime(value)
}
}

View File

@@ -202,6 +202,68 @@ impl Activity {
}
}
pub(crate) fn get_progress_text(&self) -> Option<Cow<'_, str>> {
match self {
Activity::Root(_activity_root) => {
// TODO
panic!("Attempted to get_progress_text of a root activity.");
}
Activity::Unknown(_activity_unknown) => None,
Activity::CopyPath(activity_copy_path) => Some(Cow::Owned(format!(
"[{}/{}]",
activity_copy_path.done, activity_copy_path.expected
))),
Activity::FileTransfer(activity_file_transfer) => Some(Cow::Owned(format!(
"[{}/{}]",
activity_file_transfer.done, activity_file_transfer.expected
))),
Activity::Realize(_activity_realize) => {
// TODO
panic!("Attempted to get_progress_text of a realize activity.");
}
Activity::CopyPaths(activity_copy_paths) => Some(Cow::Owned(format!(
"[{}/{}]",
activity_copy_paths.done, activity_copy_paths.expected
))),
Activity::Builds(activity_builds) => Some(Cow::Owned(format!(
"[{}/{}]",
activity_builds.done, activity_builds.expected
))),
Activity::Build(activity_build) => activity_build
.phase
.as_ref()
.map(|phase| Cow::Owned(format!("[{}]", phase))),
Activity::OptimizeStore(_activity_optimize_store) => {
// TODO
panic!("Attempted to get_progress_text of a optimize store activity.");
}
Activity::VerifyPaths(_activity_verify_paths) => {
// TODO
panic!("Attempted to get_progress_text of a verify paths activity.");
}
Activity::Substitute(_activity_substitute) => {
// TODO
panic!("Attempted to get_progress_text of a substitute activity.");
}
Activity::QueryPathInfo(_activity_query_path_info) => {
// TODO
panic!("Attempted to get_progress_text of a query path info activity.");
}
Activity::PostBuildHook(_activity_post_build_hook) => {
// TODO
panic!("Attempted to get_progress_text of a post build hook activity.");
}
Activity::BuildWaiting(_activity_build_waiting) => {
// TODO
panic!("Attempted to get_progress_text of a build waiting activity.");
}
Activity::FetchTree(_activity_fetch_tree) => {
// TODO
panic!("Attempted to get_progress_text of a fetch tree activity.");
}
}
}
pub(crate) fn set_phase(&mut self, phase: Option<String>) -> () {
match self {
Activity::Root(_activity_root) => {

View File

@@ -43,6 +43,7 @@ where
"--show-trace",
"--max-jobs",
"1",
"--repair",
"--log-format",
"internal-json",
"-vvvvvvvvvvv",

View File

@@ -583,23 +583,23 @@ pub(crate) enum NixAction {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct MsgMessage {
level: u8,
msg: String,
pub(crate) level: u8,
pub(crate) msg: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
raw_msg: Option<String>,
pub(crate) raw_msg: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
file: Option<String>,
pub(crate) file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
line: Option<u64>,
pub(crate) line: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
column: Option<u64>,
pub(crate) column: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
trace: Option<Vec<TraceEntry>>,
pub(crate) trace: Option<Vec<TraceEntry>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]

View File

@@ -1,3 +1,8 @@
use std::borrow::Cow;
use std::time::Duration;
use std::time::Instant;
use std::time::SystemTime;
use sqlx::Row;
use tokio::process::Child;
use tracing::error;
@@ -35,6 +40,7 @@ use super::nix_output_stream::NixMessage;
pub(crate) struct RunningBuild<'db> {
db_handle: &'db DbHandle,
activity_tree: ActivityTree,
last_announce: Option<Instant>,
}
impl<'db> RunningBuild<'db> {
@@ -42,6 +48,7 @@ impl<'db> RunningBuild<'db> {
Ok(RunningBuild {
db_handle,
activity_tree: ActivityTree::new(),
last_announce: None,
})
}
@@ -53,6 +60,8 @@ impl<'db> RunningBuild<'db> {
where
TN: AsRef<str>,
{
let foo = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
let now = Instant::now();
let build_id: i64 = sqlx::query(
r#"INSERT INTO build (start_time, target) SELECT unixepoch('now'), ? RETURNING id"#,
)
@@ -110,7 +119,12 @@ impl<'db> RunningBuild<'db> {
};
match message {
NixAction::Msg(msg_message) => {
// For now we can ignore the messages.
if msg_message.msg.contains("nix log") {
eprintln!("{}", msg_message.msg);
}
// if msg_message.level == 0 {
// eprintln!("{}", msg_message.msg);
// }
}
NixAction::Start(activity_start_message) => {
match activity_start_message {
@@ -269,7 +283,7 @@ impl<'db> RunningBuild<'db> {
)?;
}
};
// self.print_current_status();
self.print_current_status();
}
NixAction::Stop(stop_message) => {
let activity = self
@@ -277,7 +291,7 @@ impl<'db> RunningBuild<'db> {
.get_activity_id(stop_message.id)
.map(|activity_id| self.activity_tree.get_mut(&activity_id))?;
activity.get_mut_activity().stop();
// self.print_current_status();
self.print_current_status();
// println!("{}", serde_json::to_string(&message)?);
}
NixAction::Result(activity_result_message) => {
@@ -296,6 +310,8 @@ impl<'db> RunningBuild<'db> {
activity
.get_mut_activity()
.set_phase(Some(activity_result_set_phase.phase));
self.print_current_status();
}
ActivityResultMessage::Progress(activity_result_progress) => {
let activity_id = self
@@ -308,6 +324,8 @@ impl<'db> RunningBuild<'db> {
activity_result_progress.running,
activity_result_progress.failed,
);
self.maybe_print_current_status();
}
ActivityResultMessage::SetExpected(activity_result_set_expected) => {
let activity_id = self
@@ -317,6 +335,8 @@ impl<'db> RunningBuild<'db> {
activity
.get_mut_activity()
.set_expected(activity_result_set_expected.expected);
self.maybe_print_current_status();
}
ActivityResultMessage::PostBuildLogLine(
_activity_result_post_build_log_line,
@@ -328,7 +348,23 @@ impl<'db> RunningBuild<'db> {
Ok(())
}
fn print_current_status(&self) -> () {
fn maybe_print_current_status(&mut self) -> () {
let last_announce = match self.last_announce {
Some(instant) => instant,
None => {
// If we haven't announced before, always announce.
return self.print_current_status();
}
};
let now = Instant::now();
let time_since_last_announce = now.duration_since(last_announce);
if time_since_last_announce > Duration::new(5, 0) {
return self.print_current_status();
}
}
fn print_current_status(&mut self) -> () {
let mut tree = String::new();
let draw_order = self.get_draw_order();
for dag_entry in draw_order {
@@ -353,15 +389,19 @@ impl<'db> RunningBuild<'db> {
.get_activity()
.display_name()
.expect("Currently we always return a display name.");
let activity_id = activity.get_activity_id();
let parent_id = activity.get_parent_id();
tree += &format!("{leading_bars}{branch}𜸟 {display_name} {parent_id} {activity_id}\n");
let progress_text = activity.get_activity().get_progress_text();
let (progress, progress_sep) = match progress_text {
Some(text) => (text, " "),
None => (Cow::Borrowed(""), ""),
};
tree += &format!("{leading_bars}{branch}𜸟 {progress}{progress_sep}{display_name}\n");
}
if tree.is_empty() {
println!("No active activities.");
} else {
print!("\n{}\n", tree);
}
self.last_announce = Some(Instant::now());
}
fn get_draw_order(&self) -> Vec<DrawDagEntry> {