Reporting Enso test failures as annotations on the PR (#10821)

This commit is contained in:
Radosław Waśko 2024-10-02 18:01:28 +02:00 committed by GitHub
parent 5f1d32f1aa
commit 0302670092
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 55 additions and 16 deletions

View File

@ -201,6 +201,7 @@ impl RunContext {
} }
prepare_simple_library_server.await??; prepare_simple_library_server.await??;
Ok(()) Ok(())
} }

View File

@ -266,7 +266,14 @@ impl BuiltEnso {
if errors.is_empty() { if errors.is_empty() {
Ok(()) Ok(())
} else { } else {
error!("{} test suit(s) failed.", errors.len()); let summary =
errors.as_slice().iter().map(|e| e.to_string()).collect::<Vec<_>>().join(", ");
println!(
"::error title=Failed Standard Library Tests::{} test suite(s) failed: {}",
errors.len(),
summary
);
error!("{} test suite(s) failed.", errors.len());
for error in &errors { for error in &errors {
error!("{}", error); error!("{}", error);
} }

View File

@ -469,19 +469,10 @@ pub fn spawn_log_processor(
match String::from_utf8(line_bytes) { match String::from_utf8(line_bytes) {
Ok(line) => { Ok(line) => {
let line = line.trim_end_matches('\r'); let line = line.trim_end_matches('\r');
let mut command = false; if let Some(special_command) = extract_github_command(line) {
if let Some(command_at) = line.find("::") { // intentionally using println to avoid info!'s prefix
if let Some(group_at) = line.find("group") { println!("{special_command}");
// we support: `::group` and `::endgroup` right now } else {
if command_at < group_at && group_at < command_at + 10 {
let line_without_prefix = &line[command_at..];
// intentionally using println to avoid info!'s prefix
println!("{line_without_prefix}");
command = true;
}
}
}
if !command {
info!("{prefix} {line}"); info!("{prefix} {line}");
} }
} }
@ -502,6 +493,29 @@ pub fn spawn_log_processor(
) )
} }
/// Checks if the line contains a GitHub command and extracts it from the line if it does.
/// Currently only error and group commands are supported. All commands are documented at https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions
fn extract_github_command(line: &str) -> Option<String> {
// We remove a possible [info] prefix that is added by sbt.
// We need to be careful as the [info] text can contain ANSI escape codes, so simple text
// matching for it won't work. Instead we locate `::`. If the line starts with `::` and the
// following text is `error`, `group`, or `endgroup`, we return the line as a special command.
let command_prefix = "::";
if let Some(start_of_command) = line.find(command_prefix) {
let command_part = &line[start_of_command + command_prefix.len()..];
if command_part.starts_with("error")
|| command_part.starts_with("group")
|| command_part.starts_with("endgroup")
{
// We now remove the stripped [info] prefix and return the rest of the line.
let trimmed = &line[start_of_command..];
return Some(trimmed.to_string());
}
}
None
}
pub trait Manipulator { pub trait Manipulator {
fn apply<C: IsCommandWrapper + ?Sized>(&self, command: &mut C); fn apply<C: IsCommandWrapper + ?Sized>(&self, command: &mut C);
} }

View File

@ -823,9 +823,9 @@ type File
list_immediate_children self = Vector.from_polyglot_array (self.list_immediate_children_array) list_immediate_children self = Vector.from_polyglot_array (self.list_immediate_children_array)
## PRIVATE ## PRIVATE
Return the absolute path of this File Return the path that this file represents.
to_text : Text to_text : Text
to_text self = self.absolute . path to_text self = self.path
## PRIVATE ## PRIVATE
Convert to a display representation of this File. Convert to a display representation of this File.

View File

@ -76,6 +76,7 @@ print_single_result (test_result : Test_Result) (config : Suite_Config) =
txt = " - " + test_result.spec_name + " " + times_suffix txt = " - " + test_result.spec_name + " " + times_suffix
IO.println (maybe_green_text txt config) IO.println (maybe_green_text txt config)
Spec_Result.Failure msg details -> Spec_Result.Failure msg details ->
report_github_error_message test_result.spec_name msg
IO.println "" IO.println ""
txt = " - [FAILED] " + test_result.spec_name + " " + times_suffix txt = " - [FAILED] " + test_result.spec_name + " " + times_suffix
IO.println (maybe_red_text txt config) IO.println (maybe_red_text txt config)
@ -87,6 +88,22 @@ print_single_result (test_result : Test_Result) (config : Suite_Config) =
IO.println (maybe_grey_text (" - [PENDING] " + test_result.spec_name) config) IO.println (maybe_grey_text (" - [PENDING] " + test_result.spec_name) config)
IO.println (" Reason: " + reason) IO.println (" Reason: " + reason)
## PRIVATE
Reports an error message to show up as a note in GitHub Actions,
only if running in the GitHub Actions environment.
report_github_error_message (title : Text) (message : Text) =
is_enabled = Environment.get "GITHUB_ACTIONS" == "true"
sanitize_message txt = txt.replace '\n' '%0A'
sanitize_parameter txt = txt . replace '\n' '%0A' . replace ',' '%2C'
if is_enabled then
location_match = Regex.compile "at ((?:[A-Za-z]:)?[^:]+):([0-9]+):" . match message
location_part = if location_match.is_nothing then "" else
repo_root = enso_project.root.parent.parent.absolute.normalize
path = File.new (location_match.get 1)
relative_path = repo_root.relativize path
line = location_match.get 2
",file="+(sanitize_parameter relative_path.path)+",line="+(sanitize_parameter line)+""
IO.println "::error title="+(sanitize_parameter title)+location_part+"::"+(sanitize_message message)
## Prints all the results, optionally writing them to a jUnit XML output. ## Prints all the results, optionally writing them to a jUnit XML output.