Skip to content

Commit f0b00fd

Browse files
Add notice annotation and support more annotation fields (#855)
* Add support for notice annotation and additional properties * Add additional tests * Update readme * Change casing for endLine and endColumn * Update utils.ts * Update README.md * Rename files to have internal- nomenclature * Revert "Rename files to have internal- nomenclature" This reverts commit 7911689. * Update utils.ts
1 parent 4564768 commit f0b00fd

File tree

7 files changed

+194
-8
lines changed

7 files changed

+194
-8
lines changed

docs/assets/annotations.png

45.6 KB
Loading

docs/problem-matchers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Problem Matchers are a way to scan the output of actions for a specified regex p
66

77
Currently, GitHub Actions limit the annotation count in a workflow run.
88

9-
- 10 warning annotations and 10 error annotations per step
9+
- 10 warning annotations, 10 error annotations, and 10 notice annotations per step
1010
- 50 annotations per job (sum of annotations from all the steps)
1111
- 50 annotations per run (separate from the job annotations, these annotations aren’t created by users)
1212

packages/core/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ try {
9292

9393
// Do stuff
9494
core.info('Output to the actions build log')
95+
96+
core.notice('This is a message that will also emit an annotation')
9597
}
9698
catch (err) {
9799
core.error(`Error ${err}, action may still succeed though`);
@@ -115,6 +117,54 @@ const result = await core.group('Do something async', async () => {
115117
})
116118
```
117119

120+
#### Annotations
121+
122+
This library has 3 methods that will produce [annotations](https://docs.github.com/en/rest/reference/checks#create-a-check-run).
123+
```js
124+
core.error('This is a bad error. This will also fail the build.')
125+
126+
core.warning('Something went wrong, but it\'s not bad enough to fail the build.')
127+
128+
core.notice('Something happened that you might want to know about.')
129+
```
130+
131+
These will surface to the UI in the Actions page and on Pull Requests. They look something like this:
132+
133+
![Annotations Image](../../docs/assets/annotations.png)
134+
135+
These annotations can also be attached to particular lines and columns of your source files to show exactly where a problem is occuring.
136+
137+
These options are:
138+
```typescript
139+
export interface AnnotationProperties {
140+
/**
141+
* A title for the annotation.
142+
*/
143+
title?: string
144+
145+
/**
146+
* The start line for the annotation.
147+
*/
148+
startLine?: number
149+
150+
/**
151+
* The end line for the annotation. Defaults to `startLine` when `startLine` is provided.
152+
*/
153+
endLine?: number
154+
155+
/**
156+
* The start column for the annotation. Cannot be sent when `startLine` and `endLine` are different values.
157+
*/
158+
startColumn?: number
159+
160+
/**
161+
* The start column for the annotation. Cannot be sent when `startLine` and `endLine` are different values.
162+
* Defaults to `startColumn` when `startColumn` is provided.
163+
*/
164+
endColumn?: number
165+
}
166+
```
167+
118168
#### Styling output
119169

120170
Colored output is supported in the Action logs via standard [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code). 3/4 bit, 8 bit and 24 bit colors are all supported.

packages/core/__tests__/core.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as fs from 'fs'
22
import * as os from 'os'
33
import * as path from 'path'
44
import * as core from '../src/core'
5+
import {toCommandProperties} from '../src/utils'
56

67
/* eslint-disable @typescript-eslint/unbound-method */
78

@@ -269,6 +270,20 @@ describe('@actions/core', () => {
269270
assertWriteCalls([`::error::Error: ${message}${os.EOL}`])
270271
})
271272

273+
it('error handles parameters correctly', () => {
274+
const message = 'this is my error message'
275+
core.error(new Error(message), {
276+
title: 'A title',
277+
startColumn: 1,
278+
endColumn: 2,
279+
startLine: 5,
280+
endLine: 5
281+
})
282+
assertWriteCalls([
283+
`::error title=A title,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
284+
])
285+
})
286+
272287
it('warning sets the correct message', () => {
273288
core.warning('Warning')
274289
assertWriteCalls([`::warning::Warning${os.EOL}`])
@@ -285,6 +300,38 @@ describe('@actions/core', () => {
285300
assertWriteCalls([`::warning::Error: ${message}${os.EOL}`])
286301
})
287302

303+
it('warning handles parameters correctly', () => {
304+
const message = 'this is my error message'
305+
core.warning(new Error(message), {
306+
title: 'A title',
307+
startColumn: 1,
308+
endColumn: 2,
309+
startLine: 5,
310+
endLine: 5
311+
})
312+
assertWriteCalls([
313+
`::warning title=A title,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
314+
])
315+
})
316+
317+
it('annotations map field names correctly', () => {
318+
const commandProperties = toCommandProperties({
319+
title: 'A title',
320+
startColumn: 1,
321+
endColumn: 2,
322+
startLine: 5,
323+
endLine: 5
324+
})
325+
expect(commandProperties.title).toBe('A title')
326+
expect(commandProperties.col).toBe(1)
327+
expect(commandProperties.endColumn).toBe(2)
328+
expect(commandProperties.line).toBe(5)
329+
expect(commandProperties.endLine).toBe(5)
330+
331+
expect(commandProperties.startColumn).toBeUndefined()
332+
expect(commandProperties.startLine).toBeUndefined()
333+
})
334+
288335
it('startGroup starts a new group', () => {
289336
core.startGroup('my-group')
290337
assertWriteCalls([`::group::my-group${os.EOL}`])

packages/core/src/command.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {toCommandValue} from './utils'
66
// We use any as a valid input type
77
/* eslint-disable @typescript-eslint/no-explicit-any */
88

9-
interface CommandProperties {
9+
export interface CommandProperties {
1010
[key: string]: any
1111
}
1212

packages/core/src/core.ts

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {issue, issueCommand} from './command'
22
import {issueCommand as issueFileCommand} from './file-command'
3-
import {toCommandValue} from './utils'
3+
import {toCommandProperties, toCommandValue} from './utils'
44

55
import * as os from 'os'
66
import * as path from 'path'
@@ -31,6 +31,38 @@ export enum ExitCode {
3131
Failure = 1
3232
}
3333

34+
/**
35+
* Optional properties that can be sent with annotatation commands (notice, error, and warning)
36+
* See: https://docs.github.com/en/rest/reference/checks#create-a-check-run for more information about annotations.
37+
*/
38+
export interface AnnotationProperties {
39+
/**
40+
* A title for the annotation.
41+
*/
42+
title?: string
43+
44+
/**
45+
* The start line for the annotation.
46+
*/
47+
startLine?: number
48+
49+
/**
50+
* The end line for the annotation. Defaults to `startLine` when `startLine` is provided.
51+
*/
52+
endLine?: number
53+
54+
/**
55+
* The start column for the annotation. Cannot be sent when `startLine` and `endLine` are different values.
56+
*/
57+
startColumn?: number
58+
59+
/**
60+
* The start column for the annotation. Cannot be sent when `startLine` and `endLine` are different values.
61+
* Defaults to `startColumn` when `startColumn` is provided.
62+
*/
63+
endColumn?: number
64+
}
65+
3466
//-----------------------------------------------------------------------
3567
// Variables
3668
//-----------------------------------------------------------------------
@@ -199,17 +231,49 @@ export function debug(message: string): void {
199231
/**
200232
* Adds an error issue
201233
* @param message error issue message. Errors will be converted to string via toString()
234+
* @param properties optional properties to add to the annotation.
202235
*/
203-
export function error(message: string | Error): void {
204-
issue('error', message instanceof Error ? message.toString() : message)
236+
export function error(
237+
message: string | Error,
238+
properties: AnnotationProperties = {}
239+
): void {
240+
issueCommand(
241+
'error',
242+
toCommandProperties(properties),
243+
message instanceof Error ? message.toString() : message
244+
)
205245
}
206246

207247
/**
208-
* Adds an warning issue
248+
* Adds a warning issue
209249
* @param message warning issue message. Errors will be converted to string via toString()
250+
* @param properties optional properties to add to the annotation.
210251
*/
211-
export function warning(message: string | Error): void {
212-
issue('warning', message instanceof Error ? message.toString() : message)
252+
export function warning(
253+
message: string | Error,
254+
properties: AnnotationProperties = {}
255+
): void {
256+
issueCommand(
257+
'warning',
258+
toCommandProperties(properties),
259+
message instanceof Error ? message.toString() : message
260+
)
261+
}
262+
263+
/**
264+
* Adds a notice issue
265+
* @param message notice issue message. Errors will be converted to string via toString()
266+
* @param properties optional properties to add to the annotation.
267+
*/
268+
export function notice(
269+
message: string | Error,
270+
properties: AnnotationProperties = {}
271+
): void {
272+
issueCommand(
273+
'notice',
274+
toCommandProperties(properties),
275+
message instanceof Error ? message.toString() : message
276+
)
213277
}
214278

215279
/**

packages/core/src/utils.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// We use any as a valid input type
22
/* eslint-disable @typescript-eslint/no-explicit-any */
33

4+
import {AnnotationProperties} from './core'
5+
import {CommandProperties} from './command'
6+
47
/**
58
* Sanitizes an input into a string so it can be passed into issueCommand safely
69
* @param input input to sanitize into a string
@@ -13,3 +16,25 @@ export function toCommandValue(input: any): string {
1316
}
1417
return JSON.stringify(input)
1518
}
19+
20+
/**
21+
*
22+
* @param annotationProperties
23+
* @returns The command properties to send with the actual annotation command
24+
* See IssueCommandProperties: https://github.com/actions/runner/blob/main/src/Runner.Worker/ActionCommandManager.cs#L646
25+
*/
26+
export function toCommandProperties(
27+
annotationProperties: AnnotationProperties
28+
): CommandProperties {
29+
if (!Object.keys(annotationProperties).length) {
30+
return {}
31+
}
32+
33+
return {
34+
title: annotationProperties.title,
35+
line: annotationProperties.startLine,
36+
endLine: annotationProperties.endLine,
37+
col: annotationProperties.startColumn,
38+
endColumn: annotationProperties.endColumn
39+
}
40+
}

0 commit comments

Comments
 (0)