ci_manager/err_parse/
yocto.rs

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