Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reset color settings after writing body #844

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

runesoerensen
Copy link
Contributor

The color settings are currently reset immediately after writing the header value in the log_warning and log_error functions, but before setting and writing the (non-bold), colored body value.

As a result, the color setting persist after the function call (e.g. after logging a warning, subsequent log_info output will be yellow). I might be missing something here -- but I doubt that's the intended behavior?

@runesoerensen runesoerensen added bug Something isn't working skip changelog labels Jul 20, 2024
@runesoerensen runesoerensen requested a review from a team as a code owner July 20, 2024 06:58
@@ -14,12 +14,12 @@ pub fn log_error(header: impl AsRef<str>, body: impl AsRef<str>) {
.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))
.unwrap();
writeln!(&mut stream, "\n[Error: {}]", header.as_ref()).unwrap();
stream.reset().unwrap();
Copy link
Member

@edmorley edmorley Jul 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree this is broken on main at the moment (see also #555), and that we need to adjust the resets here, however, I think we need a few more changes.

Something that came up during the buildpack_output work, is that because other tools can end up adding prefixes to each log line (for example, endosome adds the remote: prefix, and Pack CLI can add the [builder] prefixes for an untrusted builder, or the timestamp prefixes when using --timestamps), the log helpers need to wrap each line separately, rather than being able to rely on the ANSI codes from an earlier line still applying for the current line. See:

/// Wraps each line in an ANSI escape sequence while preserving prior ANSI escape sequences.
///
/// ## Why does this exist?
///
/// When buildpack output is streamed to the user, each line is prefixed with `remote: ` by Git.
/// Any colorization of text will apply to those prefixes which is not the desired behavior. This
/// function colors lines of text while ensuring that styles are disabled at the end of each line.
///
/// ## Supports recursive colorization
///
/// Strings that are previously colorized will not be overridden by this function. For example,
/// if a word is already colored yellow, that word will continue to be yellow.
pub(crate) fn wrap_ansi_escape_each_line(ansi: &ANSI, body: impl AsRef<str>) -> String {
let ansi_escape = ansi.to_str();
body.as_ref()
.split('\n')
// If sub contents are colorized it will contain SUBCOLOR ... RESET. After the reset,
// ensure we change back to the current color
.map(|line| line.replace(RESET, &format!("{RESET}{ansi_escape}"))) // Handles nested color
// Set the main color for each line and reset after so we don't colorize `remote:` by accident
.map(|line| format!("{ansi_escape}{line}{RESET}"))
// The above logic causes redundant colors and resets, clean them up
.map(|line| line.replace(&format!("{ansi_escape}{ansi_escape}"), ansi_escape)) // Reduce useless color
.map(|line| line.replace(&format!("{ansi_escape}{RESET}"), "")) // Empty lines or where the nested color is at the end of the line
.collect::<Vec<String>>()
.join("\n")
}

The log_error() behaviour on main is currently equivalent to:

<colour>
[Error: Foo]
<reset><colour>Message

And with prefixes this ends up being:

[builder] <colour>
[builder] [Error: Foo]
[builder] <reset><colour>Message body

When really what we need is:

[builder] 
[builder] <colour>[Error: Foo]<reset>
[builder] <colour>Message body<reset>

ie: I think we need to change a few things:

  1. Add the extra .reset() after writing the message body (as this PR does)
  2. Leave the .reset() after writing the header (ie: skip the removal currently in this PR)
  3. For the header, also change it to use two writeln! calls, rather than including the leading \n in a single call. The heading colour attributes would only then be specified on the second call.

Making those changes should fix #555.

(Those changes still won't fix the case where the message body is a multi-line string, but fixing the latter would be more involved and perhaps not worth doing given the log_* helpers are deprecated in favour of buildpack_output.)

@edmorley
Copy link
Member

Removing skip-changelog since this (a) changes user-facing behaviour, (b) will fix a bug (see #555) - so should be mentioned in the changelog.

@runesoerensen runesoerensen marked this pull request as draft October 17, 2024 04:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working libherokubuildpack
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants