gh_workflow_parser/err_msg_parse/
yocto_err.rs

1use crate::{
2    commands::locate_failure_log::logfile_path_from_str, err_msg_parse::LOGFILE_MAX_LEN,
3    util::first_path_from_str,
4};
5use std::error::Error;
6
7use self::util::YoctoFailureKind;
8
9pub mod util;
10
11#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
12pub struct YoctoError {
13    summary: String,
14    kind: YoctoFailureKind,
15    logfile: Option<YoctoFailureLog>,
16}
17
18impl YoctoError {
19    pub fn new(summary: String, kind: YoctoFailureKind, logfile: Option<YoctoFailureLog>) -> Self {
20        YoctoError {
21            summary,
22            kind,
23            logfile,
24        }
25    }
26
27    pub fn summary(&self) -> &str {
28        &self.summary
29    }
30    pub fn kind(&self) -> YoctoFailureKind {
31        self.kind
32    }
33    pub fn logfile(&self) -> Option<&YoctoFailureLog> {
34        self.logfile.as_ref()
35    }
36}
37
38#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
39pub struct YoctoFailureLog {
40    pub name: String,
41    pub contents: String,
42}
43
44/// Parse a log from a Yocto build and return a [YoctoError] containing error
45/// summary, error kind, and logfile contents if it exists and is not too large.
46pub fn parse_yocto_error(log: &str) -> Result<YoctoError, Box<dyn Error>> {
47    let error_summary = util::yocto_error_summary(log)?;
48    log::debug!(
49        "Yocto error before trimming just recipe failures: \n{}",
50        error_summary
51    );
52
53    let error_summary = util::trim_trailing_just_recipes(&error_summary)?;
54    log::info!("Yocto error: \n{}", error_summary);
55
56    // Find the kind of yocto failure in the string e.g. this would be `do_fetch`
57    // ERROR: Logfile of failure stored in: /app/yocto/build/tmp/work/x86_64-linux/sqlite3-native/3.43.2/temp/log.do_fetch.21616
58
59    // Find the line with the `Logfile of failure stored in` and get the path
60    let log_file_line = util::find_yocto_failure_log_str(&error_summary)?;
61    let path = first_path_from_str(log_file_line)?;
62    let fname = path.file_stem().unwrap().to_str().unwrap();
63    let yocto_failure_kind = match YoctoFailureKind::parse_from_logfilename(fname) {
64        Ok(kind) => kind,
65        Err(e) => {
66            log::error!("{e}");
67            log::warn!("Could not determine yocto failure kind, continuing with default kind");
68            YoctoFailureKind::default()
69        },
70    };
71
72    let failure_log: Option<YoctoFailureLog> = match logfile_path_from_str(path.to_str().unwrap()) {
73        Ok(p) => {
74            let contents = std::fs::read_to_string(p)?;
75            if contents.len() > LOGFILE_MAX_LEN {
76                log::warn!("Logfile of yocto failure exceeds maximum length of {LOGFILE_MAX_LEN}. It will not be added to the issue body.");
77                None
78            } else {
79                Some(YoctoFailureLog {
80                    name: fname.to_owned(),
81                    contents,
82                })
83            }
84        },
85        Err(e) => {
86            log::trace!("{e}");
87            log::error!("Logfile from error summary does not exist at: {path:?}");
88            log::warn!("Continuing without attempting to attach logfile to issue");
89            None
90        },
91    };
92
93    let yocto_error = YoctoError {
94        summary: error_summary,
95        kind: yocto_failure_kind,
96        logfile: failure_log,
97    };
98
99    Ok(yocto_error)
100}