Add parsing and finding the failed derivation
Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
parent
a7986584d5
commit
85c6a8f169
9 changed files with 985 additions and 76 deletions
184
nix-json/src/helpers.rs
Normal file
184
nix-json/src/helpers.rs
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use displaydoc::Display;
|
||||
use itertools::Itertools;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::NixBuildLogLine;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum LogBuildState {
|
||||
NotStarted,
|
||||
Started,
|
||||
Failed,
|
||||
Succeeded,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LogBuildStatus {
|
||||
state: LogBuildState,
|
||||
store_path: String,
|
||||
log_lines: Vec<String>,
|
||||
}
|
||||
|
||||
impl LogBuildStatus {
|
||||
pub fn is_failed(&self) -> bool {
|
||||
self.state != LogBuildState::Succeeded
|
||||
}
|
||||
|
||||
pub fn log_lines(&self) -> &[String] {
|
||||
&self.log_lines
|
||||
}
|
||||
}
|
||||
|
||||
/// Accumulated build status of different nix-builds
|
||||
///
|
||||
/// This helper can ingest [`NixBuildLogLine`] and keep track of the state of whatever is
|
||||
/// being built. It then potentially emits note-events one can then use in their tool to respond to
|
||||
/// various statuses
|
||||
#[derive(Debug, Default)]
|
||||
pub struct NixBuildState {
|
||||
raw_log_lines: Vec<NixBuildLogLine>,
|
||||
builds: HashMap<u64, LogBuildStatus>,
|
||||
actions: HashMap<u64, Vec<NixBuildLogLine>>,
|
||||
build_succeeded: Option<bool>,
|
||||
next_stop_is_cause: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Display)]
|
||||
pub enum NixBuildStateError {
|
||||
/// An error occured while parsing a log line
|
||||
JsonParse(serde_json::Error),
|
||||
}
|
||||
|
||||
impl NixBuildState {
|
||||
pub fn from_log_lines(log_lines: impl AsRef<str>) -> Result<NixBuildState, NixBuildStateError> {
|
||||
let lines: Vec<NixBuildLogLine> = log_lines
|
||||
.as_ref()
|
||||
.trim()
|
||||
.lines()
|
||||
.map(|line| {
|
||||
serde_json::from_str(line.trim().trim_start_matches("@nix "))
|
||||
.map_err(NixBuildStateError::JsonParse)
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let mut state = NixBuildState::default();
|
||||
|
||||
lines.into_iter().for_each(|line| {
|
||||
state.handle_log_line(line);
|
||||
});
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub fn handle_log_line(&mut self, log_line: NixBuildLogLine) -> Option<NixBuildEvent> {
|
||||
self.raw_log_lines.push(log_line.clone());
|
||||
|
||||
if let Some(id) = log_line.id() {
|
||||
let entry = self.actions.entry(id).or_default();
|
||||
|
||||
entry.push(log_line.clone());
|
||||
}
|
||||
|
||||
match log_line {
|
||||
NixBuildLogLine::Start(nix_log_start_action) => {
|
||||
self.handle_start_action(nix_log_start_action)
|
||||
}
|
||||
NixBuildLogLine::Stop(nix_log_stop_action) => {
|
||||
self.handle_stop_action(nix_log_stop_action)
|
||||
}
|
||||
NixBuildLogLine::Result(nix_log_result_action) => {
|
||||
self.handle_result_action(nix_log_result_action)
|
||||
}
|
||||
NixBuildLogLine::Msg(nix_log_msg_action) => self.handle_log_action(nix_log_msg_action),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_failed(&self) -> bool {
|
||||
self.build_succeeded != Some(true)
|
||||
}
|
||||
|
||||
fn handle_start_action(
|
||||
&mut self,
|
||||
nix_log_start_action: crate::NixLogStartAction,
|
||||
) -> Option<NixBuildEvent> {
|
||||
match nix_log_start_action.kind {
|
||||
crate::ActivityKind::Build => {
|
||||
self.builds.insert(
|
||||
nix_log_start_action.id,
|
||||
LogBuildStatus {
|
||||
state: LogBuildState::Started,
|
||||
store_path: nix_log_start_action.fields[0].as_str().unwrap().to_string(),
|
||||
log_lines: vec![],
|
||||
},
|
||||
);
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_stop_action(
|
||||
&mut self,
|
||||
nix_log_stop_action: crate::NixLogStopAction,
|
||||
) -> Option<NixBuildEvent> {
|
||||
let log_build_status = self.builds.get_mut(&nix_log_stop_action.id)?;
|
||||
|
||||
if self.next_stop_is_cause {
|
||||
self.next_stop_is_cause = false;
|
||||
|
||||
log_build_status.state = LogBuildState::Failed;
|
||||
} else if log_build_status.state == LogBuildState::Started {
|
||||
log_build_status.state = LogBuildState::Succeeded;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn handle_result_action(
|
||||
&mut self,
|
||||
nix_log_result_action: crate::NixLogResultAction,
|
||||
) -> Option<NixBuildEvent> {
|
||||
match nix_log_result_action.kind {
|
||||
crate::ResultKind::FileLinked => {}
|
||||
crate::ResultKind::BuildLogLine => {}
|
||||
crate::ResultKind::UntrustedPath => {}
|
||||
crate::ResultKind::CorruptedPath => {}
|
||||
crate::ResultKind::SetPhase => {}
|
||||
crate::ResultKind::Progress => {
|
||||
let Some((_done, _expected, _running, failed)) = nix_log_result_action
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.as_u64().unwrap())
|
||||
.collect_tuple()
|
||||
else {
|
||||
panic!("Unexpected amount of fields for progress");
|
||||
};
|
||||
|
||||
if failed > 0 {
|
||||
self.build_succeeded = Some(false);
|
||||
self.next_stop_is_cause = true;
|
||||
}
|
||||
}
|
||||
crate::ResultKind::SetExpected => {}
|
||||
crate::ResultKind::PostBuildLogLine => {}
|
||||
crate::ResultKind::FetchStatus => {}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn handle_log_action(
|
||||
&self,
|
||||
nix_log_msg_action: crate::NixLogMsgAction,
|
||||
) -> Option<NixBuildEvent> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_derivation(&self, drv: &str) -> Option<&LogBuildStatus> {
|
||||
self.builds.values().find(|b| b.store_path == drv)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum NixBuildEvent {}
|
||||
|
|
@ -1,49 +1,106 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde_repr::Deserialize_repr;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub mod helpers;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(tag = "action", rename_all = "lowercase")]
|
||||
pub enum NixBuildLogLine {
|
||||
Start(NixLogStartAction),
|
||||
Stop(NixLogStartAction),
|
||||
Stop(NixLogStopAction),
|
||||
Result(NixLogResultAction),
|
||||
Msg(NixLogMsgAction),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
impl NixBuildLogLine {
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match self {
|
||||
NixBuildLogLine::Start(NixLogStartAction { id, .. })
|
||||
| NixBuildLogLine::Stop(NixLogStopAction { id })
|
||||
| NixBuildLogLine::Result(NixLogResultAction { id, .. }) => Some(*id),
|
||||
NixBuildLogLine::Msg(..) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// What kind of activity was logged
|
||||
///
|
||||
/// This definition mirrors the one in nixcpp. As it can evolve, the enum is marked as
|
||||
/// `non_exhaustive` and may get extended later on.
|
||||
#[derive(Debug, Clone, Copy, Deserialize_repr, PartialEq, Eq, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
pub enum ActivityKind {
|
||||
Unknown = 0,
|
||||
CopyPath = 100,
|
||||
FileTransfer = 101,
|
||||
Realise = 102,
|
||||
CopyPaths = 103,
|
||||
Builds = 104,
|
||||
Build = 105,
|
||||
OptimiseStore = 106,
|
||||
VerifyPaths = 107,
|
||||
Substitute = 108,
|
||||
QueryPathInfo = 109,
|
||||
PostBuildHook = 110,
|
||||
BuildWaiting = 111,
|
||||
FetchTree = 112,
|
||||
}
|
||||
|
||||
/// What kind of result information was logged
|
||||
///
|
||||
/// This definition mirrors the one in nixcpp. As it can evolve, the enum is marked as
|
||||
/// `non_exhaustive` and may get extended later on.
|
||||
#[derive(Debug, Clone, Copy, Deserialize_repr, PartialEq, Eq, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
pub enum ResultKind {
|
||||
FileLinked = 100,
|
||||
BuildLogLine = 101,
|
||||
UntrustedPath = 102,
|
||||
CorruptedPath = 103,
|
||||
SetPhase = 104,
|
||||
Progress = 105,
|
||||
SetExpected = 106,
|
||||
PostBuildLogLine = 107,
|
||||
FetchStatus = 108,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct NixLogResultAction {
|
||||
pub id: i64,
|
||||
pub id: u64,
|
||||
#[serde(rename = "type")]
|
||||
pub kind: i64,
|
||||
pub kind: ResultKind,
|
||||
#[serde(default)]
|
||||
pub fields: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct NixLogStopAction {
|
||||
pub id: i64,
|
||||
pub id: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct NixLogMsgAction {
|
||||
pub level: Option<i64>,
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct NixLogStartAction {
|
||||
pub id: i64,
|
||||
pub level: Option<i64>,
|
||||
pub parent: Option<i64>,
|
||||
pub id: u64,
|
||||
pub level: i64,
|
||||
pub parent: i64,
|
||||
pub text: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
pub kind: Option<i64>,
|
||||
pub kind: ActivityKind,
|
||||
#[serde(default)]
|
||||
pub fields: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct RawNixDerivationInfoOutput(HashMap<String, RawNixDerivationInfo>);
|
||||
|
||||
impl RawNixDerivationInfoOutput {
|
||||
|
|
@ -52,45 +109,15 @@ impl RawNixDerivationInfoOutput {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct RawNixDerivationInfo {
|
||||
args: Vec<String>,
|
||||
builder: String,
|
||||
pub args: Vec<String>,
|
||||
pub builder: String,
|
||||
#[serde(rename = "inputDrvs")]
|
||||
input_derivations: HashMap<String, serde_json::Value>,
|
||||
pub input_derivations: HashMap<String, serde_json::Value>,
|
||||
#[serde(rename = "inputSrcs")]
|
||||
input_sources: Vec<String>,
|
||||
name: String,
|
||||
outputs: HashMap<String, HashMap<String, String>>,
|
||||
system: String,
|
||||
}
|
||||
|
||||
impl RawNixDerivationInfo {
|
||||
pub fn args(&self) -> &[String] {
|
||||
&self.args
|
||||
}
|
||||
|
||||
pub fn builder(&self) -> &str {
|
||||
&self.builder
|
||||
}
|
||||
|
||||
pub fn input_derivations(&self) -> &HashMap<String, serde_json::Value> {
|
||||
&self.input_derivations
|
||||
}
|
||||
|
||||
pub fn input_sources(&self) -> &[String] {
|
||||
&self.input_sources
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn outputs(&self) -> &HashMap<String, HashMap<String, String>> {
|
||||
&self.outputs
|
||||
}
|
||||
|
||||
pub fn system(&self) -> &str {
|
||||
&self.system
|
||||
}
|
||||
pub input_sources: Vec<String>,
|
||||
pub name: String,
|
||||
pub outputs: HashMap<String, HashMap<String, String>>,
|
||||
pub system: String,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue