gh_workflow_parser/commands/
locate_failure_log.rs1use crate::util::first_path_from_str;
2use std::{io, path::PathBuf};
3
4use super::BuildKind;
5
6pub fn locate_failure_log(
17 kind: BuildKind,
18 log_file: Option<&PathBuf>,
19) -> Result<(), Box<dyn std::error::Error>> {
20 let logfile_content: String = match log_file {
21 Some(file) => {
22 log::info!("Reading log file: {file:?}");
23 if !file.exists() {
24 return Err(format!("File: {file:?} does not exist",).into());
25 }
26 std::fs::read_to_string(file)?
27 },
28 None => {
29 log::info!("Reading log from stdin");
30 let stdin = io::stdin();
31 let mut handle = stdin.lock();
32 let mut buf = String::new();
33 io::Read::read_to_string(&mut handle, &mut buf)?;
34 buf
35 },
36 };
37
38 match kind {
39 BuildKind::Yocto => locate_yocto_failure_log(&logfile_content)?,
40 BuildKind::Other => todo!("This feature is not implemented yet!"),
41 }
42
43 Ok(())
44}
45
46pub fn locate_yocto_failure_log(logfile_content: &str) -> Result<(), Box<dyn std::error::Error>> {
68 use crate::err_msg_parse::yocto_err::util;
69 use std::io::Write;
70
71 log::trace!("Finding failure log in log file contents: {logfile_content}");
72 let error_summary = util::yocto_error_summary(logfile_content)?;
73 let error_summary = util::trim_trailing_just_recipes(&error_summary)?;
74 log::trace!("Trimmed error summary: {error_summary}");
75 let log_file_line = util::find_yocto_failure_log_str(&error_summary)?;
76 let path = logfile_path_from_str(log_file_line)?;
77 crate::macros::pipe_print!("{}", path.to_string_lossy())?;
79
80 Ok(())
81}
82
83pub fn logfile_path_from_str(s: &str) -> Result<PathBuf, Box<dyn std::error::Error>> {
97 let path = first_path_from_str(s)?;
98 log::debug!("Searching for logfile from path: {path:?}");
99 if path.exists() {
100 return canonicalize_if_file(path);
101 }
102
103 let mut parts = path.components().collect::<Vec<_>>();
104 log::debug!("File not found, looking for file using parts: {parts:?}");
105 for _ in 0..parts.len() {
106 parts.remove(0);
107 let tmp_path = parts.iter().collect::<PathBuf>();
108 log::debug!("Looking for file at path: {tmp_path:?}");
109 if tmp_path.exists() {
110 return canonicalize_if_file(tmp_path);
111 }
112 let tmp_path_from_root = PathBuf::from("/").join(tmp_path);
114 log::debug!("Looking for file at path: {tmp_path_from_root:?}");
115 if tmp_path_from_root.exists() {
116 return canonicalize_if_file(tmp_path_from_root);
117 }
118 }
119
120 Err(format!("No file found at path: {s}").into())
121}
122
123fn canonicalize_if_file(path: PathBuf) -> Result<PathBuf, Box<dyn std::error::Error>> {
127 if path.is_file() {
128 return Ok(path.canonicalize()?);
129 }
130 Err(format!("No file found at path: {path:?}").into())
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use temp_dir::TempDir;
137
138 #[test]
139 fn test_logfile_path_from_str_simple() {
140 let dir = TempDir::new().unwrap();
142 let dir_file = dir.child("test.log");
143 let tmp_log_file = dir_file.as_path();
144 let test_log_str = format!(
146 "ERROR: Logfile of failure stored in: /app{real_location}",
147 real_location = tmp_log_file.to_string_lossy()
148 );
149 std::fs::write(tmp_log_file, &test_log_str).unwrap();
150
151 let path = logfile_path_from_str(&test_log_str).unwrap();
153
154 assert_eq!(path, tmp_log_file);
156 }
157
158 #[test]
159 fn test_logfile_path_from_str() {
160 let dir = TempDir::new().unwrap();
161 let real_path_str =
162 r#"yocto/build/tmp/work/x86_64-linux/sqlite3-native/3.43.2/temp/log.do_fetch.21616"#;
163 let path_to_log = dir.path().join(real_path_str);
165 std::fs::create_dir_all(path_to_log.parent().unwrap()).unwrap();
167 let test_log_str = format!(
169 r"other contents
170ERROR: Logfile of failure stored in: /app{real_location} other contents
171other contents",
172 real_location = &path_to_log.to_string_lossy()
173 );
174 std::fs::write(&path_to_log, &test_log_str).unwrap();
176
177 let path = logfile_path_from_str(&test_log_str).unwrap();
179 assert_eq!(path, path_to_log);
181 }
182}