@sasjs/cli 6.8.0 → 6.8.2
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/build/commands/fs/fsCommand.d.ts +2 -2
- package/build/commands/fs/fsCommand.js +50 -15
- package/build/commands/fs/internal/executeCode.d.ts +6 -0
- package/build/commands/fs/internal/executeCode.js +6 -0
- package/build/commands/fs/spec/fsCommand.spec.js +120 -0
- package/build/commands/job/internal/execute/viya.js +60 -36
- package/build/commands/job/spec/execute.spec.js +117 -0
- package/build/utils/logcheck.d.ts +12 -1
- package/build/utils/logcheck.js +72 -35
- package/build/utils/spec/logcheck.spec.js +106 -0
- package/package.json +1 -1
|
@@ -7,6 +7,6 @@ export declare class FSCommand extends TargetCommand {
|
|
|
7
7
|
getOutputPath(): Promise<string>;
|
|
8
8
|
execute(): Promise<ReturnCode>;
|
|
9
9
|
compile(): Promise<ReturnCode>;
|
|
10
|
-
deploy(): Promise<ReturnCode.Success | ReturnCode.InternalError>;
|
|
11
|
-
sync(): Promise<ReturnCode.Success | ReturnCode.InternalError>;
|
|
10
|
+
deploy(): Promise<ReturnCode.Success | ReturnCode.InternalError | ReturnCode.LogCheckError>;
|
|
11
|
+
sync(): Promise<ReturnCode.Success | ReturnCode.InternalError | ReturnCode.LogCheckError>;
|
|
12
12
|
}
|
|
@@ -113,6 +113,7 @@ var utils_2 = require("@sasjs/utils");
|
|
|
113
113
|
// bundle that imported anything from the barrel (sasjs/utils#275, #283).
|
|
114
114
|
var fs_1 = require("@sasjs/utils/fs");
|
|
115
115
|
var executeCode_1 = require("./internal/executeCode");
|
|
116
|
+
var logcheck_1 = require("../../utils/logcheck");
|
|
116
117
|
var FsSubCommand;
|
|
117
118
|
(function (FsSubCommand) {
|
|
118
119
|
FsSubCommand["Compile"] = "compile";
|
|
@@ -123,7 +124,12 @@ var syntax = 'fs <subCommand> [options]';
|
|
|
123
124
|
var usage = 'Usage: sasjs fs <subCommand> [options]';
|
|
124
125
|
var description = 'Handles operations around file system synchronisation';
|
|
125
126
|
var parseOptions = {
|
|
126
|
-
output: { type: 'string', alias: 'o' }
|
|
127
|
+
output: { type: 'string', alias: 'o' },
|
|
128
|
+
logcheck: {
|
|
129
|
+
type: 'boolean',
|
|
130
|
+
default: undefined,
|
|
131
|
+
description: 'Check the SAS log this command received, and fail when logCheck in sasjsconfig.json says so. --no-logcheck switches both the report and the verdict off.'
|
|
132
|
+
}
|
|
127
133
|
};
|
|
128
134
|
var compileCommandSyntax = 'fs <subCommand> <local> [options]';
|
|
129
135
|
var compileCommandUsage = 'sasjs fs compile <localFolder> [options]';
|
|
@@ -243,6 +249,7 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
243
249
|
return __generator(this, function (_a) {
|
|
244
250
|
switch (_a.label) {
|
|
245
251
|
case 0:
|
|
252
|
+
(0, logcheck_1.setLogCheckMode)((0, logcheck_1.logCheckModeOf)(this.parsed.logcheck));
|
|
246
253
|
if (!(this.parsed.subCommand === FsSubCommand.Compile)) return [3 /*break*/, 2];
|
|
247
254
|
return [4 /*yield*/, this.compile()];
|
|
248
255
|
case 1: return [2 /*return*/, _a.sent()];
|
|
@@ -324,8 +331,24 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
324
331
|
var log = _b.log;
|
|
325
332
|
return __generator(this, function (_c) {
|
|
326
333
|
switch (_c.label) {
|
|
327
|
-
case 0: return [4 /*yield*/, (0, utils_1.saveLog)(log, path_1.default.join(outputFolder, 'deploy.log'), '', false)
|
|
334
|
+
case 0: return [4 /*yield*/, (0, utils_1.saveLog)(log, path_1.default.join(outputFolder, 'deploy.log'), '', false)
|
|
335
|
+
// After the log file is written: a deploy whose log fails the check is
|
|
336
|
+
// the deploy whose record someone needs to read. Fatal, because a
|
|
337
|
+
// deploy that aborted still has to fail the build - reporting it and
|
|
338
|
+
// then printing "successfully deployed" is the bug in sasjs/cli#1535.
|
|
339
|
+
];
|
|
328
340
|
case 1:
|
|
341
|
+
_c.sent();
|
|
342
|
+
// After the log file is written: a deploy whose log fails the check is
|
|
343
|
+
// the deploy whose record someone needs to read. Fatal, because a
|
|
344
|
+
// deploy that aborted still has to fail the build - reporting it and
|
|
345
|
+
// then printing "successfully deployed" is the bug in sasjs/cli#1535.
|
|
346
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(log, 'fs deploy', { fatal: true })];
|
|
347
|
+
case 2:
|
|
348
|
+
// After the log file is written: a deploy whose log fails the check is
|
|
349
|
+
// the deploy whose record someone needs to read. Fatal, because a
|
|
350
|
+
// deploy that aborted still has to fail the build - reporting it and
|
|
351
|
+
// then printing "successfully deployed" is the bug in sasjs/cli#1535.
|
|
329
352
|
_c.sent();
|
|
330
353
|
return [2 /*return*/];
|
|
331
354
|
}
|
|
@@ -337,6 +360,10 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
337
360
|
return [2 /*return*/, command_1.ReturnCode.Success];
|
|
338
361
|
case 7:
|
|
339
362
|
error_1 = _f.sent();
|
|
363
|
+
// the verdict the caller asked for, not an internal failure: it carries
|
|
364
|
+
// its own exit code and has already printed the report
|
|
365
|
+
if ((0, logcheck_1.isLogCheckFailure)(error_1))
|
|
366
|
+
return [2 /*return*/, command_1.ReturnCode.LogCheckError];
|
|
340
367
|
logPath = path_1.default.join(outputFolder, 'error.log');
|
|
341
368
|
return [4 /*yield*/, createErrorLogFile(error_1, logPath)];
|
|
342
369
|
case 8:
|
|
@@ -386,7 +413,7 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
386
413
|
outputFolder = _v.sent();
|
|
387
414
|
_v.label = 2;
|
|
388
415
|
case 2:
|
|
389
|
-
_v.trys.push([2,
|
|
416
|
+
_v.trys.push([2, 17, , 19]);
|
|
390
417
|
(_h = process.logger) === null || _h === void 0 ? void 0 : _h.info('generating program to get remote hash');
|
|
391
418
|
return [4 /*yield*/, (0, fs_1.generateProgramToGetRemoteHash)(obj.remote)];
|
|
392
419
|
case 3:
|
|
@@ -400,15 +427,18 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
400
427
|
log = (_v.sent()).log;
|
|
401
428
|
return [4 /*yield*/, (0, utils_1.saveLog)(log, path_1.default.join(outputFolder, 'getRemoteHash.log'), '', false)];
|
|
402
429
|
case 6:
|
|
430
|
+
_v.sent();
|
|
431
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(log, 'fs sync')];
|
|
432
|
+
case 7:
|
|
403
433
|
_v.sent();
|
|
404
434
|
(_k = process.logger) === null || _k === void 0 ? void 0 : _k.info('extracting hashes from log');
|
|
405
435
|
remoteHashes = (0, fs_1.extractHashArray)(log);
|
|
406
436
|
return [4 /*yield*/, createHashFile(JSON.stringify(remoteHashes, null, 2), path_1.default.join(outputFolder, 'hashesBeforeSync.json'))];
|
|
407
|
-
case
|
|
437
|
+
case 8:
|
|
408
438
|
_v.sent();
|
|
409
439
|
(_l = process.logger) === null || _l === void 0 ? void 0 : _l.info('creating the hash of local folder');
|
|
410
440
|
return [4 /*yield*/, (0, fs_1.getHash)((0, utils_2.getAbsolutePath)(obj.local, process.projectDir))];
|
|
411
|
-
case
|
|
441
|
+
case 9:
|
|
412
442
|
localHash = _v.sent();
|
|
413
443
|
remoteHashMap_1 = remoteHashes.reduce(function (map, item) {
|
|
414
444
|
var from = obj.remote.replace(/\//g, path_1.default.sep);
|
|
@@ -424,25 +454,28 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
424
454
|
(_o = process.logger) === null || _o === void 0 ? void 0 : _o.info('Extract differences from local and remote hash');
|
|
425
455
|
hashedDiff = (0, fs_1.compareHashes)(localHash, remoteHashMap_1);
|
|
426
456
|
return [4 /*yield*/, createHashFile(JSON.stringify(hashedDiff, null, 2), path_1.default.join(outputFolder, 'hashesDiff.json'))];
|
|
427
|
-
case
|
|
457
|
+
case 10:
|
|
428
458
|
_v.sent();
|
|
429
459
|
(_p = process.logger) === null || _p === void 0 ? void 0 : _p.info('generating program to sync differences');
|
|
430
460
|
return [4 /*yield*/, (0, fs_1.generateProgramToSyncHashDiff)(hashedDiff, obj.remote)];
|
|
431
|
-
case
|
|
461
|
+
case 11:
|
|
432
462
|
syncProgram = _v.sent();
|
|
433
463
|
return [4 /*yield*/, createProgramFile(syncProgram, path_1.default.join(outputFolder, 'syncProgram.sas'))];
|
|
434
|
-
case
|
|
464
|
+
case 12:
|
|
435
465
|
_v.sent();
|
|
436
466
|
(_q = process.logger) === null || _q === void 0 ? void 0 : _q.info('executing program to sync differences');
|
|
437
467
|
return [4 /*yield*/, (0, executeCode_1.executeCode)(target, syncProgram)];
|
|
438
|
-
case
|
|
468
|
+
case 13:
|
|
439
469
|
syncLog = (_v.sent()).log;
|
|
440
470
|
return [4 /*yield*/, (0, utils_1.saveLog)(syncLog, path_1.default.join(outputFolder, 'sync.log'), '', false)];
|
|
441
|
-
case
|
|
471
|
+
case 14:
|
|
472
|
+
_v.sent();
|
|
473
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(syncLog, 'fs sync')];
|
|
474
|
+
case 15:
|
|
442
475
|
_v.sent();
|
|
443
476
|
syncedHash = (0, fs_1.extractHashArray)(syncLog);
|
|
444
477
|
return [4 /*yield*/, createHashFile(JSON.stringify(syncedHash, null, 2), path_1.default.join(outputFolder, 'hashesAfterSync.json'))];
|
|
445
|
-
case
|
|
478
|
+
case 16:
|
|
446
479
|
_v.sent();
|
|
447
480
|
syncedHashMap = syncedHash.reduce(function (map, item) {
|
|
448
481
|
var from = obj.remote.replace(/\//g, path_1.default.sep);
|
|
@@ -472,16 +505,18 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
472
505
|
(_a = process.logger) === null || _a === void 0 ? void 0 : _a.log("* ".concat(item));
|
|
473
506
|
});
|
|
474
507
|
}
|
|
475
|
-
return [3 /*break*/,
|
|
476
|
-
case
|
|
508
|
+
return [3 /*break*/, 19];
|
|
509
|
+
case 17:
|
|
477
510
|
error_2 = _v.sent();
|
|
511
|
+
if ((0, logcheck_1.isLogCheckFailure)(error_2))
|
|
512
|
+
return [2 /*return*/, { value: command_1.ReturnCode.LogCheckError }];
|
|
478
513
|
logPath = path_1.default.join(outputFolder, 'error.log');
|
|
479
514
|
return [4 /*yield*/, createErrorLogFile(error_2, logPath)];
|
|
480
|
-
case
|
|
515
|
+
case 18:
|
|
481
516
|
_v.sent();
|
|
482
517
|
(_t = process.logger) === null || _t === void 0 ? void 0 : _t.error("An error has occurred. For more info see ".concat(logPath));
|
|
483
518
|
return [2 /*return*/, { value: command_1.ReturnCode.InternalError }];
|
|
484
|
-
case
|
|
519
|
+
case 19: return [2 /*return*/];
|
|
485
520
|
}
|
|
486
521
|
});
|
|
487
522
|
};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { Target } from '@sasjs/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Runs generated SAS and returns the log it came back with.
|
|
4
|
+
*
|
|
5
|
+
* The caller saves that log and then checks it - not here, because a check that
|
|
6
|
+
* throws before the log file is written is a failed run nobody can read.
|
|
7
|
+
*/
|
|
2
8
|
export declare const executeCode: (target: Target, code: string) => Promise<{
|
|
3
9
|
log: any;
|
|
4
10
|
}>;
|
|
@@ -39,6 +39,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.executeCode = void 0;
|
|
40
40
|
var utils_1 = require("@sasjs/utils");
|
|
41
41
|
var utils_2 = require("../../../utils/");
|
|
42
|
+
/**
|
|
43
|
+
* Runs generated SAS and returns the log it came back with.
|
|
44
|
+
*
|
|
45
|
+
* The caller saves that log and then checks it - not here, because a check that
|
|
46
|
+
* throws before the log file is written is a failed run nobody can read.
|
|
47
|
+
*/
|
|
42
48
|
var executeCode = function (target, code) { return __awaiter(void 0, void 0, void 0, function () {
|
|
43
49
|
return __generator(this, function (_a) {
|
|
44
50
|
switch (_a.label) {
|
|
@@ -103,6 +103,8 @@ var FileModule = __importStar(require("@sasjs/utils/file"));
|
|
|
103
103
|
var configUtils = __importStar(require("../../../utils/config"));
|
|
104
104
|
var setConstantsUtils = __importStar(require("../../../utils/setConstants"));
|
|
105
105
|
var executeCodeModule = __importStar(require("../internal/executeCode"));
|
|
106
|
+
var logcheckModule = __importStar(require("../../../utils/logcheck"));
|
|
107
|
+
var logcheck_1 = require("../../../utils/logcheck");
|
|
106
108
|
var defaultArgs = ['node', 'sasjs', 'fs'];
|
|
107
109
|
var target = new utils_1.Target({
|
|
108
110
|
name: 'test',
|
|
@@ -227,6 +229,28 @@ describe('FSCommand', function () {
|
|
|
227
229
|
}
|
|
228
230
|
});
|
|
229
231
|
}); });
|
|
232
|
+
it('should return the logcheck code when the deploy log fails the check', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
233
|
+
var returnCode;
|
|
234
|
+
return __generator(this, function (_a) {
|
|
235
|
+
switch (_a.label) {
|
|
236
|
+
case 0:
|
|
237
|
+
jest
|
|
238
|
+
.spyOn(logcheckModule, 'checkLog')
|
|
239
|
+
.mockImplementation(function () {
|
|
240
|
+
return Promise.reject(new logcheck_1.LogCheckFailure('2 errors'));
|
|
241
|
+
});
|
|
242
|
+
return [4 /*yield*/, executeCommandWrapper([
|
|
243
|
+
'deploy',
|
|
244
|
+
'localFolder',
|
|
245
|
+
'remoteFolder'
|
|
246
|
+
])];
|
|
247
|
+
case 1:
|
|
248
|
+
returnCode = _a.sent();
|
|
249
|
+
expect(returnCode).toEqual(command_1.ReturnCode.LogCheckError);
|
|
250
|
+
return [2 /*return*/];
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}); });
|
|
230
254
|
});
|
|
231
255
|
describe('sync', function () {
|
|
232
256
|
beforeEach(function () {
|
|
@@ -335,6 +359,102 @@ describe('FSCommand', function () {
|
|
|
335
359
|
}
|
|
336
360
|
});
|
|
337
361
|
}); });
|
|
362
|
+
it('should return the logcheck code when the sync log fails the check', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
363
|
+
var returnCode;
|
|
364
|
+
return __generator(this, function (_a) {
|
|
365
|
+
switch (_a.label) {
|
|
366
|
+
case 0:
|
|
367
|
+
jest
|
|
368
|
+
.spyOn(logcheckModule, 'checkLog')
|
|
369
|
+
.mockImplementation(function () {
|
|
370
|
+
return Promise.reject(new logcheck_1.LogCheckFailure('1 error'));
|
|
371
|
+
});
|
|
372
|
+
jest.spyOn(FSModule, 'extractHashArray').mockImplementationOnce(function () { return []; });
|
|
373
|
+
return [4 /*yield*/, executeCommandWrapper([
|
|
374
|
+
'sync',
|
|
375
|
+
'-l',
|
|
376
|
+
'localFolder',
|
|
377
|
+
'-r',
|
|
378
|
+
'remoteFolder'
|
|
379
|
+
])];
|
|
380
|
+
case 1:
|
|
381
|
+
returnCode = _a.sent();
|
|
382
|
+
expect(returnCode).toEqual(command_1.ReturnCode.LogCheckError);
|
|
383
|
+
return [2 /*return*/];
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}); });
|
|
387
|
+
});
|
|
388
|
+
describe('log check mode', function () {
|
|
389
|
+
beforeEach(function () {
|
|
390
|
+
process.logger = new utils_1.Logger(utils_1.LogLevel.Off);
|
|
391
|
+
jest.spyOn(process.logger, 'info');
|
|
392
|
+
setupMocks();
|
|
393
|
+
jest
|
|
394
|
+
.spyOn(FSModule, 'generateCompileProgram')
|
|
395
|
+
.mockImplementation(function () { return Promise.resolve(''); });
|
|
396
|
+
jest
|
|
397
|
+
.spyOn(FileModule, 'createFile')
|
|
398
|
+
.mockImplementation(function () { return Promise.resolve(); });
|
|
399
|
+
jest
|
|
400
|
+
.spyOn(executeCodeModule, 'executeCode')
|
|
401
|
+
.mockImplementation(function () { return Promise.resolve({ log: '' }); });
|
|
402
|
+
jest
|
|
403
|
+
.spyOn(logcheckModule, 'checkLog')
|
|
404
|
+
.mockImplementation(function () { return Promise.resolve(); });
|
|
405
|
+
});
|
|
406
|
+
it('should switch the log check on for --logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
407
|
+
var spy;
|
|
408
|
+
return __generator(this, function (_a) {
|
|
409
|
+
switch (_a.label) {
|
|
410
|
+
case 0:
|
|
411
|
+
spy = jest.spyOn(logcheckModule, 'setLogCheckMode');
|
|
412
|
+
return [4 /*yield*/, executeCommandWrapper([
|
|
413
|
+
'deploy',
|
|
414
|
+
'localFolder',
|
|
415
|
+
'remoteFolder',
|
|
416
|
+
'--logcheck'
|
|
417
|
+
])];
|
|
418
|
+
case 1:
|
|
419
|
+
_a.sent();
|
|
420
|
+
expect(spy).toHaveBeenCalledWith('on');
|
|
421
|
+
return [2 /*return*/];
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
}); });
|
|
425
|
+
it('should switch the log check off for --no-logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
426
|
+
var spy;
|
|
427
|
+
return __generator(this, function (_a) {
|
|
428
|
+
switch (_a.label) {
|
|
429
|
+
case 0:
|
|
430
|
+
spy = jest.spyOn(logcheckModule, 'setLogCheckMode');
|
|
431
|
+
return [4 /*yield*/, executeCommandWrapper([
|
|
432
|
+
'deploy',
|
|
433
|
+
'localFolder',
|
|
434
|
+
'remoteFolder',
|
|
435
|
+
'--no-logcheck'
|
|
436
|
+
])];
|
|
437
|
+
case 1:
|
|
438
|
+
_a.sent();
|
|
439
|
+
expect(spy).toHaveBeenCalledWith('off');
|
|
440
|
+
return [2 /*return*/];
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
}); });
|
|
444
|
+
it('should leave the log check on auto when no flag is given', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
445
|
+
var spy;
|
|
446
|
+
return __generator(this, function (_a) {
|
|
447
|
+
switch (_a.label) {
|
|
448
|
+
case 0:
|
|
449
|
+
spy = jest.spyOn(logcheckModule, 'setLogCheckMode');
|
|
450
|
+
return [4 /*yield*/, executeCommandWrapper(['deploy', 'localFolder', 'remoteFolder'])];
|
|
451
|
+
case 1:
|
|
452
|
+
_a.sent();
|
|
453
|
+
expect(spy).toHaveBeenCalledWith('auto');
|
|
454
|
+
return [2 /*return*/];
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
}); });
|
|
338
458
|
});
|
|
339
459
|
describe('invalid subCommand', function () {
|
|
340
460
|
it('should return ReturnCode.InvalidCommand when subCommand is unknown', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
@@ -68,7 +68,7 @@ var utils_4 = require("../utils");
|
|
|
68
68
|
*/
|
|
69
69
|
function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output, logFile, statusFile, ignoreWarnings, source, streamLog) {
|
|
70
70
|
return __awaiter(this, void 0, void 0, function () {
|
|
71
|
-
var pollOptions, result, startTime, contextName, macroVars, submittedJob, endTime, outputJson, currentDirPath, outputPath, folderPath, parentFolderPath, logObj, logUrl, logCount, logData,
|
|
71
|
+
var pollOptions, result, startTime, contextName, macroVars, submittedJob, endTime, wantsVerdict, outputJson, currentDirPath, outputPath, folderPath, parentFolderPath, error_1, logObj, logUrl, logCount, logData, error_2;
|
|
72
72
|
var _this = this;
|
|
73
73
|
var _a, _b;
|
|
74
74
|
return __generator(this, function (_c) {
|
|
@@ -176,15 +176,18 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
|
|
|
176
176
|
_c.sent();
|
|
177
177
|
_c.label = 8;
|
|
178
178
|
case 8:
|
|
179
|
-
if (!(submittedJob && submittedJob.links)) return [3 /*break*/,
|
|
179
|
+
if (!(submittedJob && submittedJob.links)) return [3 /*break*/, 28];
|
|
180
180
|
if (!result)
|
|
181
181
|
result = true;
|
|
182
|
-
|
|
183
|
-
_c.label = 9;
|
|
182
|
+
return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
|
|
184
183
|
case 9:
|
|
185
|
-
_c.
|
|
184
|
+
wantsVerdict = _c.sent();
|
|
185
|
+
if (!output) return [3 /*break*/, 18];
|
|
186
|
+
_c.label = 10;
|
|
187
|
+
case 10:
|
|
188
|
+
_c.trys.push([10, 17, , 18]);
|
|
186
189
|
outputJson = JSON.stringify(submittedJob, null, 2);
|
|
187
|
-
if (!(typeof output === 'string')) return [3 /*break*/,
|
|
190
|
+
if (!(typeof output === 'string')) return [3 /*break*/, 15];
|
|
188
191
|
currentDirPath = path_1.default.isAbsolute(output)
|
|
189
192
|
? ''
|
|
190
193
|
: process.projectDir;
|
|
@@ -195,50 +198,71 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
|
|
|
195
198
|
folderPath.pop();
|
|
196
199
|
parentFolderPath = folderPath.join(path_1.default.sep);
|
|
197
200
|
return [4 /*yield*/, (0, utils_2.folderExists)(parentFolderPath)];
|
|
198
|
-
case 10:
|
|
199
|
-
if (!!(_c.sent())) return [3 /*break*/, 12];
|
|
200
|
-
return [4 /*yield*/, (0, utils_2.createFolder)(parentFolderPath)];
|
|
201
201
|
case 11:
|
|
202
|
+
if (!!(_c.sent())) return [3 /*break*/, 13];
|
|
203
|
+
return [4 /*yield*/, (0, utils_2.createFolder)(parentFolderPath)];
|
|
204
|
+
case 12:
|
|
202
205
|
_c.sent();
|
|
203
|
-
_c.label =
|
|
204
|
-
case
|
|
205
|
-
case
|
|
206
|
+
_c.label = 13;
|
|
207
|
+
case 13: return [4 /*yield*/, (0, utils_2.createFile)(outputPath, outputJson)];
|
|
208
|
+
case 14:
|
|
206
209
|
_c.sent();
|
|
207
210
|
(0, displayResult_1.displaySuccess)("Output saved to: ".concat(outputPath));
|
|
208
|
-
return [3 /*break*/,
|
|
209
|
-
case
|
|
211
|
+
return [3 /*break*/, 16];
|
|
212
|
+
case 15:
|
|
210
213
|
if (output) {
|
|
211
214
|
// log job object to the console
|
|
212
215
|
;
|
|
213
216
|
(process.logger || console).log(outputJson);
|
|
214
217
|
}
|
|
215
|
-
_c.label =
|
|
216
|
-
case
|
|
217
|
-
|
|
218
|
+
_c.label = 16;
|
|
219
|
+
case 16:
|
|
220
|
+
result = submittedJob;
|
|
221
|
+
return [3 /*break*/, 18];
|
|
222
|
+
case 17:
|
|
223
|
+
error_1 = _c.sent();
|
|
224
|
+
// handle use case when submitted job can't be parsed
|
|
225
|
+
result = false;
|
|
226
|
+
(0, displayResult_1.displayError)(null, 'An error has occurred when parsing an output of the job.');
|
|
227
|
+
return [3 /*break*/, 18];
|
|
228
|
+
case 18:
|
|
229
|
+
if (!(!pollOptions.streamLog && (Boolean(logFile) || wantsVerdict))) return [3 /*break*/, 27];
|
|
218
230
|
logObj = submittedJob.links.find(function (link) { return link.rel === 'log' && link.method === 'GET'; });
|
|
219
|
-
if (!logObj) return [3 /*break*/,
|
|
231
|
+
if (!logObj) return [3 /*break*/, 26];
|
|
232
|
+
_c.label = 19;
|
|
233
|
+
case 19:
|
|
234
|
+
_c.trys.push([19, 24, , 25]);
|
|
220
235
|
logUrl = target.serverUrl + logObj.href;
|
|
221
236
|
logCount = submittedJob.logStatistics.lineCount;
|
|
222
237
|
return [4 /*yield*/, (0, fetchLogFileContent_1.fetchLogFileContent)(sasjs, authConfig.access_token, logUrl, logCount)];
|
|
223
|
-
case
|
|
238
|
+
case 20:
|
|
224
239
|
logData = _c.sent();
|
|
240
|
+
if (!logFile) return [3 /*break*/, 22];
|
|
225
241
|
return [4 /*yield*/, (0, utils_4.saveLog)(logData, logFile, jobPath)];
|
|
226
|
-
case
|
|
242
|
+
case 21:
|
|
227
243
|
_c.sent();
|
|
228
|
-
|
|
229
|
-
case
|
|
244
|
+
_c.label = 22;
|
|
245
|
+
case 22: return [4 /*yield*/, (0, logcheck_1.checkLog)(typeof logData === 'string' ? logData : (0, utils_1.parseLogLines)(logData), jobPath)];
|
|
246
|
+
case 23:
|
|
230
247
|
_c.sent();
|
|
231
|
-
|
|
232
|
-
case
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// handle use case when submitted job can't be parsed
|
|
248
|
+
return [3 /*break*/, 25];
|
|
249
|
+
case 24:
|
|
250
|
+
error_2 = _c.sent();
|
|
251
|
+
// the verdict the caller asked for, not a failed fetch
|
|
252
|
+
if ((0, logcheck_1.isLogCheckFailure)(error_2))
|
|
253
|
+
throw error_2;
|
|
238
254
|
result = false;
|
|
239
|
-
(0, displayResult_1.displayError)(null, 'An error has occurred when
|
|
240
|
-
return [3 /*break*/,
|
|
241
|
-
case
|
|
255
|
+
(0, displayResult_1.displayError)(null, 'An error has occurred when retrieving the job log.');
|
|
256
|
+
return [3 /*break*/, 25];
|
|
257
|
+
case 25: return [3 /*break*/, 27];
|
|
258
|
+
case 26:
|
|
259
|
+
// A log was asked for and the job carries none. Staying quiet here is
|
|
260
|
+
// the same "flag that does nothing" this block exists to remove: it
|
|
261
|
+
// reaches the end with the job's own status and exits 0.
|
|
262
|
+
result = false;
|
|
263
|
+
(0, displayResult_1.displayError)(null, 'An error has occurred: the job returned no log to check or save.');
|
|
264
|
+
_c.label = 27;
|
|
265
|
+
case 27:
|
|
242
266
|
// wait for job state and return status(0,1,2) based on it
|
|
243
267
|
if (waitForJob && submittedJob.state) {
|
|
244
268
|
switch (submittedJob.state) {
|
|
@@ -254,13 +278,13 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
|
|
|
254
278
|
break;
|
|
255
279
|
}
|
|
256
280
|
}
|
|
257
|
-
return [3 /*break*/,
|
|
258
|
-
case
|
|
281
|
+
return [3 /*break*/, 29];
|
|
282
|
+
case 28:
|
|
259
283
|
if (result) {
|
|
260
284
|
(0, utils_3.terminateProcess)(command_1.ReturnCode.InternalError);
|
|
261
285
|
}
|
|
262
|
-
_c.label =
|
|
263
|
-
case
|
|
286
|
+
_c.label = 29;
|
|
287
|
+
case 29:
|
|
264
288
|
(_b = process.logger) === null || _b === void 0 ? void 0 : _b.info("This job was executed in ".concat((endTime - startTime) / 1000, " seconds"));
|
|
265
289
|
return [2 /*return*/, result];
|
|
266
290
|
}
|
|
@@ -79,6 +79,11 @@ var path_1 = __importDefault(require("path"));
|
|
|
79
79
|
var utils_2 = require("../../../utils");
|
|
80
80
|
var utilsModule = __importStar(require("../../../utils/utils"));
|
|
81
81
|
var configUtils = __importStar(require("../../../utils/config"));
|
|
82
|
+
var logcheckModule = __importStar(require("../../../utils/logcheck"));
|
|
83
|
+
var fetchLogFileContentModule = __importStar(require("../../shared/fetchLogFileContent"));
|
|
84
|
+
var logcheck_1 = require("../../../utils/logcheck");
|
|
85
|
+
var jobUtils = __importStar(require("../internal/utils"));
|
|
86
|
+
var displayResultModule = __importStar(require("../../../utils/displayResult"));
|
|
82
87
|
var execute_1 = require("../internal/execute");
|
|
83
88
|
var mocks_1 = require("./mocks");
|
|
84
89
|
var sasjs = new node_1.default();
|
|
@@ -165,6 +170,118 @@ describe('executeJobViya', function () {
|
|
|
165
170
|
}
|
|
166
171
|
});
|
|
167
172
|
}); });
|
|
173
|
+
it('propagates a log-check failure instead of reporting a parse error', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
174
|
+
var failure;
|
|
175
|
+
return __generator(this, function (_a) {
|
|
176
|
+
switch (_a.label) {
|
|
177
|
+
case 0:
|
|
178
|
+
// a job whose log fails the check used to print the verdict and then exit 0,
|
|
179
|
+
// because this function's catch-all treated the failure as a bad output
|
|
180
|
+
jest.spyOn(sasjs, 'startComputeJob').mockResolvedValue({
|
|
181
|
+
job: {
|
|
182
|
+
state: 'completed',
|
|
183
|
+
links: [{ rel: 'log', method: 'GET', href: '/log' }],
|
|
184
|
+
logStatistics: { lineCount: 5 }
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
jest
|
|
188
|
+
.spyOn(fetchLogFileContentModule, 'fetchLogFileContent')
|
|
189
|
+
.mockResolvedValue('ERROR: deliberate');
|
|
190
|
+
failure = new logcheck_1.LogCheckFailure('SAS log check failed for test/job');
|
|
191
|
+
jest.spyOn(logcheckModule, 'checkLog').mockRejectedValue(failure);
|
|
192
|
+
return [4 /*yield*/, expect((0, execute_1.executeJobViya)(sasjs, mocks_1.mockAuthConfig, 'test/job', target, true, false, testLogsPath, undefined, false, undefined, false)).rejects.toBe(failure)];
|
|
193
|
+
case 1:
|
|
194
|
+
_a.sent();
|
|
195
|
+
return [2 /*return*/];
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}); });
|
|
199
|
+
it('fetches the log under --logcheck even when no log file was named', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
200
|
+
var saveSpy, fetchSpy, failure;
|
|
201
|
+
return __generator(this, function (_a) {
|
|
202
|
+
switch (_a.label) {
|
|
203
|
+
case 0:
|
|
204
|
+
// otherwise the flag prints nothing and exits 0, which reads as a clean log
|
|
205
|
+
jest.spyOn(logcheckModule, 'logCheckWantsVerdict').mockResolvedValue(true);
|
|
206
|
+
saveSpy = jest.spyOn(jobUtils, 'saveLog').mockResolvedValue(undefined);
|
|
207
|
+
jest.spyOn(sasjs, 'startComputeJob').mockResolvedValue({
|
|
208
|
+
job: {
|
|
209
|
+
state: 'completed',
|
|
210
|
+
links: [{ rel: 'log', method: 'GET', href: '/log' }],
|
|
211
|
+
logStatistics: { lineCount: 5 }
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
fetchSpy = jest
|
|
215
|
+
.spyOn(fetchLogFileContentModule, 'fetchLogFileContent')
|
|
216
|
+
.mockResolvedValue('ERROR: deliberate');
|
|
217
|
+
failure = new logcheck_1.LogCheckFailure('SAS log check failed for test/job');
|
|
218
|
+
jest.spyOn(logcheckModule, 'checkLog').mockRejectedValue(failure);
|
|
219
|
+
return [4 /*yield*/, expect((0, execute_1.executeJobViya)(sasjs, mocks_1.mockAuthConfig, 'test/job', target, true, false, undefined, undefined, false, undefined, false)).rejects.toBe(failure)];
|
|
220
|
+
case 1:
|
|
221
|
+
_a.sent();
|
|
222
|
+
expect(fetchSpy).toHaveBeenCalled();
|
|
223
|
+
// the log was fetched for the verdict, not asked for: writing it would
|
|
224
|
+
// leave a file in the project directory that nobody requested
|
|
225
|
+
expect(saveSpy).not.toHaveBeenCalled();
|
|
226
|
+
return [2 /*return*/];
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}); });
|
|
230
|
+
it('names the log fetch, not the job output, when the log cannot be retrieved', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
231
|
+
var checkSpy, displaySpy, result;
|
|
232
|
+
return __generator(this, function (_a) {
|
|
233
|
+
switch (_a.label) {
|
|
234
|
+
case 0:
|
|
235
|
+
jest.spyOn(logcheckModule, 'logCheckWantsVerdict').mockResolvedValue(true);
|
|
236
|
+
jest.spyOn(sasjs, 'startComputeJob').mockResolvedValue({
|
|
237
|
+
job: {
|
|
238
|
+
state: 'completed',
|
|
239
|
+
links: [{ rel: 'log', method: 'GET', href: '/log' }],
|
|
240
|
+
logStatistics: { lineCount: 5 }
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
jest
|
|
244
|
+
.spyOn(fetchLogFileContentModule, 'fetchLogFileContent')
|
|
245
|
+
.mockRejectedValue(new Error('502 Bad Gateway'));
|
|
246
|
+
checkSpy = jest.spyOn(logcheckModule, 'checkLog');
|
|
247
|
+
displaySpy = jest.spyOn(displayResultModule, 'displayError');
|
|
248
|
+
return [4 /*yield*/, (0, execute_1.executeJobViya)(sasjs, mocks_1.mockAuthConfig, 'test/job', target, false, false, undefined, undefined, false, undefined, false)];
|
|
249
|
+
case 1:
|
|
250
|
+
result = _a.sent();
|
|
251
|
+
expect(result).toBe(false);
|
|
252
|
+
expect(checkSpy).not.toHaveBeenCalled();
|
|
253
|
+
expect(displaySpy).toHaveBeenCalledWith(null, 'An error has occurred when retrieving the job log.');
|
|
254
|
+
return [2 /*return*/];
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}); });
|
|
258
|
+
it('fails a verdict asked for from a job that carries no log link', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
259
|
+
var fetchSpy, checkSpy, displaySpy, result;
|
|
260
|
+
return __generator(this, function (_a) {
|
|
261
|
+
switch (_a.label) {
|
|
262
|
+
case 0:
|
|
263
|
+
jest.spyOn(logcheckModule, 'logCheckWantsVerdict').mockResolvedValue(true);
|
|
264
|
+
jest.spyOn(sasjs, 'startComputeJob').mockResolvedValue({
|
|
265
|
+
job: {
|
|
266
|
+
state: 'completed',
|
|
267
|
+
links: [],
|
|
268
|
+
logStatistics: { lineCount: 0 }
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
fetchSpy = jest.spyOn(fetchLogFileContentModule, 'fetchLogFileContent');
|
|
272
|
+
checkSpy = jest.spyOn(logcheckModule, 'checkLog');
|
|
273
|
+
displaySpy = jest.spyOn(displayResultModule, 'displayError');
|
|
274
|
+
return [4 /*yield*/, (0, execute_1.executeJobViya)(sasjs, mocks_1.mockAuthConfig, 'test/job', target, false, false, undefined, undefined, false, undefined, false)];
|
|
275
|
+
case 1:
|
|
276
|
+
result = _a.sent();
|
|
277
|
+
expect(result).toBe(false);
|
|
278
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
279
|
+
expect(checkSpy).not.toHaveBeenCalled();
|
|
280
|
+
expect(displaySpy).toHaveBeenCalledWith(null, 'An error has occurred: the job returned no log to check or save.');
|
|
281
|
+
return [2 /*return*/];
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}); });
|
|
168
285
|
});
|
|
169
286
|
describe('executeJobSasjs', function () {
|
|
170
287
|
beforeEach(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
@@ -32,6 +32,15 @@ export declare class LogCheckFailure extends Error {
|
|
|
32
32
|
readonly logCheckFailure = true;
|
|
33
33
|
constructor(message: string);
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Whether a log reaching this command has to be fetched at all.
|
|
37
|
+
*
|
|
38
|
+
* Reporting is free once a command owns a log, but a command that never
|
|
39
|
+
* receives one - a Viya job run without `--log` - has to ask for it. It asks
|
|
40
|
+
* when the verdict counts: `--logcheck`, or `logCheck.autoCheck` in the project
|
|
41
|
+
* config. A check nobody asked for is not worth an extra request.
|
|
42
|
+
*/
|
|
43
|
+
export declare const logCheckWantsVerdict: () => Promise<boolean>;
|
|
35
44
|
export declare const isLogCheckFailure: (error: unknown) => error is LogCheckFailure;
|
|
36
45
|
/**
|
|
37
46
|
* Reports the issues in a log a command just received, and throws when the
|
|
@@ -41,4 +50,6 @@ export declare const isLogCheckFailure: (error: unknown) => error is LogCheckFai
|
|
|
41
50
|
* have their own success output, and a check that announces itself on every
|
|
42
51
|
* green run is noise that gets switched off.
|
|
43
52
|
*/
|
|
44
|
-
export declare const checkLog: (logText: string | undefined | null, label: string
|
|
53
|
+
export declare const checkLog: (logText: string | undefined | null, label: string, options?: {
|
|
54
|
+
fatal?: boolean;
|
|
55
|
+
}) => Promise<void>;
|
package/build/utils/logcheck.js
CHANGED
|
@@ -90,7 +90,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
90
90
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
91
91
|
};
|
|
92
92
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
93
|
-
exports.checkLog = exports.isLogCheckFailure = exports.LogCheckFailure = exports.logCheckModeOf = exports.getLogCheckMode = exports.setLogCheckMode = void 0;
|
|
93
|
+
exports.checkLog = exports.isLogCheckFailure = exports.logCheckWantsVerdict = exports.LogCheckFailure = exports.logCheckModeOf = exports.getLogCheckMode = exports.setLogCheckMode = void 0;
|
|
94
94
|
var logs_1 = require("@sasjs/utils/logs");
|
|
95
95
|
var chalk_1 = __importDefault(require("chalk"));
|
|
96
96
|
var collectLogs_1 = require("../commands/logcheck/collectLogs");
|
|
@@ -135,6 +135,36 @@ var LogCheckFailure = /** @class */ (function (_super) {
|
|
|
135
135
|
return LogCheckFailure;
|
|
136
136
|
}(Error));
|
|
137
137
|
exports.LogCheckFailure = LogCheckFailure;
|
|
138
|
+
/**
|
|
139
|
+
* Whether a log reaching this command has to be fetched at all.
|
|
140
|
+
*
|
|
141
|
+
* Reporting is free once a command owns a log, but a command that never
|
|
142
|
+
* receives one - a Viya job run without `--log` - has to ask for it. It asks
|
|
143
|
+
* when the verdict counts: `--logcheck`, or `logCheck.autoCheck` in the project
|
|
144
|
+
* config. A check nobody asked for is not worth an extra request.
|
|
145
|
+
*/
|
|
146
|
+
var logCheckWantsVerdict = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
147
|
+
var _a;
|
|
148
|
+
return __generator(this, function (_b) {
|
|
149
|
+
switch (_b.label) {
|
|
150
|
+
case 0:
|
|
151
|
+
if (mode === 'off')
|
|
152
|
+
return [2 /*return*/, false];
|
|
153
|
+
if (mode === 'on')
|
|
154
|
+
return [2 /*return*/, true];
|
|
155
|
+
_b.label = 1;
|
|
156
|
+
case 1:
|
|
157
|
+
_b.trys.push([1, 3, , 4]);
|
|
158
|
+
return [4 /*yield*/, (0, logCheckConfig_1.readLogCheckConfig)()];
|
|
159
|
+
case 2: return [2 /*return*/, (_b.sent()).autoCheck === true];
|
|
160
|
+
case 3:
|
|
161
|
+
_a = _b.sent();
|
|
162
|
+
return [2 /*return*/, false];
|
|
163
|
+
case 4: return [2 /*return*/];
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}); };
|
|
167
|
+
exports.logCheckWantsVerdict = logCheckWantsVerdict;
|
|
138
168
|
var isLogCheckFailure = function (error) {
|
|
139
169
|
return (error === null || error === void 0 ? void 0 : error.logCheckFailure) === true;
|
|
140
170
|
};
|
|
@@ -147,43 +177,50 @@ exports.isLogCheckFailure = isLogCheckFailure;
|
|
|
147
177
|
* have their own success output, and a check that announces itself on every
|
|
148
178
|
* green run is noise that gets switched off.
|
|
149
179
|
*/
|
|
150
|
-
var checkLog = function (
|
|
151
|
-
var
|
|
152
|
-
var
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
180
|
+
var checkLog = function (logText_1, label_1) {
|
|
181
|
+
var args_1 = [];
|
|
182
|
+
for (var _i = 2; _i < arguments.length; _i++) {
|
|
183
|
+
args_1[_i - 2] = arguments[_i];
|
|
184
|
+
}
|
|
185
|
+
return __awaiter(void 0, __spreadArray([logText_1, label_1], __read(args_1), false), void 0, function (logText, label, options) {
|
|
186
|
+
var config, error_1, _a, issues, suppressed, counts, failing, totals, failure;
|
|
187
|
+
var _b;
|
|
188
|
+
if (options === void 0) { options = {}; }
|
|
189
|
+
return __generator(this, function (_c) {
|
|
190
|
+
switch (_c.label) {
|
|
191
|
+
case 0:
|
|
192
|
+
if (mode === 'off' || !logText)
|
|
193
|
+
return [2 /*return*/];
|
|
194
|
+
_c.label = 1;
|
|
195
|
+
case 1:
|
|
196
|
+
_c.trys.push([1, 3, , 4]);
|
|
197
|
+
return [4 /*yield*/, (0, logCheckConfig_1.readLogCheckConfig)()];
|
|
198
|
+
case 2:
|
|
199
|
+
config = _c.sent();
|
|
200
|
+
return [3 /*break*/, 4];
|
|
201
|
+
case 3:
|
|
202
|
+
error_1 = _c.sent();
|
|
203
|
+
// An automatic check must never break the command it is riding on: a
|
|
204
|
+
// config it cannot read is worth a warning, not a failed run.
|
|
205
|
+
(_b = process.logger) === null || _b === void 0 ? void 0 : _b.warn("Skipping the SAS log check: ".concat(error_1.message));
|
|
157
206
|
return [2 /*return*/];
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return [2 /*return*/];
|
|
171
|
-
case 4:
|
|
172
|
-
_a = (0, collectLogs_1.partitionIssues)((0, logs_1.parseLog)(logText), config.ignoreCodes), issues = _a.issues, suppressed = _a.suppressed;
|
|
173
|
-
if (!issues.length)
|
|
207
|
+
case 4:
|
|
208
|
+
_a = (0, collectLogs_1.partitionIssues)((0, logs_1.parseLog)(logText), config.ignoreCodes), issues = _a.issues, suppressed = _a.suppressed;
|
|
209
|
+
if (!issues.length)
|
|
210
|
+
return [2 /*return*/];
|
|
211
|
+
counts = (0, collectLogs_1.countIssues)(issues);
|
|
212
|
+
failing = options.fatal === true || mode === 'on' || config.autoCheck === true;
|
|
213
|
+
totals = __assign(__assign({}, counts), { suppressed: suppressed.length, logs: 1, skipped: 0 });
|
|
214
|
+
failure = failing ? (0, processLogCheck_1.describeFailure)(totals, config) : undefined;
|
|
215
|
+
renderInline(label, issues, counts, suppressed.length, config, failure);
|
|
216
|
+
if (failure) {
|
|
217
|
+
throw new LogCheckFailure("SAS log check failed for ".concat(label, ": ").concat(failure));
|
|
218
|
+
}
|
|
174
219
|
return [2 /*return*/];
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
totals = __assign(__assign({}, counts), { suppressed: suppressed.length, logs: 1, skipped: 0 });
|
|
178
|
-
failure = failing ? (0, processLogCheck_1.describeFailure)(totals, config) : undefined;
|
|
179
|
-
renderInline(label, issues, counts, suppressed.length, config, failure);
|
|
180
|
-
if (failure) {
|
|
181
|
-
throw new LogCheckFailure("SAS log check failed for ".concat(label, ": ").concat(failure));
|
|
182
|
-
}
|
|
183
|
-
return [2 /*return*/];
|
|
184
|
-
}
|
|
220
|
+
}
|
|
221
|
+
});
|
|
185
222
|
});
|
|
186
|
-
}
|
|
223
|
+
};
|
|
187
224
|
exports.checkLog = checkLog;
|
|
188
225
|
/**
|
|
189
226
|
* The inline report: the heading, the issues, and the verdict when there is
|
|
@@ -208,6 +208,19 @@ describe('checkLog', function () {
|
|
|
208
208
|
}
|
|
209
209
|
});
|
|
210
210
|
}); });
|
|
211
|
+
it('fails a command that cannot succeed on a bad log, whatever the config says', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
212
|
+
return __generator(this, function (_a) {
|
|
213
|
+
switch (_a.label) {
|
|
214
|
+
case 0: return [4 /*yield*/, withProject()];
|
|
215
|
+
case 1:
|
|
216
|
+
_a.sent();
|
|
217
|
+
return [4 /*yield*/, expect((0, logcheck_1.checkLog)(logWithError, 'fs deploy', { fatal: true })).rejects.toBeInstanceOf(logcheck_1.LogCheckFailure)];
|
|
218
|
+
case 2:
|
|
219
|
+
_a.sent();
|
|
220
|
+
return [2 /*return*/];
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}); });
|
|
211
224
|
it('does not fail on a note under the default policy', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
212
225
|
return __generator(this, function (_a) {
|
|
213
226
|
switch (_a.label) {
|
|
@@ -269,6 +282,99 @@ describe('checkLog', function () {
|
|
|
269
282
|
});
|
|
270
283
|
}); });
|
|
271
284
|
});
|
|
285
|
+
describe('logCheckWantsVerdict', function () {
|
|
286
|
+
var originalProjectDir = process.projectDir;
|
|
287
|
+
var withProject = function (logCheck) { return __awaiter(void 0, void 0, void 0, function () {
|
|
288
|
+
var folder;
|
|
289
|
+
return __generator(this, function (_a) {
|
|
290
|
+
switch (_a.label) {
|
|
291
|
+
case 0: return [4 /*yield*/, tempFolder()];
|
|
292
|
+
case 1:
|
|
293
|
+
folder = _a.sent();
|
|
294
|
+
process.projectDir = folder;
|
|
295
|
+
if (!(logCheck !== undefined)) return [3 /*break*/, 3];
|
|
296
|
+
return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjsconfig.json'), JSON.stringify({ logCheck: logCheck }))];
|
|
297
|
+
case 2:
|
|
298
|
+
_a.sent();
|
|
299
|
+
_a.label = 3;
|
|
300
|
+
case 3: return [2 /*return*/];
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}); };
|
|
304
|
+
afterEach(function () {
|
|
305
|
+
process.projectDir = originalProjectDir;
|
|
306
|
+
(0, logcheck_1.setLogCheckMode)('auto');
|
|
307
|
+
});
|
|
308
|
+
it('is true under --logcheck and false under --no-logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
309
|
+
var _a, _b;
|
|
310
|
+
return __generator(this, function (_c) {
|
|
311
|
+
switch (_c.label) {
|
|
312
|
+
case 0: return [4 /*yield*/, withProject()];
|
|
313
|
+
case 1:
|
|
314
|
+
_c.sent();
|
|
315
|
+
(0, logcheck_1.setLogCheckMode)('on');
|
|
316
|
+
_a = expect;
|
|
317
|
+
return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
|
|
318
|
+
case 2:
|
|
319
|
+
_a.apply(void 0, [_c.sent()]).toBe(true);
|
|
320
|
+
(0, logcheck_1.setLogCheckMode)('off');
|
|
321
|
+
_b = expect;
|
|
322
|
+
return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
|
|
323
|
+
case 3:
|
|
324
|
+
_b.apply(void 0, [_c.sent()]).toBe(false);
|
|
325
|
+
return [2 /*return*/];
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}); });
|
|
329
|
+
it('follows autoCheck when the mode is auto', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
330
|
+
var _a, _b, _c;
|
|
331
|
+
return __generator(this, function (_d) {
|
|
332
|
+
switch (_d.label) {
|
|
333
|
+
case 0: return [4 /*yield*/, withProject({ autoCheck: true })];
|
|
334
|
+
case 1:
|
|
335
|
+
_d.sent();
|
|
336
|
+
_a = expect;
|
|
337
|
+
return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
|
|
338
|
+
case 2:
|
|
339
|
+
_a.apply(void 0, [_d.sent()]).toBe(true);
|
|
340
|
+
return [4 /*yield*/, withProject()];
|
|
341
|
+
case 3:
|
|
342
|
+
_d.sent();
|
|
343
|
+
_b = expect;
|
|
344
|
+
return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
|
|
345
|
+
case 4:
|
|
346
|
+
_b.apply(void 0, [_d.sent()]).toBe(false);
|
|
347
|
+
return [4 /*yield*/, withProject({ autoCheck: false })];
|
|
348
|
+
case 5:
|
|
349
|
+
_d.sent();
|
|
350
|
+
_c = expect;
|
|
351
|
+
return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
|
|
352
|
+
case 6:
|
|
353
|
+
_c.apply(void 0, [_d.sent()]).toBe(false);
|
|
354
|
+
return [2 /*return*/];
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}); });
|
|
358
|
+
it('asks for no log when the config cannot be read', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
359
|
+
var folder, _a;
|
|
360
|
+
return __generator(this, function (_b) {
|
|
361
|
+
switch (_b.label) {
|
|
362
|
+
case 0: return [4 /*yield*/, tempFolder()];
|
|
363
|
+
case 1:
|
|
364
|
+
folder = _b.sent();
|
|
365
|
+
process.projectDir = folder;
|
|
366
|
+
return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjsconfig.json'), '{ "logCheck": ')];
|
|
367
|
+
case 2:
|
|
368
|
+
_b.sent();
|
|
369
|
+
_a = expect;
|
|
370
|
+
return [4 /*yield*/, (0, logcheck_1.logCheckWantsVerdict)()];
|
|
371
|
+
case 3:
|
|
372
|
+
_a.apply(void 0, [_b.sent()]).toBe(false);
|
|
373
|
+
return [2 /*return*/];
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
}); });
|
|
377
|
+
});
|
|
272
378
|
describe('isLogCheckFailure', function () {
|
|
273
379
|
it('recognises only its own error', function () {
|
|
274
380
|
expect((0, logcheck_1.isLogCheckFailure)(new logcheck_1.LogCheckFailure('x'))).toBe(true);
|
package/package.json
CHANGED