treewide: remove dead code; track more activity types
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I8ef141010291a30f28d1d3e8bb9567046a6a6964
This commit is contained in:
parent
503fcbf4e2
commit
287dec65c3
4 changed files with 205 additions and 205 deletions
|
|
@ -35,19 +35,6 @@ pub enum Verbosity {
|
|||
Vomit = 7,
|
||||
}
|
||||
|
||||
/// Activity progress tracking for downloads/uploads/builds
|
||||
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ActivityProgress {
|
||||
/// Bytes completed
|
||||
pub done: u64,
|
||||
/// Total bytes expected
|
||||
pub expected: u64,
|
||||
/// Currently running transfers
|
||||
pub running: u64,
|
||||
/// Failed transfers
|
||||
pub failed: u64,
|
||||
}
|
||||
|
||||
pub type Id = u64;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
|
|
|
|||
|
|
@ -382,9 +382,9 @@ impl<W: Write> Display<W> {
|
|||
|| downloading > 0
|
||||
|| uploading > 0
|
||||
{
|
||||
lines.push(self.colored(&"═".repeat(60), Color::Blue).to_string());
|
||||
lines.push(self.colored(&"═".repeat(60), Color::Blue).clone());
|
||||
lines.push(format!("{} Build Summary", self.colored("┃", Color::Blue)));
|
||||
lines.push(self.colored(&"─".repeat(60), Color::Blue).to_string());
|
||||
lines.push(self.colored(&"─".repeat(60), Color::Blue).clone());
|
||||
|
||||
// Builds section
|
||||
if running + completed + failed > 0 {
|
||||
|
|
@ -431,7 +431,7 @@ impl<W: Write> Display<W> {
|
|||
self.format_duration(duration)
|
||||
));
|
||||
|
||||
lines.push(self.colored(&"═".repeat(60), Color::Blue).to_string());
|
||||
lines.push(self.colored(&"═".repeat(60), Color::Blue).clone());
|
||||
}
|
||||
|
||||
lines
|
||||
|
|
|
|||
|
|
@ -223,8 +223,7 @@ impl<W: Write> Monitor<W> {
|
|||
.full_summary
|
||||
.running_downloads
|
||||
.get(&path_id)
|
||||
.map(|t| t.start)
|
||||
.unwrap_or(now);
|
||||
.map_or(now, |t| t.start);
|
||||
|
||||
let completed = crate::state::CompletedTransferInfo {
|
||||
start,
|
||||
|
|
@ -405,30 +404,6 @@ fn extract_path_from_message(line: &str) -> Option<String> {
|
|||
None
|
||||
}
|
||||
|
||||
/// Parse byte size from human-readable format (e.g., "123 KiB", "4.5 MiB")
|
||||
/// Supports: B, KiB, MiB, GiB, TiB, PiB
|
||||
fn parse_byte_size(text: &str) -> Option<u64> {
|
||||
let parts: Vec<&str> = text.split_whitespace().collect();
|
||||
if parts.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let value: f64 = parts[0].parse().ok()?;
|
||||
let unit = parts[1];
|
||||
|
||||
let multiplier = match unit {
|
||||
"B" => 1_u64,
|
||||
"KiB" => 1024,
|
||||
"MiB" => 1024 * 1024,
|
||||
"GiB" => 1024 * 1024 * 1024,
|
||||
"TiB" => 1024_u64 * 1024 * 1024 * 1024,
|
||||
"PiB" => 1024_u64 * 1024 * 1024 * 1024 * 1024,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some((value * multiplier as f64) as u64)
|
||||
}
|
||||
|
||||
/// Extract byte size from a message line (e.g., "downloaded 123 KiB")
|
||||
fn extract_byte_size(line: &str) -> Option<u64> {
|
||||
// Look for patterns like "123 KiB", "6.7 MiB", etc.
|
||||
|
|
@ -483,19 +458,6 @@ mod tests {
|
|||
assert!(path.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_byte_size() {
|
||||
assert_eq!(parse_byte_size("123 B"), Some(123));
|
||||
assert_eq!(parse_byte_size("1 KiB"), Some(1024));
|
||||
assert_eq!(parse_byte_size("1 MiB"), Some(1024 * 1024));
|
||||
assert_eq!(parse_byte_size("1 GiB"), Some(1024 * 1024 * 1024));
|
||||
assert_eq!(
|
||||
parse_byte_size("2.5 MiB"),
|
||||
Some((2.5 * 1024.0 * 1024.0) as u64)
|
||||
);
|
||||
assert_eq!(parse_byte_size("invalid"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_byte_size() {
|
||||
let line = "downloaded 123 KiB in 2 seconds";
|
||||
|
|
|
|||
|
|
@ -91,18 +91,25 @@ fn handle_start(
|
|||
});
|
||||
|
||||
let changed = match activity_u8 {
|
||||
104 | 105 => handle_build_start(state, id, parent_id, &text, &fields, now), /* Builds | Build */
|
||||
105 => handle_build_start(state, id, parent_id, &text, &fields, now), /* Build */
|
||||
108 => handle_substitute_start(state, id, &text, &fields, now), /* Substitute */
|
||||
101 => handle_transfer_start(state, id, &text, &fields, now, false), /* FileTransfer */
|
||||
100 | 103 => handle_transfer_start(state, id, &text, &fields, now, true), /* CopyPath | CopyPaths */
|
||||
_ => false,
|
||||
109 => handle_query_path_info_start(state, id, &text, &fields, now), /* QueryPathInfo */
|
||||
110 => handle_post_build_hook_start(state, id, &text, &fields, now), /* PostBuildHook */
|
||||
101 => handle_file_transfer_start(state, id, &text, &fields, now), /* FileTransfer */
|
||||
100 => handle_copy_path_start(state, id, &text, &fields, now), // CopyPath
|
||||
102 | 103 | 104 | 106 | 107 | 111 | 112 => {
|
||||
// Realise, CopyPaths, Builds, OptimiseStore, VerifyPaths, BuildWaiting,
|
||||
// FetchTree These activities have no fields and are just tracked
|
||||
true
|
||||
},
|
||||
_ => {
|
||||
debug!("Unknown activity type: {}", activity_u8);
|
||||
false
|
||||
},
|
||||
};
|
||||
|
||||
// Track parent-child relationships for dependency tree
|
||||
if changed
|
||||
&& (activity_u8 == 104 || activity_u8 == 105)
|
||||
&& parent_id.is_some()
|
||||
{
|
||||
if changed && activity_u8 == 105 && parent_id.is_some() {
|
||||
let parent_act_id = parent_id.unwrap();
|
||||
|
||||
// Find parent and child derivation IDs
|
||||
|
|
@ -112,8 +119,8 @@ fn handle_start(
|
|||
if let Some(parent_drv_id) = parent_drv_id {
|
||||
if let Some(child_drv_id) = child_drv_id {
|
||||
debug!(
|
||||
"Establishing parent-child relationship: parent={}, child={}",
|
||||
parent_drv_id, child_drv_id
|
||||
"Establishing parent-child relationship: parent={parent_drv_id}, \
|
||||
child={child_drv_id}"
|
||||
);
|
||||
|
||||
// Add child as a dependency of parent
|
||||
|
|
@ -152,9 +159,19 @@ fn handle_stop(state: &mut State, id: Id, now: f64) -> bool {
|
|||
state.activities.remove(&id);
|
||||
|
||||
match activity_status.activity {
|
||||
104 | 105 => handle_build_stop(state, id, now), // Builds | Build
|
||||
108 => handle_substitute_stop(state, id, now), // Substitute
|
||||
101 | 100 | 103 => handle_transfer_stop(state, id, now), /* FileTransfer, CopyPath, CopyPaths */
|
||||
105 => handle_build_stop(state, id, now), // Build
|
||||
108 => handle_substitute_stop(state, id, now), // Substitute
|
||||
101 | 100 => handle_transfer_stop(state, id, now), // FileTransfer,
|
||||
// CopyPath
|
||||
109 | 110 => {
|
||||
// QueryPathInfo, PostBuildHook - just acknowledge stop
|
||||
false
|
||||
},
|
||||
102 | 103 | 104 | 106 | 107 | 111 | 112 => {
|
||||
// Realise, CopyPaths, Builds, OptimiseStore, VerifyPaths, BuildWaiting,
|
||||
// FetchTree
|
||||
false
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
|
|
@ -168,7 +185,7 @@ fn handle_message(state: &mut State, level: Verbosity, msg: String) -> bool {
|
|||
|
||||
// Extract phase from log messages like "Running phase: configurePhase"
|
||||
if let Some(phase_start) = msg.find("Running phase: ") {
|
||||
let phase_name = &msg[phase_start + 15..]; // Skip "Running phase: "
|
||||
let phase_name = &msg[phase_start + 15..]; // skip "Running phase: "
|
||||
let phase = phase_name.trim().to_string();
|
||||
|
||||
// Find the active build and update its phase
|
||||
|
|
@ -247,38 +264,70 @@ fn handle_message(state: &mut State, level: Verbosity, msg: String) -> bool {
|
|||
fn handle_result(
|
||||
state: &mut State,
|
||||
id: Id,
|
||||
activity: u8,
|
||||
result_type: u8,
|
||||
fields: Vec<serde_json::Value>,
|
||||
_now: f64,
|
||||
) -> bool {
|
||||
match activity {
|
||||
101 | 108 => {
|
||||
// FileTransfer or Substitute
|
||||
// Fields contain progress information
|
||||
// Format: [bytes_transferred, total_bytes]
|
||||
// Result message types are DIFFERENT from Activity types
|
||||
// Type 100: FileLinked (2 ints)
|
||||
// Type 101: BuildLogLine (1 text)
|
||||
// Type 102: UntrustedPath (1 text - store path)
|
||||
// Type 103: CorruptedPath (1 text - store path)
|
||||
// Type 104: SetPhase (1 text)
|
||||
// Type 105: Progress (4 ints: done, expected, running, failed)
|
||||
// Type 106: SetExpected (2 ints: activity type, count)
|
||||
// Type 107: PostBuildLogLine (1 text)
|
||||
// Type 108: FetchStatus (1 text)
|
||||
|
||||
match result_type {
|
||||
100 => {
|
||||
// FileLinked: 2 int fields
|
||||
if fields.len() >= 2 {
|
||||
update_transfer_progress(state, id, &fields);
|
||||
let _linked = fields[0].as_u64();
|
||||
let _total = fields[1].as_u64();
|
||||
// TODO: Track file linking progress
|
||||
}
|
||||
false
|
||||
},
|
||||
101 => {
|
||||
// BuildLogLine: 1 text field
|
||||
if let Some(line) = fields.first().and_then(|f| f.as_str()) {
|
||||
state.build_logs.push(line.to_string());
|
||||
return true;
|
||||
}
|
||||
false
|
||||
},
|
||||
102 => {
|
||||
// UntrustedPath: 1 text field (store path)
|
||||
if let Some(path_str) = fields.first().and_then(|f| f.as_str()) {
|
||||
debug!("Untrusted path: {}", path_str);
|
||||
// TODO: Track untrusted paths
|
||||
}
|
||||
false
|
||||
},
|
||||
103 => {
|
||||
// CorruptedPath: 1 text field (store path)
|
||||
if let Some(path_str) = fields.first().and_then(|f| f.as_str()) {
|
||||
state
|
||||
.nix_errors
|
||||
.push(format!("Corrupted path: {path_str}"));
|
||||
return true;
|
||||
}
|
||||
false
|
||||
},
|
||||
104 => {
|
||||
// Builds activity type - contains phase information or progress
|
||||
if !fields.is_empty() {
|
||||
if let Some(phase_str) = fields[0].as_str() {
|
||||
// Update the activity's phase field
|
||||
if let Some(activity) = state.activities.get_mut(&id) {
|
||||
activity.phase = Some(phase_str.to_string());
|
||||
return true;
|
||||
}
|
||||
// SetPhase: 1 text field
|
||||
if let Some(phase_str) = fields.first().and_then(|f| f.as_str()) {
|
||||
if let Some(activity) = state.activities.get_mut(&id) {
|
||||
activity.phase = Some(phase_str.to_string());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
},
|
||||
105 => {
|
||||
// Progress update (done, expected, running, failed)
|
||||
// OR Build completed (fields contain output path as string)
|
||||
// Progress: 4 int fields (done, expected, running, failed)
|
||||
if fields.len() >= 4 {
|
||||
// This is a progress update: [done, expected, running, failed]
|
||||
if let (Some(done), Some(expected), Some(running), Some(failed)) = (
|
||||
fields[0].as_u64(),
|
||||
fields[1].as_u64(),
|
||||
|
|
@ -294,19 +343,39 @@ fn handle_result(
|
|||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if !fields.is_empty() && fields[0].is_string() {
|
||||
// This is a build completion with output path
|
||||
complete_build(state, id)
|
||||
} else {
|
||||
// Legacy: just mark build as complete
|
||||
complete_build(state, id)
|
||||
}
|
||||
false
|
||||
},
|
||||
106 => {
|
||||
// SetExpected: 2 int fields (activity type, count)
|
||||
if fields.len() >= 2 {
|
||||
let _activity_type = fields[0].as_u64();
|
||||
let _expected_count = fields[1].as_u64();
|
||||
// TODO: Track expected counts
|
||||
}
|
||||
false
|
||||
},
|
||||
107 => {
|
||||
// PostBuildLogLine: 1 text field
|
||||
if let Some(line) = fields.first().and_then(|f| f.as_str()) {
|
||||
state.build_logs.push(format!("[post-build] {line}"));
|
||||
return true;
|
||||
}
|
||||
false
|
||||
},
|
||||
108 => {
|
||||
// FetchStatus: 1 text field
|
||||
if let Some(status) = fields.first().and_then(|f| f.as_str()) {
|
||||
debug!("Fetch status: {}", status);
|
||||
// TODO: Track fetch status
|
||||
}
|
||||
false
|
||||
},
|
||||
_ => {
|
||||
debug!("Unknown result type: {}", result_type);
|
||||
false
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -358,55 +427,19 @@ fn handle_build_start(
|
|||
);
|
||||
|
||||
// Mark as forest root if no parent
|
||||
// Only add to forest roots if no parent
|
||||
if parent_id.is_none() && !state.forest_roots.contains(&drv_id) {
|
||||
state.forest_roots.push(drv_id);
|
||||
}
|
||||
|
||||
// Store activity -> derivation mapping
|
||||
// Phase will be extracted from log messages
|
||||
return true;
|
||||
}
|
||||
debug!("Failed to parse derivation from path: {}", drv_path);
|
||||
} else {
|
||||
debug!(
|
||||
"No derivation path found - creating placeholder for activity {}",
|
||||
"No derivation path in fields for Build activity {} - this should not \
|
||||
happen",
|
||||
id
|
||||
);
|
||||
// For shell/develop commands, nix doesn't report specific derivation paths
|
||||
// Create a placeholder derivation to track that builds are happening
|
||||
use std::path::PathBuf;
|
||||
|
||||
let placeholder_name = format!("building-{id}");
|
||||
let placeholder_path = format!("/nix/store/placeholder-{id}.drv");
|
||||
|
||||
let placeholder_drv = Derivation {
|
||||
path: PathBuf::from(placeholder_path),
|
||||
name: placeholder_name,
|
||||
};
|
||||
|
||||
let drv_id = state.get_or_create_derivation_id(placeholder_drv);
|
||||
let host = extract_host(text);
|
||||
|
||||
let build_info = BuildInfo {
|
||||
start: now,
|
||||
host,
|
||||
estimate: None,
|
||||
activity_id: Some(id),
|
||||
};
|
||||
|
||||
debug!(
|
||||
"Setting placeholder derivation {} to Building status",
|
||||
drv_id
|
||||
);
|
||||
state.update_build_status(drv_id, BuildStatus::Building(build_info));
|
||||
|
||||
// Mark as forest root if no parent
|
||||
if parent_id.is_none() && !state.forest_roots.contains(&drv_id) {
|
||||
state.forest_roots.push(drv_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
@ -512,45 +545,111 @@ fn handle_substitute_stop(state: &mut State, id: Id, now: f64) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
fn handle_transfer_start(
|
||||
fn handle_file_transfer_start(
|
||||
_state: &mut State,
|
||||
id: Id,
|
||||
_text: &str,
|
||||
fields: &[serde_json::Value],
|
||||
_now: f64,
|
||||
) -> bool {
|
||||
// FileTransfer expects 1 text field: URL or description
|
||||
if fields.is_empty() {
|
||||
debug!("FileTransfer activity {} has no fields", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Just track the activity, actual progress comes via Result messages
|
||||
true
|
||||
}
|
||||
|
||||
fn handle_copy_path_start(
|
||||
state: &mut State,
|
||||
id: Id,
|
||||
text: &str,
|
||||
_text: &str,
|
||||
fields: &[serde_json::Value],
|
||||
now: f64,
|
||||
is_copy: bool,
|
||||
) -> bool {
|
||||
let path_str = if fields.is_empty() {
|
||||
extract_store_path(text)
|
||||
} else {
|
||||
fields[0].as_str().map(std::string::ToString::to_string)
|
||||
};
|
||||
// CopyPath expects 3 text fields: path, from, to
|
||||
if fields.len() < 3 {
|
||||
debug!("CopyPath activity {} has insufficient fields", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(path_str) = path_str {
|
||||
if let Some(path) = StorePath::parse(&path_str) {
|
||||
let path_str = fields[0].as_str();
|
||||
let _from_host = fields[1].as_str().map(|s| {
|
||||
if s.is_empty() || s == "localhost" {
|
||||
Host::Localhost
|
||||
} else {
|
||||
Host::Remote(s.to_string())
|
||||
}
|
||||
});
|
||||
let to_host = fields[2].as_str().map(|s| {
|
||||
if s.is_empty() || s == "localhost" {
|
||||
Host::Localhost
|
||||
} else {
|
||||
Host::Remote(s.to_string())
|
||||
}
|
||||
});
|
||||
|
||||
if let (Some(path_str), Some(to)) = (path_str, to_host) {
|
||||
if let Some(path) = StorePath::parse(path_str) {
|
||||
let path_id = state.get_or_create_store_path_id(path);
|
||||
let host = extract_host(text);
|
||||
|
||||
let transfer = TransferInfo {
|
||||
start: now,
|
||||
host,
|
||||
activity_id: id,
|
||||
start: now,
|
||||
host: to, // destination host
|
||||
activity_id: id,
|
||||
bytes_transferred: 0,
|
||||
total_bytes: None,
|
||||
total_bytes: None,
|
||||
};
|
||||
|
||||
if is_copy {
|
||||
state.full_summary.running_uploads.insert(path_id, transfer);
|
||||
} else {
|
||||
state
|
||||
.full_summary
|
||||
.running_downloads
|
||||
.insert(path_id, transfer);
|
||||
}
|
||||
|
||||
// CopyPath is an upload from 'from' to 'to'
|
||||
state.full_summary.running_uploads.insert(path_id, transfer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn handle_query_path_info_start(
|
||||
_state: &mut State,
|
||||
id: Id,
|
||||
_text: &str,
|
||||
fields: &[serde_json::Value],
|
||||
_now: f64,
|
||||
) -> bool {
|
||||
// QueryPathInfo expects 2 text fields: path, host
|
||||
if fields.len() < 2 {
|
||||
debug!("QueryPathInfo activity {} has insufficient fields", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Just track the activity
|
||||
true
|
||||
}
|
||||
|
||||
fn handle_post_build_hook_start(
|
||||
_state: &mut State,
|
||||
id: Id,
|
||||
_text: &str,
|
||||
fields: &[serde_json::Value],
|
||||
_now: f64,
|
||||
) -> bool {
|
||||
// PostBuildHook expects 1 text field: derivation path
|
||||
if fields.is_empty() {
|
||||
debug!("PostBuildHook activity {} has no fields", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
let drv_path = fields[0].as_str();
|
||||
if let Some(drv_path) = drv_path {
|
||||
if let Some(_drv) = Derivation::parse(drv_path) {
|
||||
// Just track that the hook is running
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
|
|
@ -599,54 +698,6 @@ fn handle_transfer_stop(state: &mut State, id: Id, now: f64) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
fn update_transfer_progress(
|
||||
state: &mut State,
|
||||
id: Id,
|
||||
fields: &[serde_json::Value],
|
||||
) {
|
||||
if fields.len() < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
let bytes_transferred = fields[0].as_u64().unwrap_or(0);
|
||||
let total_bytes = fields[1].as_u64();
|
||||
|
||||
// Update running downloads
|
||||
for transfer_info in state.full_summary.running_downloads.values_mut() {
|
||||
if transfer_info.activity_id == id {
|
||||
transfer_info.bytes_transferred = bytes_transferred;
|
||||
transfer_info.total_bytes = total_bytes;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update running uploads
|
||||
for transfer_info in state.full_summary.running_uploads.values_mut() {
|
||||
if transfer_info.activity_id == id {
|
||||
transfer_info.bytes_transferred = bytes_transferred;
|
||||
transfer_info.total_bytes = total_bytes;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn complete_build(state: &mut State, id: Id) -> bool {
|
||||
// Find the derivation that just completed
|
||||
for (drv_id, info) in &state.derivation_infos.clone() {
|
||||
if let BuildStatus::Building(build_info) = &info.build_status {
|
||||
if build_info.activity_id == Some(id) {
|
||||
let end = current_time();
|
||||
state.update_build_status(*drv_id, BuildStatus::Built {
|
||||
info: build_info.clone(),
|
||||
end,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn extract_derivation_path(text: &str) -> Option<String> {
|
||||
// Look for .drv paths in the text
|
||||
if let Some(start) = text.find("/nix/store/") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue