@crvouga/atlas 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/playwright-reporter/src/index.d.ts +11 -0
- package/dist/playwright-reporter/src/index.js +4 -2
- package/dist/reports/src/artifact-tag-store.d.ts +20 -0
- package/dist/reports/src/artifact-tag-store.js +70 -0
- package/dist/reports/src/artifact-tags.d.ts +96 -0
- package/dist/reports/src/artifact-tags.js +96 -0
- package/dist/reports/src/evidence-detail.d.ts +1 -1
- package/dist/reports/src/evidence-detail.js +6 -2
- package/dist/reports/src/evidence-types.d.ts +5 -0
- package/dist/reports/src/layer-merge.d.ts +1 -1
- package/dist/reports/src/layer-merge.js +3 -2
- package/dist/reports/src/layer-types.d.ts +3 -0
- package/dist/reports/src/publish-checks.js +1 -0
- package/dist/reports/src/report-cli.d.ts +1 -1
- package/dist/reports/src/report-cli.js +30 -2
- package/dist/reports/src/reporter.d.ts +1 -1
- package/dist/reports/src/reports-explorer-route.d.ts +8 -0
- package/dist/reports/src/reports-route.d.ts +8 -0
- package/dist/reports/src/test-catalog.d.ts +31 -27
- package/dist/reports/src/test-catalog.js +73 -12
- package/dist/schema/src/api.d.ts +128 -1
- package/dist/schema/src/api.js +38 -4
- package/dist/schema/src/layer.d.ts +59 -0
- package/dist/schema/src/layer.js +28 -1
- package/dist/server/src/app.js +32 -1
- package/dist/server/src/openapi.d.ts +1 -1
- package/dist/server/src/openapi.js +18 -2
- package/docs/extending.md +3 -0
- package/docs/integrating.md +8 -0
- package/docs/reference/cli.md +7 -0
- package/docs/reference/http-api.md +48 -0
- package/docs/reference/playwright-reporter.md +6 -6
- package/docs/reference/reports.md +235 -8
- package/docs/reference/schema.md +52 -1
- package/docs/report-contract.md +1 -0
- package/docs/rich-evidence.md +28 -0
- package/docs/server.md +24 -0
- package/llms-full.txt +119 -0
- package/package.json +1 -1
- package/packages/schema/schemas/check-detail.schema.json +28 -0
- package/packages/schema/schemas/effective-delta.schema.json +44 -0
- package/packages/schema/schemas/effective-evidence.schema.json +44 -0
- package/packages/schema/schemas/evidence-page.schema.json +37 -0
- package/packages/schema/schemas/layer-rows.schema.json +7 -0
- package/packages/schema/schemas/layer.schema.json +100 -0
- package/packages/schema/schemas/publish-checks.schema.json +65 -0
- package/packages/schema/schemas/report-run.schema.json +17 -1
- package/packages/schema/schemas/report-runs.schema.json +17 -1
- package/packages/schema/schemas/test-catalog.schema.json +42 -0
- package/packages/schema/schemas/test-history.schema.json +28 -0
- package/packages/server/openapi.json +886 -3
- package/packages/visualizer/src/evidence/artifact-tags.tsx +285 -0
- package/packages/visualizer/src/evidence/artifacts.tsx +18 -12
- package/packages/visualizer/src/explorer/RunsView.tsx +23 -4
- package/packages/visualizer/src/explorer/parts.tsx +2 -1
|
@@ -60,6 +60,20 @@ export type CheckStatus = z.infer<typeof CheckStatusSchema>;
|
|
|
60
60
|
* changes only with a new `schemaVersion`.
|
|
61
61
|
*/
|
|
62
62
|
export declare const FRESHNESS: readonly ["unknown", "modified"];
|
|
63
|
+
/**
|
|
64
|
+
* The execution a layer belongs to, named by the producer: one invocation of a local or manual run, which several
|
|
65
|
+
* layers (shards, batches, producers) may share. Presentation and history grouping only: it never takes part in the
|
|
66
|
+
* slot a result fills, in which result wins, or in a status. Absent, a run is told by its CI run URL, else `branch@sha`.
|
|
67
|
+
* Within one project, layers that carry the same `id` are one run; a later invocation gets another `id`.
|
|
68
|
+
*/
|
|
69
|
+
export declare const LayerRunSchema: z.ZodObject<{
|
|
70
|
+
id: z.ZodString;
|
|
71
|
+
title: z.ZodOptional<z.ZodString>;
|
|
72
|
+
startedAtMs: z.ZodOptional<z.ZodNumber>;
|
|
73
|
+
endedAtMs: z.ZodOptional<z.ZodNumber>;
|
|
74
|
+
}, z.core.$loose>;
|
|
75
|
+
/** The execution of a layer, parsed from `LayerRunSchema`. */
|
|
76
|
+
export type LayerRun = z.output<typeof LayerRunSchema>;
|
|
63
77
|
/**
|
|
64
78
|
* A number a check measured, such as a latency or a size, with its unit and an optional allowance. A measurement over
|
|
65
79
|
* its `limit` is reported and never changes the check's status.
|
|
@@ -107,6 +121,7 @@ export declare const AttachmentSchema: z.ZodObject<{
|
|
|
107
121
|
height: z.ZodOptional<z.ZodNumber>;
|
|
108
122
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
109
123
|
description: z.ZodOptional<z.ZodString>;
|
|
124
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
110
125
|
at: z.ZodOptional<z.ZodObject<{
|
|
111
126
|
step: z.ZodOptional<z.ZodString>;
|
|
112
127
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -167,6 +182,7 @@ export declare const StepSchema: z.ZodObject<{
|
|
|
167
182
|
height: z.ZodOptional<z.ZodNumber>;
|
|
168
183
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
169
184
|
description: z.ZodOptional<z.ZodString>;
|
|
185
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
170
186
|
at: z.ZodOptional<z.ZodObject<{
|
|
171
187
|
step: z.ZodOptional<z.ZodString>;
|
|
172
188
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -234,6 +250,7 @@ export declare const AttemptSchema: z.ZodObject<{
|
|
|
234
250
|
height: z.ZodOptional<z.ZodNumber>;
|
|
235
251
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
236
252
|
description: z.ZodOptional<z.ZodString>;
|
|
253
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
237
254
|
at: z.ZodOptional<z.ZodObject<{
|
|
238
255
|
step: z.ZodOptional<z.ZodString>;
|
|
239
256
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -278,6 +295,7 @@ export declare const AttemptSchema: z.ZodObject<{
|
|
|
278
295
|
height: z.ZodOptional<z.ZodNumber>;
|
|
279
296
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
280
297
|
description: z.ZodOptional<z.ZodString>;
|
|
298
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
281
299
|
at: z.ZodOptional<z.ZodObject<{
|
|
282
300
|
step: z.ZodOptional<z.ZodString>;
|
|
283
301
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -341,6 +359,7 @@ export declare const CheckSchema: z.ZodObject<{
|
|
|
341
359
|
height: z.ZodOptional<z.ZodNumber>;
|
|
342
360
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
343
361
|
description: z.ZodOptional<z.ZodString>;
|
|
362
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
344
363
|
at: z.ZodOptional<z.ZodObject<{
|
|
345
364
|
step: z.ZodOptional<z.ZodString>;
|
|
346
365
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -405,6 +424,7 @@ export declare const CheckSchema: z.ZodObject<{
|
|
|
405
424
|
height: z.ZodOptional<z.ZodNumber>;
|
|
406
425
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
407
426
|
description: z.ZodOptional<z.ZodString>;
|
|
427
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
408
428
|
at: z.ZodOptional<z.ZodObject<{
|
|
409
429
|
step: z.ZodOptional<z.ZodString>;
|
|
410
430
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -449,6 +469,7 @@ export declare const CheckSchema: z.ZodObject<{
|
|
|
449
469
|
height: z.ZodOptional<z.ZodNumber>;
|
|
450
470
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
451
471
|
description: z.ZodOptional<z.ZodString>;
|
|
472
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
452
473
|
at: z.ZodOptional<z.ZodObject<{
|
|
453
474
|
step: z.ZodOptional<z.ZodString>;
|
|
454
475
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -500,6 +521,7 @@ export declare const CheckSchema: z.ZodObject<{
|
|
|
500
521
|
height: z.ZodOptional<z.ZodNumber>;
|
|
501
522
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
502
523
|
description: z.ZodOptional<z.ZodString>;
|
|
524
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
503
525
|
at: z.ZodOptional<z.ZodObject<{
|
|
504
526
|
step: z.ZodOptional<z.ZodString>;
|
|
505
527
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -586,6 +608,7 @@ export declare const ReportPartSchema: z.ZodObject<{
|
|
|
586
608
|
height: z.ZodOptional<z.ZodNumber>;
|
|
587
609
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
588
610
|
description: z.ZodOptional<z.ZodString>;
|
|
611
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
589
612
|
at: z.ZodOptional<z.ZodObject<{
|
|
590
613
|
step: z.ZodOptional<z.ZodString>;
|
|
591
614
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -649,6 +672,7 @@ export declare const ReportPartSchema: z.ZodObject<{
|
|
|
649
672
|
height: z.ZodOptional<z.ZodNumber>;
|
|
650
673
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
651
674
|
description: z.ZodOptional<z.ZodString>;
|
|
675
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
652
676
|
at: z.ZodOptional<z.ZodObject<{
|
|
653
677
|
step: z.ZodOptional<z.ZodString>;
|
|
654
678
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -713,6 +737,7 @@ export declare const ReportPartSchema: z.ZodObject<{
|
|
|
713
737
|
height: z.ZodOptional<z.ZodNumber>;
|
|
714
738
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
715
739
|
description: z.ZodOptional<z.ZodString>;
|
|
740
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
716
741
|
at: z.ZodOptional<z.ZodObject<{
|
|
717
742
|
step: z.ZodOptional<z.ZodString>;
|
|
718
743
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -757,6 +782,7 @@ export declare const ReportPartSchema: z.ZodObject<{
|
|
|
757
782
|
height: z.ZodOptional<z.ZodNumber>;
|
|
758
783
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
759
784
|
description: z.ZodOptional<z.ZodString>;
|
|
785
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
760
786
|
at: z.ZodOptional<z.ZodObject<{
|
|
761
787
|
step: z.ZodOptional<z.ZodString>;
|
|
762
788
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -808,6 +834,7 @@ export declare const ReportPartSchema: z.ZodObject<{
|
|
|
808
834
|
height: z.ZodOptional<z.ZodNumber>;
|
|
809
835
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
810
836
|
description: z.ZodOptional<z.ZodString>;
|
|
837
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
811
838
|
at: z.ZodOptional<z.ZodObject<{
|
|
812
839
|
step: z.ZodOptional<z.ZodString>;
|
|
813
840
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -896,6 +923,7 @@ export declare const LayerSourceSchema: z.ZodObject<{
|
|
|
896
923
|
height: z.ZodOptional<z.ZodNumber>;
|
|
897
924
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
898
925
|
description: z.ZodOptional<z.ZodString>;
|
|
926
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
899
927
|
at: z.ZodOptional<z.ZodObject<{
|
|
900
928
|
step: z.ZodOptional<z.ZodString>;
|
|
901
929
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -960,6 +988,7 @@ export declare const LayerSourceSchema: z.ZodObject<{
|
|
|
960
988
|
height: z.ZodOptional<z.ZodNumber>;
|
|
961
989
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
962
990
|
description: z.ZodOptional<z.ZodString>;
|
|
991
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
963
992
|
at: z.ZodOptional<z.ZodObject<{
|
|
964
993
|
step: z.ZodOptional<z.ZodString>;
|
|
965
994
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1004,6 +1033,7 @@ export declare const LayerSourceSchema: z.ZodObject<{
|
|
|
1004
1033
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1005
1034
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1006
1035
|
description: z.ZodOptional<z.ZodString>;
|
|
1036
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1007
1037
|
at: z.ZodOptional<z.ZodObject<{
|
|
1008
1038
|
step: z.ZodOptional<z.ZodString>;
|
|
1009
1039
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1055,6 +1085,7 @@ export declare const LayerSourceSchema: z.ZodObject<{
|
|
|
1055
1085
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1056
1086
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1057
1087
|
description: z.ZodOptional<z.ZodString>;
|
|
1088
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1058
1089
|
at: z.ZodOptional<z.ZodObject<{
|
|
1059
1090
|
step: z.ZodOptional<z.ZodString>;
|
|
1060
1091
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1169,6 +1200,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1169
1200
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1170
1201
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1171
1202
|
description: z.ZodOptional<z.ZodString>;
|
|
1203
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1172
1204
|
at: z.ZodOptional<z.ZodObject<{
|
|
1173
1205
|
step: z.ZodOptional<z.ZodString>;
|
|
1174
1206
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1211,6 +1243,12 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1211
1243
|
unknown: "unknown";
|
|
1212
1244
|
modified: "modified";
|
|
1213
1245
|
}>>;
|
|
1246
|
+
run: z.ZodOptional<z.ZodObject<{
|
|
1247
|
+
id: z.ZodString;
|
|
1248
|
+
title: z.ZodOptional<z.ZodString>;
|
|
1249
|
+
startedAtMs: z.ZodOptional<z.ZodNumber>;
|
|
1250
|
+
endedAtMs: z.ZodOptional<z.ZodNumber>;
|
|
1251
|
+
}, z.core.$loose>>;
|
|
1214
1252
|
specs: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
1215
1253
|
path: z.ZodString;
|
|
1216
1254
|
domain: z.ZodString;
|
|
@@ -1240,6 +1278,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1240
1278
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1241
1279
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1242
1280
|
description: z.ZodOptional<z.ZodString>;
|
|
1281
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1243
1282
|
at: z.ZodOptional<z.ZodObject<{
|
|
1244
1283
|
step: z.ZodOptional<z.ZodString>;
|
|
1245
1284
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1303,6 +1342,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1303
1342
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1304
1343
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1305
1344
|
description: z.ZodOptional<z.ZodString>;
|
|
1345
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1306
1346
|
at: z.ZodOptional<z.ZodObject<{
|
|
1307
1347
|
step: z.ZodOptional<z.ZodString>;
|
|
1308
1348
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1367,6 +1407,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1367
1407
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1368
1408
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1369
1409
|
description: z.ZodOptional<z.ZodString>;
|
|
1410
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1370
1411
|
at: z.ZodOptional<z.ZodObject<{
|
|
1371
1412
|
step: z.ZodOptional<z.ZodString>;
|
|
1372
1413
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1411,6 +1452,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1411
1452
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1412
1453
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1413
1454
|
description: z.ZodOptional<z.ZodString>;
|
|
1455
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1414
1456
|
at: z.ZodOptional<z.ZodObject<{
|
|
1415
1457
|
step: z.ZodOptional<z.ZodString>;
|
|
1416
1458
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1462,6 +1504,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1462
1504
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1463
1505
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1464
1506
|
description: z.ZodOptional<z.ZodString>;
|
|
1507
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1465
1508
|
at: z.ZodOptional<z.ZodObject<{
|
|
1466
1509
|
step: z.ZodOptional<z.ZodString>;
|
|
1467
1510
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1549,6 +1592,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1549
1592
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1550
1593
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1551
1594
|
description: z.ZodOptional<z.ZodString>;
|
|
1595
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1552
1596
|
at: z.ZodOptional<z.ZodObject<{
|
|
1553
1597
|
step: z.ZodOptional<z.ZodString>;
|
|
1554
1598
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1613,6 +1657,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1613
1657
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1614
1658
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1615
1659
|
description: z.ZodOptional<z.ZodString>;
|
|
1660
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1616
1661
|
at: z.ZodOptional<z.ZodObject<{
|
|
1617
1662
|
step: z.ZodOptional<z.ZodString>;
|
|
1618
1663
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1657,6 +1702,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1657
1702
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1658
1703
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1659
1704
|
description: z.ZodOptional<z.ZodString>;
|
|
1705
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1660
1706
|
at: z.ZodOptional<z.ZodObject<{
|
|
1661
1707
|
step: z.ZodOptional<z.ZodString>;
|
|
1662
1708
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1708,6 +1754,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1708
1754
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1709
1755
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1710
1756
|
description: z.ZodOptional<z.ZodString>;
|
|
1757
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1711
1758
|
at: z.ZodOptional<z.ZodObject<{
|
|
1712
1759
|
step: z.ZodOptional<z.ZodString>;
|
|
1713
1760
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1835,6 +1882,7 @@ export declare const AttachmentInputSchema: z.ZodObject<{
|
|
|
1835
1882
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1836
1883
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1837
1884
|
description: z.ZodOptional<z.ZodString>;
|
|
1885
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1838
1886
|
at: z.ZodOptional<z.ZodObject<{
|
|
1839
1887
|
step: z.ZodOptional<z.ZodString>;
|
|
1840
1888
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1915,6 +1963,7 @@ export declare const PublishChecksSchema: z.ZodObject<{
|
|
|
1915
1963
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1916
1964
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1917
1965
|
description: z.ZodOptional<z.ZodString>;
|
|
1966
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1918
1967
|
at: z.ZodOptional<z.ZodObject<{
|
|
1919
1968
|
step: z.ZodOptional<z.ZodString>;
|
|
1920
1969
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1970,6 +2019,7 @@ export declare const PublishChecksSchema: z.ZodObject<{
|
|
|
1970
2019
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1971
2020
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1972
2021
|
description: z.ZodOptional<z.ZodString>;
|
|
2022
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1973
2023
|
at: z.ZodOptional<z.ZodObject<{
|
|
1974
2024
|
step: z.ZodOptional<z.ZodString>;
|
|
1975
2025
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -2058,6 +2108,7 @@ export declare const PublishChecksSchema: z.ZodObject<{
|
|
|
2058
2108
|
height: z.ZodOptional<z.ZodNumber>;
|
|
2059
2109
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
2060
2110
|
description: z.ZodOptional<z.ZodString>;
|
|
2111
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2061
2112
|
at: z.ZodOptional<z.ZodObject<{
|
|
2062
2113
|
step: z.ZodOptional<z.ZodString>;
|
|
2063
2114
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -2084,6 +2135,7 @@ export declare const PublishChecksSchema: z.ZodObject<{
|
|
|
2084
2135
|
height: z.ZodOptional<z.ZodNumber>;
|
|
2085
2136
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
2086
2137
|
description: z.ZodOptional<z.ZodString>;
|
|
2138
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2087
2139
|
at: z.ZodOptional<z.ZodObject<{
|
|
2088
2140
|
step: z.ZodOptional<z.ZodString>;
|
|
2089
2141
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -2109,6 +2161,12 @@ export declare const PublishChecksSchema: z.ZodObject<{
|
|
|
2109
2161
|
unknown: "unknown";
|
|
2110
2162
|
modified: "modified";
|
|
2111
2163
|
}>>;
|
|
2164
|
+
run: z.ZodOptional<z.ZodObject<{
|
|
2165
|
+
id: z.ZodString;
|
|
2166
|
+
title: z.ZodOptional<z.ZodString>;
|
|
2167
|
+
startedAtMs: z.ZodOptional<z.ZodNumber>;
|
|
2168
|
+
endedAtMs: z.ZodOptional<z.ZodNumber>;
|
|
2169
|
+
}, z.core.$loose>>;
|
|
2112
2170
|
specs: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
2113
2171
|
path: z.ZodString;
|
|
2114
2172
|
domain: z.ZodString;
|
|
@@ -2137,6 +2195,7 @@ export declare const PublishChecksSchema: z.ZodObject<{
|
|
|
2137
2195
|
height: z.ZodOptional<z.ZodNumber>;
|
|
2138
2196
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
2139
2197
|
description: z.ZodOptional<z.ZodString>;
|
|
2198
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2140
2199
|
at: z.ZodOptional<z.ZodObject<{
|
|
2141
2200
|
step: z.ZodOptional<z.ZodString>;
|
|
2142
2201
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
package/dist/schema/src/layer.js
CHANGED
|
@@ -50,6 +50,18 @@ export const CheckStatusSchema = z.enum(CHECK_STATUSES);
|
|
|
50
50
|
* changes only with a new `schemaVersion`.
|
|
51
51
|
*/
|
|
52
52
|
export const FRESHNESS = ['unknown', 'modified'];
|
|
53
|
+
/**
|
|
54
|
+
* The execution a layer belongs to, named by the producer: one invocation of a local or manual run, which several
|
|
55
|
+
* layers (shards, batches, producers) may share. Presentation and history grouping only: it never takes part in the
|
|
56
|
+
* slot a result fills, in which result wins, or in a status. Absent, a run is told by its CI run URL, else `branch@sha`.
|
|
57
|
+
* Within one project, layers that carry the same `id` are one run; a later invocation gets another `id`.
|
|
58
|
+
*/
|
|
59
|
+
export const LayerRunSchema = z.looseObject({
|
|
60
|
+
id: z.string().min(1).max(256).describe('The execution\'s identity within a project, chosen by the producer: a UUID, a timestamped name. Layers with the same id are one run; it never changes after it is recorded'),
|
|
61
|
+
title: z.string().optional().describe('A human name for the run. Presentation only: it is not part of the identity and does not hide the commit or `freshness`'),
|
|
62
|
+
startedAtMs: z.number().nonnegative().optional().describe('When the execution started, epoch milliseconds'),
|
|
63
|
+
endedAtMs: z.number().nonnegative().optional().describe('When the execution ended, epoch milliseconds')
|
|
64
|
+
});
|
|
53
65
|
/**
|
|
54
66
|
* A number a check measured, such as a latency or a size, with its unit and an optional allowance. A measurement over
|
|
55
67
|
* its `limit` is reported and never changes the check's status.
|
|
@@ -97,6 +109,7 @@ export const AttachmentSchema = z.looseObject({
|
|
|
97
109
|
height: z.number().nonnegative().optional(),
|
|
98
110
|
durationMs: z.number().nonnegative().optional().describe('For video and audio'),
|
|
99
111
|
description: z.string().optional(),
|
|
112
|
+
tags: z.array(z.string()).optional().describe('Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result'),
|
|
100
113
|
at: MomentSchema.optional(),
|
|
101
114
|
poster: z.string().optional().describe('The `id` of an attachment that stands for this one before it is opened'),
|
|
102
115
|
captions: z.array(z.string()).optional().describe('The `id`s of attachments (WebVTT, SRT) that caption this one'),
|
|
@@ -250,6 +263,7 @@ export const LayerDocumentSchema = z.looseObject({
|
|
|
250
263
|
environment: z.string().nullable().optional().describe('The client that was checked: desktop-web, mobile-web, ios, android, or any other lowercase name'),
|
|
251
264
|
parameters: z.record(z.string(), z.string()).default({}).describe('Other choices that make a result its own slot, such as a device size'),
|
|
252
265
|
freshness: z.enum(FRESHNESS).optional().describe('`modified`: recorded from a checkout with uncommitted changes. `unknown`: origin not captured'),
|
|
266
|
+
run: LayerRunSchema.optional().describe('The execution this layer belongs to, when the producer names one (a local or manual run without a CI run URL). Grouping and display only; it never partitions a report or decides a result'),
|
|
253
267
|
specs: z.array(SpecEntrySchema).default([]),
|
|
254
268
|
parts: z.array(ReportPartSchema),
|
|
255
269
|
source: LayerSourceSchema.nullable().default(null),
|
|
@@ -272,6 +286,15 @@ function lenientList(value, schema, prepare = (item) => item) {
|
|
|
272
286
|
});
|
|
273
287
|
}
|
|
274
288
|
const object = (value) => (typeof value === 'object' && value !== null && !Array.isArray(value) ? value : null);
|
|
289
|
+
/** An attachment whose `tags` cannot be read keeps its file: only the tags that are strings are kept. */
|
|
290
|
+
function lenientAttachment(item) {
|
|
291
|
+
const attachment = object(item);
|
|
292
|
+
if (!attachment || !('tags' in attachment))
|
|
293
|
+
return item;
|
|
294
|
+
const { tags, ...rest } = attachment;
|
|
295
|
+
const kept = lenientList(tags, z.string());
|
|
296
|
+
return kept ? { ...rest, tags: kept } : rest;
|
|
297
|
+
}
|
|
275
298
|
function lenientEvidence(holder, nested = []) {
|
|
276
299
|
const out = { ...holder };
|
|
277
300
|
const set = (key, value) => { if (value === undefined)
|
|
@@ -283,7 +306,7 @@ function lenientEvidence(holder, nested = []) {
|
|
|
283
306
|
if ('links' in out)
|
|
284
307
|
set('links', lenientList(out.links, LinkSchema));
|
|
285
308
|
if ('attachments' in out)
|
|
286
|
-
set('attachments', lenientList(out.attachments, AttachmentSchema));
|
|
309
|
+
set('attachments', lenientList(out.attachments, AttachmentSchema, lenientAttachment));
|
|
287
310
|
if ('annotations' in out)
|
|
288
311
|
set('annotations', lenientList(out.annotations, AnnotationSchema));
|
|
289
312
|
if ('labels' in out)
|
|
@@ -298,6 +321,9 @@ function lenientEvidence(holder, nested = []) {
|
|
|
298
321
|
}
|
|
299
322
|
function lenientLayer(raw) {
|
|
300
323
|
const layer = lenientEvidence(raw);
|
|
324
|
+
// The execution only groups and labels: one that cannot be read leaves the layer to the documented grouping, never refuses it.
|
|
325
|
+
if ('run' in layer && !LayerRunSchema.safeParse(layer.run).success)
|
|
326
|
+
delete layer.run;
|
|
301
327
|
if (Array.isArray(layer.parts)) {
|
|
302
328
|
layer.parts = layer.parts.map((part) => {
|
|
303
329
|
const found = object(part);
|
|
@@ -384,6 +410,7 @@ export const PublishChecksSchema = z.looseObject({
|
|
|
384
410
|
environment: z.string().nullable().optional(),
|
|
385
411
|
parameters: z.record(z.string(), z.string()).default({}),
|
|
386
412
|
freshness: z.enum(FRESHNESS).optional(),
|
|
413
|
+
run: LayerRunSchema.optional().describe('Name the execution these checks belong to, so repeated local runs at one commit stay separate runs and shards of one execution stay together'),
|
|
387
414
|
specs: z.array(SpecEntrySchema).default([]),
|
|
388
415
|
tags: evidenceShape.tags,
|
|
389
416
|
labels: evidenceShape.labels,
|
package/dist/server/src/app.js
CHANGED
|
@@ -20,6 +20,7 @@ import { combineBackends, summarize } from '../../reports/src/compose.js';
|
|
|
20
20
|
import { combineEvidence } from '../../reports/src/layer-merge.js';
|
|
21
21
|
import { layerCatalogKey } from '../../reports/src/layer-store.js';
|
|
22
22
|
import { createLayersApp } from '../../reports/src/layers-route.js';
|
|
23
|
+
import { readArtifactTagEntries, recordArtifactTags, ArtifactTagError } from '../../reports/src/artifact-tag-store.js';
|
|
23
24
|
import { publishChecks } from '../../reports/src/publish-checks.js';
|
|
24
25
|
import { reportStatus } from '../../reports/src/report-status.js';
|
|
25
26
|
import { ReportIndex } from '../../reports/src/report-index.js';
|
|
@@ -254,7 +255,7 @@ function createProjectApplication(options) {
|
|
|
254
255
|
name: 'atlas', version: options.version ?? null, role: identity?.role ?? 'read', open: auth.open,
|
|
255
256
|
write: options.write, ready: index.ready, viewers: events.count, time: now(),
|
|
256
257
|
backends: snapshot.backends.map(({ id, label, available }) => ({ id, label, available, write: id === options.write })),
|
|
257
|
-
...(({ backends: _unused, ...rest }) => rest)(index.stats()), capabilities: { events: true, ingest: true, store: true, checks: true, status: true, effective: true, live: true, tests: true }
|
|
258
|
+
...(({ backends: _unused, ...rest }) => rest)(index.stats()), capabilities: { events: true, ingest: true, store: true, checks: true, status: true, effective: true, live: true, tests: true, artifactTags: true }
|
|
258
259
|
});
|
|
259
260
|
});
|
|
260
261
|
app.get('/api/live', async (c) => c.json({ runs: await live.list() }, 200, { 'cache-control': 'no-store' }));
|
|
@@ -440,6 +441,36 @@ function createProjectApplication(options) {
|
|
|
440
441
|
return problemResponse(404, 'No run has that key.');
|
|
441
442
|
return answer(c, found.version, () => document, () => runMarkdown(document), `run|${key}|${wantsMarkdown(c)}`);
|
|
442
443
|
});
|
|
444
|
+
// Reviewer tags for attachments: an audit trail of edits kept beside the layers, which they never change. Read
|
|
445
|
+
// with a read token; an edit needs a publish token in a Bearer header, and is recorded under that token's name.
|
|
446
|
+
const tagStore = options.liveStore ?? writeBackend.store;
|
|
447
|
+
// Edits are ordered by time, so two in one millisecond are told apart by moving the later one on.
|
|
448
|
+
let lastTagAt = 0;
|
|
449
|
+
const artifactTags = cached(() => readArtifactTagEntries(tagStore), 5_000);
|
|
450
|
+
app.get('/api/reports/artifact-tags', async (c) => c.json({ schemaVersion: 1, entries: await artifactTags() }, 200, { 'cache-control': 'no-store' }));
|
|
451
|
+
app.post('/api/reports/artifact-tags', async (c) => {
|
|
452
|
+
const verdict = auth.check(c.req.raw, 'publish');
|
|
453
|
+
if (!verdict.ok)
|
|
454
|
+
return unauthorized(verdict);
|
|
455
|
+
try {
|
|
456
|
+
const bytes = await readLimited(c.req.raw, Math.min(limits.json, 64 * 1024));
|
|
457
|
+
let input;
|
|
458
|
+
try {
|
|
459
|
+
input = JSON.parse(new TextDecoder().decode(bytes));
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
return problemResponse(422, 'The body is not JSON.');
|
|
463
|
+
}
|
|
464
|
+
const entry = await recordArtifactTags(tagStore, input, verdict.identity.name, lastTagAt = Math.max(now(), lastTagAt + 1));
|
|
465
|
+
await artifactTags.invalidate();
|
|
466
|
+
return c.json(entry, 201, { 'cache-control': 'no-store' });
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
if (error instanceof ArtifactTagError || error instanceof Refusal)
|
|
470
|
+
return problemResponse(error.status, error.message);
|
|
471
|
+
throw error;
|
|
472
|
+
}
|
|
473
|
+
});
|
|
443
474
|
app.route('/api/reports', layers);
|
|
444
475
|
// Upload: the store protocol, and a simple JSON way in for anything that can make a request.
|
|
445
476
|
app.route('/api/store', storeApi({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The version of the API described, `major.minor.patch`: the minor grows when an operation or field is added, the major never within `/api`. */
|
|
2
|
-
export declare const API_VERSION = "1.
|
|
2
|
+
export declare const API_VERSION = "1.5.0";
|
|
3
3
|
/** Who may call an operation: `none` (anyone), or the least role that may (`read` < `publish` < `admin`). */
|
|
4
4
|
export type Role = 'none' | 'read' | 'publish' | 'admin';
|
|
5
5
|
type JsonSchema = Record<string, unknown>;
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { z } from 'zod/v4';
|
|
16
16
|
import { ProjectsSchema } from '../../schema/src/project.js';
|
|
17
|
-
import { EffectiveDeltaSchema, EffectiveEvidenceSchema, EvidencePageSchema, HealthSchema, LayerRowsSchema, CheckDetailSchema, OutlineSchema, ProblemSchema, ReadySchema, ServerInfoSchema, StoreListingSchema, StoreWriteResultSchema, ChecksResultSchema, HelloEventSchema, ReportEventSchema, PingEventSchema, TestCatalogSchema, CatalogHealthSchema, TestHistorySchema, RunListSchema, RunDetailSchema } from '../../schema/src/api.js';
|
|
17
|
+
import { EffectiveDeltaSchema, EffectiveEvidenceSchema, EvidencePageSchema, HealthSchema, LayerRowsSchema, CheckDetailSchema, OutlineSchema, ProblemSchema, ReadySchema, ServerInfoSchema, StoreListingSchema, StoreWriteResultSchema, ChecksResultSchema, HelloEventSchema, ReportEventSchema, PingEventSchema, TestCatalogSchema, CatalogHealthSchema, TestHistorySchema, RunListSchema, RunDetailSchema, ArtifactTagEditSchema, ArtifactTagEntrySchema, ArtifactTagsSchema } from '../../schema/src/api.js';
|
|
18
18
|
import { LiveRunSchema, LiveRunStartSchema, LiveRunUpdateSchema, ServerEditableSettingsSchema, ServerSettingsSnapshotSchema, AtlasChangeSchema, PublishChecksSchema, ReportSourcesSchema, ReportStatusSchema, RunsIndexSchema, SpecIndexSchema } from '../../schema/src/index.js';
|
|
19
19
|
/** The version of the API described, `major.minor.patch`: the minor grows when an operation or field is added, the major never within `/api`. */
|
|
20
|
-
export const API_VERSION = '1.
|
|
20
|
+
export const API_VERSION = '1.5.0';
|
|
21
21
|
/** The JSON Schema of a Zod schema, as OpenAPI 3.1 embeds it. `output` for what the server sends, `input` for what it accepts. */
|
|
22
22
|
function schemaOf(schema, io = 'output') {
|
|
23
23
|
const { $schema: _dialect, ...json } = z.toJSONSchema(schema, { target: 'draft-2020-12', unrepresentable: 'any', io });
|
|
@@ -35,6 +35,7 @@ const COMPONENTS = {
|
|
|
35
35
|
PublishChecks: [PublishChecksSchema.partial({ recordedAtMs: true }), 'input'], ChecksResult: [ChecksResultSchema, 'output'],
|
|
36
36
|
StoreListing: [StoreListingSchema, 'output'], StoreWriteResult: [StoreWriteResultSchema, 'output'],
|
|
37
37
|
Outline: [OutlineSchema, 'output'], EvidencePage: [EvidencePageSchema, 'output'], LayerRows: [LayerRowsSchema, 'output'], CheckDetail: [CheckDetailSchema, 'output'],
|
|
38
|
+
ArtifactTagEdit: [ArtifactTagEditSchema, 'input'], ArtifactTagEntry: [ArtifactTagEntrySchema, 'output'], ArtifactTags: [ArtifactTagsSchema, 'output'],
|
|
38
39
|
TestCatalog: [TestCatalogSchema, 'output'], CatalogHealth: [CatalogHealthSchema, 'output'], TestHistory: [TestHistorySchema, 'output'], RunList: [RunListSchema, 'output'], RunDetail: [RunDetailSchema, 'output'],
|
|
39
40
|
SpecIndex: [SpecIndexSchema, 'output'], ReportSources: [ReportSourcesSchema, 'output'], RunsIndex: [RunsIndexSchema, 'output'],
|
|
40
41
|
RunEvent: [LiveRunSchema, 'output'], HelloEvent: [HelloEventSchema, 'output'], ReportEvent: [ReportEventSchema, 'output'], AtlasChangeEvent: [AtlasChangeSchema, 'output'], PingEvent: [PingEventSchema, 'output']
|
|
@@ -271,6 +272,8 @@ const BASE_OPERATIONS = [
|
|
|
271
272
|
{ name: 'domain', in: 'query', description: 'Only tests whose spec is in this domain (`(no spec)` for none). Repeatable or comma-separated.', schema: str },
|
|
272
273
|
{ name: 'spec', in: 'query', description: 'Only tests of this spec path. Repeatable or comma-separated.', schema: str, example: 'specs/checkout/pay.feature' },
|
|
273
274
|
{ name: 'tag', in: 'query', description: 'Only tests with this tag. Repeatable or comma-separated.', schema: str },
|
|
275
|
+
{ name: 'artifactTag', in: 'query', description: 'Only tests with an attachment tagged this (`demo` and `#demo` are the same tag), not counting tags inherited from a check or run: the exact artifact query. `inherited=1` widens it to the tags inherited from the check and run. Repeatable or comma-separated.', schema: str, example: 'demo' },
|
|
276
|
+
{ name: 'inherited', in: 'query', description: 'With `artifactTag`: `1` also matches the tags an attachment inherits from its check and run, so a recording without a tag of its own under a run tagged `demo` is found. Without it the query is exact.', schema: { type: 'string', enum: ['1', 'true'] } },
|
|
274
277
|
{ name: 'run', in: 'query', description: 'Only tests recorded in this run (a `key` from `/api/reports/runs`).', schema: str },
|
|
275
278
|
{ name: 'sort', in: 'query', description: 'The order: `spec` (default: domain, spec, title), `title`, `recent`, `slowest`, `failures`, `flaky` or `runs`.', schema: { type: 'string', enum: ['spec', 'title', 'recent', 'slowest', 'failures', 'flaky', 'runs'] } },
|
|
276
279
|
{ name: 'offset', in: 'query', description: 'Skip this many matching tests.', schema: { type: 'integer', minimum: 0 } },
|
|
@@ -305,6 +308,19 @@ const BASE_OPERATIONS = [
|
|
|
305
308
|
],
|
|
306
309
|
responses: { '200': json('The test and its history.', 'TestHistory'), '304': { description: 'Unchanged since the `If-None-Match` ETag.' }, '400': problem('No `key`.'), '401': UNAUTHORIZED, '404': problem('No test in the report has that key.'), '503': NOT_READY }
|
|
307
310
|
},
|
|
311
|
+
{
|
|
312
|
+
route: 'GET /api/reports/artifact-tags', path: '/api/reports/artifact-tags', operationId: 'getArtifactTags', tag: 'Report', role: 'read',
|
|
313
|
+
summary: 'Reviewer tags of attachments: every recorded edit',
|
|
314
|
+
description: 'The audit trail of tags that reviewers added to and removed from attachments after upload, oldest first. Each entry says which attachment (`target`), which normalized tags were added and removed, the name of the token that made the edit (`by`, never the token) and when (`atMs`). The layers themselves are never rewritten, so a producer\'s own `tags` on an attachment stay as uploaded; fold the entries (oldest first, the last edit of a tag wins) to get the reviewer tags that stand now. Tags are normalized: `#Demo` and `demo` are `demo`.',
|
|
315
|
+
responses: { '200': json('The edits.', 'ArtifactTags'), '401': UNAUTHORIZED }
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
route: 'POST /api/reports/artifact-tags', path: '/api/reports/artifact-tags', operationId: 'editArtifactTags', tag: 'Report', role: 'publish',
|
|
319
|
+
summary: 'Tag or untag one attachment after upload',
|
|
320
|
+
description: 'Records one edit of the reviewer tags of one attachment, by the name of the publish (or admin) token that sends it. It adds an immutable entry to the audit trail and changes nothing else: not the layer, not the movie, not any result. The tags it removes are reviewer tags only; a producer\'s tags are part of the layer and stay. A reader-only token, a browser sign-in box and an anonymous reader are refused. Name the attachment as `artifactRef` does (`id:<id>`, else `key:<bundleKey>`, else `name:<name>`).',
|
|
321
|
+
body: { description: 'The attachment and the tags to add and remove. At most 20 of each, 64 KiB.', contentType: 'application/json', required: true, schema: ref('ArtifactTagEdit') },
|
|
322
|
+
responses: { '201': json('The recorded entry.', 'ArtifactTagEntry'), '401': UNAUTHORIZED, '403': FORBIDDEN, '413': problem('Request too large.'), '422': problem('Not a tag edit: a bad target, an empty or over-long tag, or nothing to change.') }
|
|
323
|
+
},
|
|
308
324
|
{
|
|
309
325
|
route: 'GET /api/reports/runs', path: '/api/reports/runs', operationId: 'getReportRuns', tag: 'Report', role: 'read',
|
|
310
326
|
summary: 'The runs that recorded the report',
|
package/docs/extending.md
CHANGED
|
@@ -35,6 +35,9 @@ carry any of these. Every one is optional.
|
|
|
35
35
|
| `extensions{}` | Structured data of your own or a tool's, under a name you own. |
|
|
36
36
|
| `measurements[]` | Numbers, with units and an allowance (already part of the contract). |
|
|
37
37
|
|
|
38
|
+
An attachment may carry its own `tags[]` (`demo`): shown as `#demo` and searchable; see
|
|
39
|
+
[Tagging artifacts and demos](rich-evidence.md#tagging-artifacts-and-demos).
|
|
40
|
+
|
|
38
41
|
On a **layer** the same fields describe the whole run: its CI log, a coverage report, an archive of traces, the
|
|
39
42
|
build number, a link to the pipeline.
|
|
40
43
|
|
package/docs/integrating.md
CHANGED
|
@@ -95,6 +95,14 @@ first commit, they are `null`: that is provenance missing, not an error, and nev
|
|
|
95
95
|
result wins. `--param key=value` (repeatable) adds a parameter that makes a result its own slot, such
|
|
96
96
|
as `--param device=iphone-15`.
|
|
97
97
|
|
|
98
|
+
Without a CI run URL, layers recorded at one branch and commit group into one run, so repeating a local demo
|
|
99
|
+
looks like one execution. To keep invocations apart, name each one: `--run-id <id>` (or `ATLAS_RUN_ID`), with
|
|
100
|
+
`--run-title <title>` (or `ATLAS_RUN_TITLE`) for a human name and `--run-started-at` / `--run-ended-at` for when
|
|
101
|
+
it ran. Every shard or batch of one invocation passes the same id and is one run; the next invocation passes a new
|
|
102
|
+
one. A run recorded from a checkout with uncommitted changes keeps saying so beside its commit. The id only
|
|
103
|
+
groups: it is not part of which result a check fills or which wins, and a producer that never passes it keeps
|
|
104
|
+
the CI-run-URL, then `branch@sha`, grouping.
|
|
105
|
+
|
|
98
106
|
The time the checks ran comes from the report (CTRF `summary.stop`, a JUnit `timestamp`, a manifest's
|
|
99
107
|
`finishedAt`), or failing that from the report file's modification time, in milliseconds. Publish in
|
|
100
108
|
the same job that wrote the report and that is the time it ran. If you copy the report first, pass
|
package/docs/reference/cli.md
CHANGED
|
@@ -86,12 +86,14 @@ atlas report — layered evidence for any kind of spec
|
|
|
86
86
|
atlas report publish [--store <dir|s3://bucket/prefix>] [--producer <name>]
|
|
87
87
|
( --ctrf <file> | --junit <file> | --manifest <file> | --checks <file.json> )
|
|
88
88
|
[--environment <name>] [--branch <b>] [--sha <s>] [--run-url <u>]
|
|
89
|
+
[--run-id <id>] [--run-title <t>] [--run-started-at <iso|ms>] [--run-ended-at <iso|ms>]
|
|
89
90
|
[--spec <path> | --specs <dir>] [--upload-run]
|
|
90
91
|
[--param key=value]... [--kind <k>] [--title <t>] [--recorded-at <iso|ms>]
|
|
91
92
|
[--attach [role=]<file>]... [--label key=value]... [--link [name=]<url>]...
|
|
92
93
|
[--attach-root <dir>]... [--max-attachment-mb <n>] [--max-total-attachment-mb <n>]
|
|
93
94
|
atlas report stream [--store <dir|s3://…|https://atlas.example.com>] --producer <name> [--environment <name>]
|
|
94
95
|
[--branch <b>] [--sha <s>] [--run-url <u>] [--kind <k>] [--param key=value]...
|
|
96
|
+
[--run-id <id>] [--run-title <t>]
|
|
95
97
|
[--flush-ms 1000] [--batch 100] (checks as JSON lines on standard input)
|
|
96
98
|
atlas report status [--store <dir|s3://…|id=url>]... [--backends <module>]
|
|
97
99
|
[--specs <dir>] [--format text|json|markdown]
|
|
@@ -112,6 +114,11 @@ atlas report — layered evidence for any kind of spec
|
|
|
112
114
|
--specs <dir> link checks to the spec files in <dir> by path, or by the suite, class or
|
|
113
115
|
file name the report gives; also publishes the specs themselves, so the
|
|
114
116
|
node can show them without a checkout
|
|
117
|
+
--run-id <id> name this execution (a local or manual run with no CI run URL; also ATLAS_RUN_ID).
|
|
118
|
+
Layers that share an id are one run, however many shards or batches uploaded them;
|
|
119
|
+
another invocation at the same commit gets another id. Never part of what a check is
|
|
120
|
+
--run-title <t> a human name for the run (also ATLAS_RUN_TITLE); --run-started-at and --run-ended-at
|
|
121
|
+
record when it ran. Without --run-id a run is its CI run URL, else branch@sha
|
|
115
122
|
--upload-run with --manifest: also upload the run's screenshots and clips for the viewer
|
|
116
123
|
--attach <file> attach a file to the whole layer: a CI log, a coverage report, a trace archive
|
|
117
124
|
(screenshots, videos and traces of a single test go in its CTRF or checks file)
|
|
@@ -467,6 +467,8 @@ Send `format=markdown` (or `Accept: text/markdown`) for the same as a Markdown r
|
|
|
467
467
|
- `domain` (query): Only tests whose spec is in this domain (`(no spec)` for none). Repeatable or comma-separated.
|
|
468
468
|
- `spec` (query): Only tests of this spec path. Repeatable or comma-separated.
|
|
469
469
|
- `tag` (query): Only tests with this tag. Repeatable or comma-separated.
|
|
470
|
+
- `artifactTag` (query): Only tests with an attachment tagged this (`demo` and `#demo` are the same tag), not counting tags inherited from a check or run: the exact artifact query. `inherited=1` widens it to the tags inherited from the check and run. Repeatable or comma-separated.
|
|
471
|
+
- `inherited` (query): With `artifactTag`: `1` also matches the tags an attachment inherits from its check and run, so a recording without a tag of its own under a run tagged `demo` is found. Without it the query is exact.
|
|
470
472
|
- `run` (query): Only tests recorded in this run (a `key` from `/api/reports/runs`).
|
|
471
473
|
- `sort` (query): The order: `spec` (default: domain, spec, title), `title`, `recent`, `slowest`, `failures`, `flaky` or `runs`.
|
|
472
474
|
- `offset` (query): Skip this many matching tests.
|
|
@@ -496,6 +498,8 @@ Scoped to one project. The same project may be selected on the unprefixed API us
|
|
|
496
498
|
- `domain` (query): Only tests whose spec is in this domain (`(no spec)` for none). Repeatable or comma-separated.
|
|
497
499
|
- `spec` (query): Only tests of this spec path. Repeatable or comma-separated.
|
|
498
500
|
- `tag` (query): Only tests with this tag. Repeatable or comma-separated.
|
|
501
|
+
- `artifactTag` (query): Only tests with an attachment tagged this (`demo` and `#demo` are the same tag), not counting tags inherited from a check or run: the exact artifact query. `inherited=1` widens it to the tags inherited from the check and run. Repeatable or comma-separated.
|
|
502
|
+
- `inherited` (query): With `artifactTag`: `1` also matches the tags an attachment inherits from its check and run, so a recording without a tag of its own under a run tagged `demo` is found. Without it the query is exact.
|
|
499
503
|
- `run` (query): Only tests recorded in this run (a `key` from `/api/reports/runs`).
|
|
500
504
|
- `sort` (query): The order: `spec` (default: domain, spec, title), `title`, `recent`, `slowest`, `failures`, `flaky` or `runs`.
|
|
501
505
|
- `offset` (query): Skip this many matching tests.
|
|
@@ -564,6 +568,50 @@ Scoped to one project. The same project may be selected on the unprefixed API us
|
|
|
564
568
|
|
|
565
569
|
Responses: `200` The test and its history.; `304` Unchanged since the `If-None-Match` ETag.; `400` No `key`.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `404` No test in the report has that key.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
|
|
566
570
|
|
|
571
|
+
### Reviewer tags of attachments: every recorded edit
|
|
572
|
+
|
|
573
|
+
`GET /api/reports/artifact-tags` · role: read
|
|
574
|
+
|
|
575
|
+
The audit trail of tags that reviewers added to and removed from attachments after upload, oldest first. Each entry says which attachment (`target`), which normalized tags were added and removed, the name of the token that made the edit (`by`, never the token) and when (`atMs`). The layers themselves are never rewritten, so a producer's own `tags` on an attachment stay as uploaded; fold the entries (oldest first, the last edit of a tag wins) to get the reviewer tags that stand now. Tags are normalized: `#Demo` and `demo` are `demo`.
|
|
576
|
+
|
|
577
|
+
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
578
|
+
|
|
579
|
+
Responses: `200` The edits.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.
|
|
580
|
+
|
|
581
|
+
### Reviewer tags of attachments: every recorded edit
|
|
582
|
+
|
|
583
|
+
`GET /api/projects/{project}/reports/artifact-tags` · role: read
|
|
584
|
+
|
|
585
|
+
The audit trail of tags that reviewers added to and removed from attachments after upload, oldest first. Each entry says which attachment (`target`), which normalized tags were added and removed, the name of the token that made the edit (`by`, never the token) and when (`atMs`). The layers themselves are never rewritten, so a producer's own `tags` on an attachment stay as uploaded; fold the entries (oldest first, the last edit of a tag wins) to get the reviewer tags that stand now. Tags are normalized: `#Demo` and `demo` are `demo`.
|
|
586
|
+
|
|
587
|
+
Scoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.
|
|
588
|
+
|
|
589
|
+
- `project` (path, required): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
590
|
+
|
|
591
|
+
Responses: `200` The edits.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.
|
|
592
|
+
|
|
593
|
+
### Tag or untag one attachment after upload
|
|
594
|
+
|
|
595
|
+
`POST /api/reports/artifact-tags` · role: publish
|
|
596
|
+
|
|
597
|
+
Records one edit of the reviewer tags of one attachment, by the name of the publish (or admin) token that sends it. It adds an immutable entry to the audit trail and changes nothing else: not the layer, not the movie, not any result. The tags it removes are reviewer tags only; a producer's tags are part of the layer and stay. A reader-only token, a browser sign-in box and an anonymous reader are refused. Name the attachment as `artifactRef` does (`id:<id>`, else `key:<bundleKey>`, else `name:<name>`).
|
|
598
|
+
|
|
599
|
+
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
600
|
+
|
|
601
|
+
Responses: `201` The recorded entry.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `403` The token is known but its role may not do this.; `413` Request too large.; `422` Not a tag edit: a bad target, an empty or over-long tag, or nothing to change.
|
|
602
|
+
|
|
603
|
+
### Tag or untag one attachment after upload
|
|
604
|
+
|
|
605
|
+
`POST /api/projects/{project}/reports/artifact-tags` · role: publish
|
|
606
|
+
|
|
607
|
+
Records one edit of the reviewer tags of one attachment, by the name of the publish (or admin) token that sends it. It adds an immutable entry to the audit trail and changes nothing else: not the layer, not the movie, not any result. The tags it removes are reviewer tags only; a producer's tags are part of the layer and stay. A reader-only token, a browser sign-in box and an anonymous reader are refused. Name the attachment as `artifactRef` does (`id:<id>`, else `key:<bundleKey>`, else `name:<name>`).
|
|
608
|
+
|
|
609
|
+
Scoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.
|
|
610
|
+
|
|
611
|
+
- `project` (path, required): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
612
|
+
|
|
613
|
+
Responses: `201` The recorded entry.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `403` The token is known but its role may not do this.; `413` Request too large.; `422` Not a tag edit: a bad target, an empty or over-long tag, or nothing to change.
|
|
614
|
+
|
|
567
615
|
### The runs that recorded the report
|
|
568
616
|
|
|
569
617
|
`GET /api/reports/runs` · role: read
|