Skip to content

Commit 0d1870a

Browse files
fargitoadriencaccia
authored andcommitted
fix(github): do not espace trailing newlines in logger
1 parent c6a4fb9 commit 0d1870a

File tree

1 file changed

+67
-6
lines changed
  • src/run/run_environment/github_actions

1 file changed

+67
-6
lines changed

src/run/run_environment/github_actions/logger.rs

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,7 @@ impl Log for GithubActionLogger {
5656
}
5757

5858
if let Some(announcement) = get_announcement_event(record) {
59-
// properly escape newlines so that GitHub Actions interprets them correctly
60-
// https://github.com/actions/toolkit/issues/193#issuecomment-605394935
61-
let escaped_announcement = announcement.replace('\n', "%0A");
59+
let escaped_announcement = escape_multiline_message(&announcement);
6260
// TODO: make the announcement title configurable
6361
println!("::notice title=New CodSpeed Feature::{escaped_announcement}");
6462
return;
@@ -79,9 +77,7 @@ impl Log for GithubActionLogger {
7977
Level::Debug => "::debug::",
8078
Level::Trace => "::debug::[TRACE]",
8179
};
82-
// properly escape newlines so that GitHub Actions interprets them correctly
83-
// https://github.com/actions/toolkit/issues/193#issuecomment-605394935
84-
let message_string = message.to_string().replace('\n', "%0A");
80+
let message_string = escape_multiline_message(&message.to_string());
8581
println!("{prefix}{message_string}");
8682
}
8783

@@ -103,3 +99,68 @@ impl SharedLogger for GithubActionLogger {
10399
Box::new(*self)
104100
}
105101
}
102+
103+
/// Escapes newlines in a message for GitHub Actions logging.
104+
/// GitHub Actions requires newlines to be replaced with `%0A` to be interpreted correctly.
105+
///
106+
/// See https://github.com/actions/toolkit/issues/193#issuecomment-605394935
107+
///
108+
/// One exception: trailing newlines are preserved as actual newlines to maintain formatting.
109+
/// Otherwise, the message gets displayed with extra `%0A` at the end.
110+
fn escape_multiline_message(message: &str) -> String {
111+
let trailing_newlines = message.len() - message.trim_end_matches('\n').len();
112+
if trailing_newlines > 0 {
113+
let stripped = &message[..message.len() - trailing_newlines];
114+
let escaped = stripped.replace('\n', "%0A");
115+
let newlines = "\n".repeat(trailing_newlines);
116+
format!("{escaped}{newlines}")
117+
} else {
118+
message.replace('\n', "%0A")
119+
}
120+
}
121+
122+
#[cfg(test)]
123+
mod tests {
124+
use super::*;
125+
126+
#[test]
127+
fn test_escape_multiline_message_no_newlines() {
128+
assert_eq!(escape_multiline_message("hello world"), "hello world");
129+
}
130+
131+
#[test]
132+
fn test_escape_multiline_message_single_trailing_newline() {
133+
assert_eq!(escape_multiline_message("hello world\n"), "hello world\n");
134+
}
135+
136+
#[test]
137+
fn test_escape_multiline_message_internal_newlines() {
138+
assert_eq!(
139+
escape_multiline_message("line1\nline2\nline3"),
140+
"line1%0Aline2%0Aline3"
141+
);
142+
}
143+
144+
#[test]
145+
fn test_escape_multiline_message_internal_and_trailing_newline() {
146+
assert_eq!(
147+
escape_multiline_message("line1\nline2\nline3\n"),
148+
"line1%0Aline2%0Aline3\n"
149+
);
150+
}
151+
152+
#[test]
153+
fn test_escape_multiline_message_empty_string() {
154+
assert_eq!(escape_multiline_message(""), "");
155+
}
156+
157+
#[test]
158+
fn test_escape_multiline_message_only_newline() {
159+
assert_eq!(escape_multiline_message("\n"), "\n");
160+
}
161+
162+
#[test]
163+
fn test_escape_multiline_message_multiple_trailing_newlines() {
164+
assert_eq!(escape_multiline_message("hello\n\n"), "hello\n\n");
165+
}
166+
}

0 commit comments

Comments
 (0)