mirror of
https://github.com/go-gitea/gitea.git
synced 2026-03-27 19:02:09 +00:00
Improve severity labels in Actions logs and tweak colors (#36993)
Add support for error, warning, notice, and debug log commands with bold label prefixes and colored backgrounds matching GitHub's style. Parse both `##[cmd]` and `::cmd args::` formats. Also improved the severity colors globally and added a devtest page for these. --------- Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
This commit is contained in:
80
templates/devtest/severity-colors.tmpl
Normal file
80
templates/devtest/severity-colors.tmpl
Normal file
@@ -0,0 +1,80 @@
|
||||
{{template "devtest/devtest-header"}}
|
||||
<div class="page-content devtest ui container">
|
||||
<h1>Severity Colors</h1>
|
||||
|
||||
<h2>Messages</h2>
|
||||
<div class="ui error message">
|
||||
<div class="header">Error Message</div>
|
||||
<p>This is an error message using --color-error-* variables.</p>
|
||||
</div>
|
||||
<div class="ui warning message">
|
||||
<div class="header">Warning Message</div>
|
||||
<p>This is a warning message using --color-warning-* variables.</p>
|
||||
</div>
|
||||
<div class="ui success message">
|
||||
<div class="header">Success Message</div>
|
||||
<p>This is a success message using --color-success-* variables.</p>
|
||||
</div>
|
||||
<div class="ui info message">
|
||||
<div class="header">Info Message</div>
|
||||
<p>This is an info message using --color-info-* variables.</p>
|
||||
</div>
|
||||
|
||||
<h2>Form Fields</h2>
|
||||
<div class="ui form">
|
||||
<div class="field error">
|
||||
<label>Error Field</label>
|
||||
<input type="text" value="Invalid input" readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Labels</h2>
|
||||
<div class="flex-text-block tw-gap-2">
|
||||
<div class="ui red label">Red</div>
|
||||
<div class="ui orange label">Orange</div>
|
||||
<div class="ui yellow label">Yellow</div>
|
||||
<div class="ui green label">Green</div>
|
||||
<div class="ui blue label">Blue</div>
|
||||
<div class="ui violet label">Violet</div>
|
||||
<div class="ui purple label">Purple</div>
|
||||
</div>
|
||||
|
||||
<h2>Color Swatches</h2>
|
||||
<h3>Error</h3>
|
||||
<div class="flex-text-block tw-gap-4">
|
||||
<div class="tw-inline-flex tw-flex-col tw-items-center tw-gap-1 tw-min-w-[120px]">
|
||||
<div class="tw-w-[100px] tw-h-[60px] tw-rounded tw-flex tw-items-center tw-justify-center tw-text-xs tw-bg-error-bg tw-text-error-text tw-border tw-border-error-border">Text</div>
|
||||
<code>error-bg</code>
|
||||
</div>
|
||||
<div class="tw-inline-flex tw-flex-col tw-items-center tw-gap-1 tw-min-w-[120px]">
|
||||
<div class="tw-w-[100px] tw-h-[60px] tw-rounded tw-flex tw-items-center tw-justify-center tw-text-xs tw-bg-error-bg-hover tw-text-error-text tw-border tw-border-error-border">Hover</div>
|
||||
<code>error-bg-hover</code>
|
||||
</div>
|
||||
<div class="tw-inline-flex tw-flex-col tw-items-center tw-gap-1 tw-min-w-[120px]">
|
||||
<div class="tw-w-[100px] tw-h-[60px] tw-rounded tw-flex tw-items-center tw-justify-center tw-text-xs tw-bg-error-bg-active tw-text-error-text tw-border tw-border-error-border">Active</div>
|
||||
<code>error-bg-active</code>
|
||||
</div>
|
||||
</div>
|
||||
<h3>Warning</h3>
|
||||
<div class="flex-text-block tw-gap-4">
|
||||
<div class="tw-inline-flex tw-flex-col tw-items-center tw-gap-1 tw-min-w-[120px]">
|
||||
<div class="tw-w-[100px] tw-h-[60px] tw-rounded tw-flex tw-items-center tw-justify-center tw-text-xs tw-bg-warning-bg tw-text-warning-text tw-border tw-border-warning-border">Text</div>
|
||||
<code>warning-bg</code>
|
||||
</div>
|
||||
</div>
|
||||
<h3>Success</h3>
|
||||
<div class="flex-text-block tw-gap-4">
|
||||
<div class="tw-inline-flex tw-flex-col tw-items-center tw-gap-1 tw-min-w-[120px]">
|
||||
<div class="tw-w-[100px] tw-h-[60px] tw-rounded tw-flex tw-items-center tw-justify-center tw-text-xs tw-bg-success-bg tw-text-success-text tw-border tw-border-success-border">Text</div>
|
||||
<code>success-bg</code>
|
||||
</div>
|
||||
</div>
|
||||
<h3>Info</h3>
|
||||
<div class="flex-text-block tw-gap-4">
|
||||
<div class="tw-inline-flex tw-flex-col tw-items-center tw-gap-1 tw-min-w-[120px]">
|
||||
<div class="tw-w-[100px] tw-h-[60px] tw-rounded tw-flex tw-items-center tw-justify-center tw-text-xs tw-bg-info-bg tw-text-info-text tw-border tw-border-info-border">Text</div>
|
||||
<code>info-bg</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "devtest/devtest-footer"}}
|
||||
@@ -393,12 +393,6 @@ img.ui.avatar,
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.ui.error.message .header,
|
||||
.ui.warning.message .header {
|
||||
color: inherit;
|
||||
filter: saturate(2);
|
||||
}
|
||||
|
||||
.full.height {
|
||||
flex-grow: 1;
|
||||
padding-bottom: var(--page-space-bottom);
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.ui.info.message .header,
|
||||
.ui.blue.message .header {
|
||||
color: var(--color-blue);
|
||||
.ui.message .header {
|
||||
color: inherit;
|
||||
filter: saturate(2);
|
||||
}
|
||||
|
||||
.ui.info.message,
|
||||
@@ -55,12 +55,6 @@
|
||||
border-color: var(--color-info-border);
|
||||
}
|
||||
|
||||
.ui.success.message .header,
|
||||
.ui.positive.message .header,
|
||||
.ui.green.message .header {
|
||||
color: var(--color-green);
|
||||
}
|
||||
|
||||
.ui.success.message,
|
||||
.ui.attached.success.message,
|
||||
.ui.positive.message,
|
||||
@@ -70,12 +64,6 @@
|
||||
border-color: var(--color-success-border);
|
||||
}
|
||||
|
||||
.ui.error.message .header,
|
||||
.ui.negative.message .header,
|
||||
.ui.red.message .header {
|
||||
color: var(--color-red);
|
||||
}
|
||||
|
||||
.ui.error.message,
|
||||
.ui.attached.error.message,
|
||||
.ui.red.message,
|
||||
@@ -87,11 +75,6 @@
|
||||
border-color: var(--color-error-border);
|
||||
}
|
||||
|
||||
.ui.warning.message .header,
|
||||
.ui.yellow.message .header {
|
||||
color: var(--color-yellow);
|
||||
}
|
||||
|
||||
.ui.warning.message,
|
||||
.ui.attached.warning.message,
|
||||
.ui.yellow.message,
|
||||
|
||||
@@ -162,20 +162,20 @@ gitea-theme-meta-info {
|
||||
--color-diff-removed-row-border: #634343;
|
||||
--color-diff-removed-word-bg: #6f3333;
|
||||
--color-diff-inactive: #22282d;
|
||||
--color-error-border: #a04141;
|
||||
--color-error-bg: #522;
|
||||
--color-error-bg-active: #744;
|
||||
--color-error-bg-hover: #633;
|
||||
--color-error-text: #f9cbcb;
|
||||
--color-error-border: #da3633;
|
||||
--color-error-bg: #3c2425;
|
||||
--color-error-bg-active: #5a3637;
|
||||
--color-error-bg-hover: #4c2d2e;
|
||||
--color-error-text: #f5817c;
|
||||
--color-success-border: #458a57;
|
||||
--color-success-bg: #284034;
|
||||
--color-success-text: #6cc664;
|
||||
--color-warning-border: #bb9d00;
|
||||
--color-warning-bg: #3a3a30;
|
||||
--color-warning-text: #fbbd08;
|
||||
--color-success-text: #69be61;
|
||||
--color-warning-border: #9e6a03;
|
||||
--color-warning-bg: #2f2a1b;
|
||||
--color-warning-text: #d29922;
|
||||
--color-info-border: #306090;
|
||||
--color-info-bg: #26354c;
|
||||
--color-info-text: #38a8e8;
|
||||
--color-info-text: #48b7f8;
|
||||
--color-red-badge: #db2828;
|
||||
--color-red-badge-bg: #db28281a;
|
||||
--color-red-badge-hover-bg: #db28284d;
|
||||
|
||||
@@ -162,20 +162,20 @@ gitea-theme-meta-info {
|
||||
--color-diff-removed-row-border: #f1c0c0;
|
||||
--color-diff-removed-word-bg: #fdb8c0;
|
||||
--color-diff-inactive: #f0f2f4;
|
||||
--color-error-border: #e0b4b4;
|
||||
--color-error-bg: #fff6f6;
|
||||
--color-error-bg-active: #fbb;
|
||||
--color-error-bg-hover: #fdd;
|
||||
--color-error-text: #9f3a38;
|
||||
--color-success-border: #a3c293;
|
||||
--color-success-bg: #fcfff5;
|
||||
--color-success-text: #2c662d;
|
||||
--color-warning-border: #c9ba9b;
|
||||
--color-warning-bg: #fffaf3;
|
||||
--color-warning-text: #573a08;
|
||||
--color-info-border: #a9d5de;
|
||||
--color-info-bg: #f8ffff;
|
||||
--color-info-text: #276f86;
|
||||
--color-error-border: #d63333;
|
||||
--color-error-bg: #ffebeb;
|
||||
--color-error-bg-active: #fdd;
|
||||
--color-error-bg-hover: #fee;
|
||||
--color-error-text: #8a3231;
|
||||
--color-success-border: #49842b;
|
||||
--color-success-bg: #eef6e4;
|
||||
--color-success-text: #2f6e30;
|
||||
--color-warning-border: #bf8700;
|
||||
--color-warning-bg: #fff8e1;
|
||||
--color-warning-text: #744500;
|
||||
--color-info-border: #2d8fa8;
|
||||
--color-info-bg: #e8f4fd;
|
||||
--color-info-text: #216078;
|
||||
--color-red-badge: #db2828;
|
||||
--color-red-badge-bg: #db28281a;
|
||||
--color-red-badge-hover-bg: #db28284d;
|
||||
|
||||
@@ -229,7 +229,8 @@ function createLogLine(stepIndex: number, startTime: number, line: LogLine, cmd:
|
||||
toggleElem(logTimeStamp, timeVisible.value['log-time-stamp']);
|
||||
toggleElem(logTimeSeconds, timeVisible.value['log-time-seconds']);
|
||||
|
||||
return createElementFromAttrs('div', {id: `jobstep-${stepIndex}-${line.index}`, class: 'job-log-line'},
|
||||
const lineClass = cmd?.name ? `job-log-line log-line-${cmd.name}` : 'job-log-line';
|
||||
return createElementFromAttrs('div', {id: `jobstep-${stepIndex}-${line.index}`, class: lineClass},
|
||||
lineNum, logTimeStamp, logMsg, logTimeSeconds,
|
||||
);
|
||||
}
|
||||
@@ -650,8 +651,28 @@ async function hashChangeListener() {
|
||||
color: var(--color-ansi-blue);
|
||||
}
|
||||
|
||||
.job-step-logs .job-log-line .log-cmd-error {
|
||||
color: var(--color-ansi-red);
|
||||
.job-step-logs .log-msg-label {
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.job-step-logs .log-line-error {
|
||||
background: var(--color-error-bg);
|
||||
}
|
||||
|
||||
.job-step-logs .log-line-warning {
|
||||
background: var(--color-warning-bg);
|
||||
}
|
||||
|
||||
.job-step-logs .log-cmd-error > .log-msg-label {
|
||||
color: var(--color-error-text);
|
||||
}
|
||||
|
||||
.job-step-logs .log-cmd-warning > .log-msg-label {
|
||||
color: var(--color-warning-text);
|
||||
}
|
||||
|
||||
.job-step-logs .log-cmd-debug {
|
||||
color: var(--color-violet);
|
||||
}
|
||||
|
||||
/* selectors here are intentionally exact to only match fullscreen */
|
||||
|
||||
@@ -8,8 +8,14 @@ test('LogLineMessage', () => {
|
||||
'##[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>',
|
||||
'##[error] foo': '<span class="log-msg log-cmd-error"><span class="log-msg-label">Error:</span><span> foo</span></span>',
|
||||
'##[warning] foo': '<span class="log-msg log-cmd-warning"><span class="log-msg-label">Warning:</span><span> foo</span></span>',
|
||||
'##[notice] foo': '<span class="log-msg log-cmd-notice"><span class="log-msg-label">Notice:</span><span> foo</span></span>',
|
||||
'##[debug] foo': '<span class="log-msg log-cmd-debug"><span class="log-msg-label">Debug:</span><span> foo</span></span>',
|
||||
'::error::foo': '<span class="log-msg log-cmd-error"><span class="log-msg-label">Error:</span><span> foo</span></span>',
|
||||
'::warning file=test.js,line=1::foo': '<span class="log-msg log-cmd-warning"><span class="log-msg-label">Warning:</span><span> foo</span></span>',
|
||||
'::notice::foo': '<span class="log-msg log-cmd-notice"><span class="log-msg-label">Notice:</span><span> foo</span></span>',
|
||||
'::debug::foo': '<span class="log-msg log-cmd-debug"><span class="log-msg-label">Debug:</span><span> foo</span></span>',
|
||||
'[command] foo': '<span class="log-msg log-cmd-command"> foo</span>',
|
||||
|
||||
// hidden is special, it is actually skipped before creating
|
||||
|
||||
@@ -17,6 +17,9 @@ const LogLinePrefixCommandMap: Record<string, LogLineCommandName> = {
|
||||
'##[endgroup]': 'endgroup',
|
||||
|
||||
'##[error]': 'error',
|
||||
'##[warning]': 'warning',
|
||||
'##[notice]': 'notice',
|
||||
'##[debug]': 'debug',
|
||||
'[command]': 'command',
|
||||
|
||||
// https://github.com/actions/toolkit/blob/master/docs/commands.md
|
||||
@@ -26,13 +29,16 @@ const LogLinePrefixCommandMap: Record<string, LogLineCommandName> = {
|
||||
'::remove-matcher': 'hidden', // it has arguments
|
||||
};
|
||||
|
||||
// Pattern for ::cmd:: and ::cmd args:: format (args are stripped for display)
|
||||
const LogLineCmdPattern = /^::(error|warning|notice|debug)(?:\s[^:]*)?::/;
|
||||
|
||||
export type LogLine = {
|
||||
index: number;
|
||||
timestamp: number;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type LogLineCommandName = 'group' | 'endgroup' | 'command' | 'error' | 'hidden';
|
||||
export type LogLineCommandName = 'group' | 'endgroup' | 'command' | 'error' | 'warning' | 'notice' | 'debug' | 'hidden';
|
||||
export type LogLineCommand = {
|
||||
name: LogLineCommandName,
|
||||
prefix: string,
|
||||
@@ -45,19 +51,39 @@ export function parseLogLineCommand(line: LogLine): LogLineCommand | null {
|
||||
return {name: LogLinePrefixCommandMap[prefix], prefix};
|
||||
}
|
||||
}
|
||||
// Handle ::cmd:: and ::cmd args:: format (runner may pass these through raw)
|
||||
const match = LogLineCmdPattern.exec(line.message);
|
||||
if (match) {
|
||||
return {name: match[1] as LogLineCommandName, prefix: match[0]};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const LogLineLabelMap: Partial<Record<LogLineCommandName, string>> = {
|
||||
'error': 'Error',
|
||||
'warning': 'Warning',
|
||||
'notice': 'Notice',
|
||||
'debug': 'Debug',
|
||||
};
|
||||
|
||||
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"
|
||||
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 logMsg = createElementFromAttrs('span', logMsgAttrs);
|
||||
logMsg.innerHTML = renderAnsi(msgContent);
|
||||
const label = cmd ? LogLineLabelMap[cmd.name] : null;
|
||||
if (label) {
|
||||
logMsg.append(createElementFromAttrs('span', {class: 'log-msg-label'}, `${label}:`));
|
||||
const msgSpan = document.createElement('span');
|
||||
msgSpan.innerHTML = ` ${renderAnsi(msgContent.trimStart())}`;
|
||||
logMsg.append(msgSpan);
|
||||
} else {
|
||||
logMsg.innerHTML = renderAnsi(msgContent);
|
||||
}
|
||||
return logMsg;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user