fix: make Actions log parser support multiple line message encoding (#38659)

fix #38652


UI part (`.log-msg`) uses "white-space: break-spaces;" so the new line
can be correctly rendered.

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
wxiaoguang
2026-07-27 22:24:46 +08:00
committed by GitHub
parent 7efcb8d6ca
commit dd407ee3e8
3 changed files with 23 additions and 6 deletions
+15 -5
View File
@@ -9,7 +9,8 @@ import {POST} from '../modules/fetch.ts';
// * Workflow command outputs log commands like "::group::the-title", "::add-matcher::...."
// * Workflow runner parses and processes the commands to "##[group]", apply "matchers", hide secrets, etc.
// * The reported logs are the processed logs.
// HOWEVER: Gitea runner does not completely process those commands. Many works are done by the frontend at the moment.
// HOWEVER: Gitea cannot, a decoded message may contain newlines and FormatLog drops them,
// so the commands arrive here still escaped and the frontend decodes them.
const LogLinePrefixCommandMap: Record<string, LogLineCommandName> = {
'::group::': 'group',
'##[group]': 'group',
@@ -67,14 +68,23 @@ const LogLineLabelMap: Partial<Record<LogLineCommandName, string>> = {
'debug': 'Debug',
};
function decodeLineMessage(line: LogLine, cmd: LogLineCommand | null): string {
// TODO: for some commands (::group::), the "prefix removal" works well, for some commands with "arguments" (::remove-matcher ...::),
// it needs to do further processing in the future (fortunately, at the moment we don't need to handle these commands)
if (!cmd) return line.message;
let msg = line.message.substring(cmd.prefix.length);
if (cmd.name === 'command') return msg; // "command" is only an output tag, do not parse or escape it
// "##[cmd]" also escapes ";" and "]" which delimit its header, "::cmd::" does not
if (!cmd.prefix.startsWith('::')) msg = msg.replace(/%3B/g, ';').replace(/%5D/g, ']');
// renderAnsiInto breaks a line per "\r", so "%0D%0A" is one break. "%25" last keeps "%250A" literal
return msg.replace(/(?:%0D)?%0A/g, '\n').replace(/%0D/g, '\r').replace(/%25/g, '%');
}
export function createLogLineMessage(line: LogLine, cmd: LogLineCommand | null) {
const logMsgAttrs = {class: 'log-msg'};
if (cmd?.name) logMsgAttrs.class += ` log-cmd-${cmd.name}`; // make it easier to add styles to some commands like "error"
// TODO: for some commands (::group::), the "prefix removal" works well, for some commands with "arguments" (::remove-matcher ...::),
// it needs to do further processing in the future (fortunately, at the moment we don't need to handle these commands)
const msgContent = cmd ? line.message.substring(cmd.prefix.length) : line.message;
const msgContent = decodeLineMessage(line, cmd);
const logMsg = createElementFromAttrs('span', logMsgAttrs);
const label = cmd ? LogLineLabelMap[cmd.name] : null;
if (label) {