Color command/error logs in Actions log (#36538)

Support `[command]` and `##[error]` log command

------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
silverwind
2026-02-06 16:05:32 +01:00
committed by GitHub
parent 403a73dca0
commit 915b44810d
2 changed files with 94 additions and 68 deletions

View File

@@ -1,22 +1,26 @@
import {shouldHideLine, type LogLine} from './RepoActionView.vue';
import {createLogLineMessage, parseLogLineCommand} from './RepoActionView.vue';
test('shouldHideLine', () => {
expect(([
{index: 1, message: 'Starting build process', timestamp: 1000},
{index: 2, message: '::add-matcher::/home/runner/go/pkg/mod/example.com/tool/matcher.json', timestamp: 1001},
{index: 3, message: 'Running tests...', timestamp: 1002},
{index: 4, message: '##[add-matcher]/opt/hostedtoolcache/go/1.25.7/x64/matchers.json', timestamp: 1003},
{index: 5, message: 'Test suite started', timestamp: 1004},
{index: 7, message: 'All tests passed', timestamp: 1006},
{index: 8, message: '::remove-matcher owner=go::', timestamp: 1007},
{index: 9, message: 'Build complete', timestamp: 1008},
] as Array<LogLine>).filter((line) => !shouldHideLine(line)).map((line) => line.message)).toMatchInlineSnapshot(`
[
"Starting build process",
"Running tests...",
"Test suite started",
"All tests passed",
"Build complete",
]
`);
test('LogLineMessage', () => {
const cases = {
'normal message': '<span class="log-msg">normal message</span>',
'##[group] foo': '<span class="log-msg log-cmd-group"> foo</span>',
'::group::foo': '<span class="log-msg log-cmd-group">foo</span>',
'##[endgroup]': '<span class="log-msg log-cmd-endgroup"></span>',
'::endgroup::': '<span class="log-msg log-cmd-endgroup"></span>',
// parser shouldn't do any trim, keep origin output as-is
'##[error] foo': '<span class="log-msg log-cmd-error"> foo</span>',
'[command] foo': '<span class="log-msg log-cmd-command"> foo</span>',
// hidden is special, it is actually skipped before creating
'##[add-matcher]foo': '<span class="log-msg log-cmd-hidden">foo</span>',
'::add-matcher::foo': '<span class="log-msg log-cmd-hidden">foo</span>',
'::remove-matcher foo::': '<span class="log-msg log-cmd-hidden"> foo::</span>', // not correctly parsed, but we don't need it
};
for (const [input, html] of Object.entries(cases)) {
const line = {index: 0, timestamp: 0, message: input};
const cmd = parseLogLineCommand(line);
const el = createLogLineMessage(line, cmd);
expect(el.outerHTML).toBe(html);
}
});