@sasjs/cli 6.6.0 → 6.7.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/README.md +2 -1
- package/build/commands/help/help.js +10 -0
- package/build/commands/index.d.ts +1 -0
- package/build/commands/index.js +3 -1
- package/build/commands/logcheck/collectLogs.d.ts +60 -0
- package/build/commands/logcheck/collectLogs.js +386 -0
- package/build/commands/logcheck/logCheckConfig.d.ts +72 -0
- package/build/commands/logcheck/logCheckConfig.js +283 -0
- package/build/commands/logcheck/logcheckCommand.d.ts +6 -0
- package/build/commands/logcheck/logcheckCommand.js +200 -0
- package/build/commands/logcheck/processLogCheck.d.ts +44 -0
- package/build/commands/logcheck/processLogCheck.js +242 -0
- package/build/commands/logcheck/renderLogCheck.d.ts +128 -0
- package/build/commands/logcheck/renderLogCheck.js +423 -0
- package/build/commands/logcheck/spec/collectLogs.spec.d.ts +1 -0
- package/build/commands/logcheck/spec/collectLogs.spec.js +477 -0
- package/build/commands/logcheck/spec/logCheckConfig.spec.d.ts +1 -0
- package/build/commands/logcheck/spec/logCheckConfig.spec.js +323 -0
- package/build/commands/logcheck/spec/logcheckCommand.spec.d.ts +1 -0
- package/build/commands/logcheck/spec/logcheckCommand.spec.js +360 -0
- package/build/commands/logcheck/spec/processLogCheck.spec.d.ts +1 -0
- package/build/commands/logcheck/spec/processLogCheck.spec.js +354 -0
- package/build/commands/logcheck/spec/renderLogCheck.spec.d.ts +1 -0
- package/build/commands/logcheck/spec/renderLogCheck.spec.js +426 -0
- package/build/commands/logcheck/types.d.ts +98 -0
- package/build/commands/logcheck/types.js +14 -0
- package/build/types/command/commandAliases.js +1 -0
- package/build/types/command/commandFactory.js +2 -0
- package/build/types/command/returnCode.d.ts +2 -1
- package/build/types/command/returnCode.js +4 -0
- package/package.json +3 -3
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
24
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var __values = (this && this.__values) || function(o) {
|
|
50
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
51
|
+
if (m) return m.call(o);
|
|
52
|
+
if (o && typeof o.length === "number") return {
|
|
53
|
+
next: function () {
|
|
54
|
+
if (o && i >= o.length) o = void 0;
|
|
55
|
+
return { value: o && o[i++], done: !o };
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
59
|
+
};
|
|
60
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
61
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
62
|
+
if (!m) return o;
|
|
63
|
+
var i = m.call(o), r, ar = [], e;
|
|
64
|
+
try {
|
|
65
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
66
|
+
}
|
|
67
|
+
catch (error) { e = { error: error }; }
|
|
68
|
+
finally {
|
|
69
|
+
try {
|
|
70
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
71
|
+
}
|
|
72
|
+
finally { if (e) throw e.error; }
|
|
73
|
+
}
|
|
74
|
+
return ar;
|
|
75
|
+
};
|
|
76
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
77
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
78
|
+
if (ar || !(i in from)) {
|
|
79
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
80
|
+
ar[i] = from[i];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
84
|
+
};
|
|
85
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
86
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
87
|
+
};
|
|
88
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89
|
+
exports.describeFailure = exports.sumTotals = exports.defaultInputPath = exports.processLogCheck = void 0;
|
|
90
|
+
var path_1 = __importDefault(require("path"));
|
|
91
|
+
var collectLogs_1 = require("./collectLogs");
|
|
92
|
+
var logCheckConfig_1 = require("./logCheckConfig");
|
|
93
|
+
var types_1 = require("./types");
|
|
94
|
+
/**
|
|
95
|
+
* Runs the check: expands the input, parses every log, applies the policy and
|
|
96
|
+
* says whether the run passes.
|
|
97
|
+
*
|
|
98
|
+
* The report is built here rather than in the renderer so that the three
|
|
99
|
+
* output formats cannot disagree about what was found - the summary is the
|
|
100
|
+
* single answer, and human, JSON and CI annotations are three ways of printing
|
|
101
|
+
* it.
|
|
102
|
+
*/
|
|
103
|
+
var processLogCheck = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
|
104
|
+
var inputPaths, searched, _c, files, missing, unreadable, skipped, toScan, files_1, files_1_1, file, displayPath, logs, scanned, totals;
|
|
105
|
+
var e_1, _d;
|
|
106
|
+
var _e = _b.paths, paths = _e === void 0 ? [] : _e, config = _b.config;
|
|
107
|
+
return __generator(this, function (_f) {
|
|
108
|
+
switch (_f.label) {
|
|
109
|
+
case 0:
|
|
110
|
+
inputPaths = paths.length ? paths : [(0, exports.defaultInputPath)(config)];
|
|
111
|
+
searched = inputPaths.map(function (inputPath) {
|
|
112
|
+
return (0, collectLogs_1.toDisplayPath)(path_1.default.resolve(inputPath));
|
|
113
|
+
});
|
|
114
|
+
return [4 /*yield*/, (0, collectLogs_1.collectLogFiles)(inputPaths)];
|
|
115
|
+
case 1:
|
|
116
|
+
_c = _f.sent(), files = _c.files, missing = _c.missing, unreadable = _c.unreadable;
|
|
117
|
+
skipped = [];
|
|
118
|
+
toScan = [];
|
|
119
|
+
try {
|
|
120
|
+
for (files_1 = __values(files), files_1_1 = files_1.next(); !files_1_1.done; files_1_1 = files_1.next()) {
|
|
121
|
+
file = files_1_1.value;
|
|
122
|
+
displayPath = (0, collectLogs_1.toDisplayPath)(file);
|
|
123
|
+
if ((0, logCheckConfig_1.isIgnoredPath)(displayPath, config.ignorePaths)) {
|
|
124
|
+
skipped.push({ displayPath: displayPath, reason: 'excluded by ignorePaths' });
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
toScan.push(file);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
131
|
+
finally {
|
|
132
|
+
try {
|
|
133
|
+
if (files_1_1 && !files_1_1.done && (_d = files_1.return)) _d.call(files_1);
|
|
134
|
+
}
|
|
135
|
+
finally { if (e_1) throw e_1.error; }
|
|
136
|
+
}
|
|
137
|
+
return [4 /*yield*/, Promise.all(toScan.map(function (file) { return (0, collectLogs_1.scanLogFile)(file, config); }))];
|
|
138
|
+
case 2:
|
|
139
|
+
logs = _f.sent();
|
|
140
|
+
scanned = logs.filter(function (log) { return !log.skippedReason; });
|
|
141
|
+
skipped.push.apply(skipped, __spreadArray([], __read(logs.filter(function (log) { return log.skippedReason; }).map(function (log) { return (0, collectLogs_1.toSkippedFile)(log); })), false));
|
|
142
|
+
totals = (0, exports.sumTotals)(scanned, files.length);
|
|
143
|
+
return [2 /*return*/, {
|
|
144
|
+
logs: logs,
|
|
145
|
+
skipped: sortSkipped(skipped),
|
|
146
|
+
missing: missing,
|
|
147
|
+
unreadable: unreadable,
|
|
148
|
+
usedDefaultInput: !paths.length,
|
|
149
|
+
searched: searched,
|
|
150
|
+
totals: totals,
|
|
151
|
+
failure: (0, exports.describeFailure)(totals, config, missing.length, unreadable.length)
|
|
152
|
+
}];
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}); };
|
|
156
|
+
exports.processLogCheck = processLogCheck;
|
|
157
|
+
/**
|
|
158
|
+
* Where a run with no arguments looks. The project's results folder is where
|
|
159
|
+
* `run`, `job run` and `test` leave their logs, which makes the bare command
|
|
160
|
+
* do the useful thing inside a project.
|
|
161
|
+
*/
|
|
162
|
+
var defaultInputPath = function (config) {
|
|
163
|
+
var projectDir = process.projectDir || process.cwd();
|
|
164
|
+
return path_1.default.resolve(projectDir, config.folder || logCheckConfig_1.DEFAULT_FOLDER);
|
|
165
|
+
};
|
|
166
|
+
exports.defaultInputPath = defaultInputPath;
|
|
167
|
+
/**
|
|
168
|
+
* Adds up what the run found. Skipped logs contribute nothing but are counted
|
|
169
|
+
* so the report can say how many files it passed over.
|
|
170
|
+
*/
|
|
171
|
+
var sumTotals = function (scanned, fileCount) {
|
|
172
|
+
var e_2, _a;
|
|
173
|
+
if (fileCount === void 0) { fileCount = scanned.length; }
|
|
174
|
+
var totals = __assign(__assign({}, (0, types_1.emptyCounts)()), { suppressed: 0, logs: scanned.length, skipped: fileCount - scanned.length });
|
|
175
|
+
try {
|
|
176
|
+
for (var scanned_1 = __values(scanned), scanned_1_1 = scanned_1.next(); !scanned_1_1.done; scanned_1_1 = scanned_1.next()) {
|
|
177
|
+
var log = scanned_1_1.value;
|
|
178
|
+
totals.errors += log.counts.errors;
|
|
179
|
+
totals.warnings += log.counts.warnings;
|
|
180
|
+
totals.notes += log.counts.notes;
|
|
181
|
+
totals.suppressed += log.suppressed.length;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
185
|
+
finally {
|
|
186
|
+
try {
|
|
187
|
+
if (scanned_1_1 && !scanned_1_1.done && (_a = scanned_1.return)) _a.call(scanned_1);
|
|
188
|
+
}
|
|
189
|
+
finally { if (e_2) throw e_2.error; }
|
|
190
|
+
}
|
|
191
|
+
return totals;
|
|
192
|
+
};
|
|
193
|
+
exports.sumTotals = sumTotals;
|
|
194
|
+
/**
|
|
195
|
+
* The verdict, as a phrase for the report.
|
|
196
|
+
*
|
|
197
|
+
* An ERROR always fails unless the policy is `never`; warnings and notes fail
|
|
198
|
+
* only when the policy asks for them, because notable notes are what legacy
|
|
199
|
+
* code emits in bulk and a gate that is red on its first run is a gate that
|
|
200
|
+
* gets commented out.
|
|
201
|
+
*
|
|
202
|
+
* A warnings budget is checked first and applies even under `never`: writing
|
|
203
|
+
* `maxWarnings: 0` is a specific instruction, and a specific instruction
|
|
204
|
+
* outranks a blanket one. `never` therefore means "do not fail on what you
|
|
205
|
+
* find", not "ignore the budget I also asked for".
|
|
206
|
+
*
|
|
207
|
+
* A path that could not be checked fails the run ahead of everything else: the
|
|
208
|
+
* answer to "is this log clean" is unknown, and unknown is not a pass.
|
|
209
|
+
*/
|
|
210
|
+
var describeFailure = function (totals, config, missingCount, unreadableCount) {
|
|
211
|
+
if (missingCount === void 0) { missingCount = 0; }
|
|
212
|
+
if (unreadableCount === void 0) { unreadableCount = 0; }
|
|
213
|
+
if (missingCount) {
|
|
214
|
+
return "".concat(missingCount, " path").concat(missingCount === 1 ? '' : 's', " not found");
|
|
215
|
+
}
|
|
216
|
+
if (unreadableCount) {
|
|
217
|
+
return "".concat(unreadableCount, " folder").concat(unreadableCount === 1 ? '' : 's', " could not be read");
|
|
218
|
+
}
|
|
219
|
+
if (config.maxWarnings !== undefined &&
|
|
220
|
+
totals.warnings > config.maxWarnings) {
|
|
221
|
+
return "".concat(plural(totals.warnings, 'warning'), " (budget ").concat(config.maxWarnings, ")");
|
|
222
|
+
}
|
|
223
|
+
if (config.failOn === 'never')
|
|
224
|
+
return undefined;
|
|
225
|
+
if (totals.errors)
|
|
226
|
+
return plural(totals.errors, 'error');
|
|
227
|
+
if (config.failOn === 'warning' || config.failOn === 'note') {
|
|
228
|
+
if (totals.warnings)
|
|
229
|
+
return plural(totals.warnings, 'warning');
|
|
230
|
+
}
|
|
231
|
+
if (config.failOn === 'note' && totals.notes) {
|
|
232
|
+
return plural(totals.notes, 'note');
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
235
|
+
};
|
|
236
|
+
exports.describeFailure = describeFailure;
|
|
237
|
+
var plural = function (count, noun) {
|
|
238
|
+
return "".concat(count, " ").concat(noun).concat(count === 1 ? '' : 's');
|
|
239
|
+
};
|
|
240
|
+
var sortSkipped = function (skipped) {
|
|
241
|
+
return __spreadArray([], __read(skipped), false).sort(function (a, b) { return a.displayPath.localeCompare(b.displayPath); });
|
|
242
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { LogIssue } from '@sasjs/utils/logs';
|
|
2
|
+
import { LogCheckConfig, LogCheckSummary, OutputFormat, ScannedLog } from './types';
|
|
3
|
+
/** Longest message shown in the human report before it is cut short. */
|
|
4
|
+
export declare const MAX_MESSAGE_WIDTH = 110;
|
|
5
|
+
/** An issue as a report states it: line and column counted from one. */
|
|
6
|
+
export interface ReportIssue {
|
|
7
|
+
line: number;
|
|
8
|
+
endLine: number;
|
|
9
|
+
column: number;
|
|
10
|
+
endColumn: number;
|
|
11
|
+
severity: LogIssue['severity'];
|
|
12
|
+
code?: string;
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
export interface RenderOptions {
|
|
16
|
+
format?: OutputFormat;
|
|
17
|
+
/** List every file that was skipped, rather than just counting them. */
|
|
18
|
+
verbose?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Prints the result in the requested format.
|
|
22
|
+
*
|
|
23
|
+
* The three formats are three views of one summary, never three scans: the
|
|
24
|
+
* terminal report, the JSON document and the CI annotations must all be
|
|
25
|
+
* describing the same parse, or they will disagree about the same log.
|
|
26
|
+
*/
|
|
27
|
+
export declare const renderLogCheck: (summary: LogCheckSummary, config: LogCheckConfig, options?: RenderOptions) => void;
|
|
28
|
+
/**
|
|
29
|
+
* The human report: what was checked, what was found, and the verdict.
|
|
30
|
+
*
|
|
31
|
+
* Colour carries severity and nothing else, so a reader scanning for trouble
|
|
32
|
+
* has one thing to look at, and the same report pasted into a ticket or
|
|
33
|
+
* piped into a log file without a terminal is still readable.
|
|
34
|
+
*/
|
|
35
|
+
export declare const renderHuman: (summary: LogCheckSummary, config: LogCheckConfig, verbose?: boolean) => void;
|
|
36
|
+
/** The one-line-per-file table: what each log carried, and whether it failed. */
|
|
37
|
+
export declare const fileSummary: (scanned: ScannedLog[], config?: LogCheckConfig) => string[];
|
|
38
|
+
/**
|
|
39
|
+
* Whether a single log breaks the policy.
|
|
40
|
+
*
|
|
41
|
+
* The warnings budget is deliberately not applied here: it is a budget for the
|
|
42
|
+
* whole run, so a log carrying three warnings is not the one that broke it.
|
|
43
|
+
*/
|
|
44
|
+
export declare const fileFails: (counts: ScannedLog["counts"], config: LogCheckConfig) => boolean;
|
|
45
|
+
/**
|
|
46
|
+
* The issues themselves, grouped under the log they came from.
|
|
47
|
+
*
|
|
48
|
+
* This is the shape a compiler prints and a reader already knows how to scan:
|
|
49
|
+
* the file once, then `line:column`, the severity, the SAS code where there is
|
|
50
|
+
* one, and the message.
|
|
51
|
+
*/
|
|
52
|
+
export declare const issueDetails: (scanned: ScannedLog[]) => string[];
|
|
53
|
+
/**
|
|
54
|
+
* Lays rows out in aligned columns, with the header underlined by dashes.
|
|
55
|
+
*
|
|
56
|
+
* Colour codes take no space on screen, so widths are measured on the text
|
|
57
|
+
* with the codes removed - otherwise every coloured cell is padded by the
|
|
58
|
+
* length of its own escape sequences and the table drifts.
|
|
59
|
+
*/
|
|
60
|
+
export declare const columns: (rows: string[][], head: string[], gap?: string, indent?: string) => string[];
|
|
61
|
+
/** `13:5` for a one-line message, `13-15:5` for one SAS wrapped. */
|
|
62
|
+
export declare const lineLabel: (issue: LogIssue) => string;
|
|
63
|
+
export declare const severityLabel: (severity: LogIssue["severity"]) => string;
|
|
64
|
+
/** Trims a message to the report width, on a word boundary where there is one. */
|
|
65
|
+
export declare const truncate: (message: string) => string;
|
|
66
|
+
/**
|
|
67
|
+
* The one-line answer: what was found, and over how many logs.
|
|
68
|
+
*
|
|
69
|
+
* It names the logs that were checked rather than the files that were not,
|
|
70
|
+
* because the number a reader compares against is the size of the result they
|
|
71
|
+
* have just looked at.
|
|
72
|
+
*/
|
|
73
|
+
export declare const summarise: (summary: LogCheckSummary, scannedCount: number) => string;
|
|
74
|
+
/**
|
|
75
|
+
* The machine report.
|
|
76
|
+
*
|
|
77
|
+
* Line and column are one-based here, as an editor and a CI annotation count
|
|
78
|
+
* them, while the parser works in indexes - the conversion happens once, at
|
|
79
|
+
* this boundary, so no consumer has to remember which convention it holds.
|
|
80
|
+
*/
|
|
81
|
+
export declare const toJson: (summary: LogCheckSummary, config: LogCheckConfig) => {
|
|
82
|
+
tool: string;
|
|
83
|
+
version: number;
|
|
84
|
+
config: {
|
|
85
|
+
failOn: import("./types").FailOn;
|
|
86
|
+
maxWarnings: number | null;
|
|
87
|
+
ignoreCodes: string[];
|
|
88
|
+
ignorePaths: string[];
|
|
89
|
+
};
|
|
90
|
+
summary: {
|
|
91
|
+
logs: number;
|
|
92
|
+
skipped: number;
|
|
93
|
+
errors: number;
|
|
94
|
+
warnings: number;
|
|
95
|
+
notes: number;
|
|
96
|
+
suppressed: number;
|
|
97
|
+
failed: boolean;
|
|
98
|
+
failure: string | null;
|
|
99
|
+
};
|
|
100
|
+
searched: string[];
|
|
101
|
+
missing: string[];
|
|
102
|
+
unreadable: {
|
|
103
|
+
folder: string;
|
|
104
|
+
reason: string;
|
|
105
|
+
}[];
|
|
106
|
+
logs: {
|
|
107
|
+
file: string;
|
|
108
|
+
errors: number;
|
|
109
|
+
warnings: number;
|
|
110
|
+
notes: number;
|
|
111
|
+
suppressed: number;
|
|
112
|
+
skipped: string | null;
|
|
113
|
+
issues: ReportIssue[];
|
|
114
|
+
}[];
|
|
115
|
+
skipped: {
|
|
116
|
+
file: string;
|
|
117
|
+
reason: string;
|
|
118
|
+
}[];
|
|
119
|
+
};
|
|
120
|
+
export declare const toReportIssue: (issue: LogIssue) => ReportIssue;
|
|
121
|
+
/**
|
|
122
|
+
* GitHub Actions annotations, one line per issue.
|
|
123
|
+
*
|
|
124
|
+
* The workflow commands escape three characters in the message and five in the
|
|
125
|
+
* properties; doing it here means SAS's own text arrives intact, commas and
|
|
126
|
+
* colons included.
|
|
127
|
+
*/
|
|
128
|
+
export declare const toAnnotations: (summary: LogCheckSummary) => string[];
|