@sasjs/cli 6.8.0 → 6.8.1
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 +44 -15
- package/build/commands/fs/internal/executeCode.d.ts +6 -0
- package/build/commands/fs/internal/executeCode.js +6 -0
- package/build/commands/job/internal/execute/viya.js +5 -0
- package/build/commands/job/spec/execute.spec.js +29 -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,18 @@ 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
|
|
337
|
+
];
|
|
328
338
|
case 1:
|
|
339
|
+
_c.sent();
|
|
340
|
+
// after the log file is written: a deploy whose log fails the check is
|
|
341
|
+
// the deploy whose record someone needs to read
|
|
342
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(log, 'fs deploy')];
|
|
343
|
+
case 2:
|
|
344
|
+
// after the log file is written: a deploy whose log fails the check is
|
|
345
|
+
// the deploy whose record someone needs to read
|
|
329
346
|
_c.sent();
|
|
330
347
|
return [2 /*return*/];
|
|
331
348
|
}
|
|
@@ -337,6 +354,10 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
337
354
|
return [2 /*return*/, command_1.ReturnCode.Success];
|
|
338
355
|
case 7:
|
|
339
356
|
error_1 = _f.sent();
|
|
357
|
+
// the verdict the caller asked for, not an internal failure: it carries
|
|
358
|
+
// its own exit code and has already printed the report
|
|
359
|
+
if ((0, logcheck_1.isLogCheckFailure)(error_1))
|
|
360
|
+
return [2 /*return*/, command_1.ReturnCode.LogCheckError];
|
|
340
361
|
logPath = path_1.default.join(outputFolder, 'error.log');
|
|
341
362
|
return [4 /*yield*/, createErrorLogFile(error_1, logPath)];
|
|
342
363
|
case 8:
|
|
@@ -386,7 +407,7 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
386
407
|
outputFolder = _v.sent();
|
|
387
408
|
_v.label = 2;
|
|
388
409
|
case 2:
|
|
389
|
-
_v.trys.push([2,
|
|
410
|
+
_v.trys.push([2, 17, , 19]);
|
|
390
411
|
(_h = process.logger) === null || _h === void 0 ? void 0 : _h.info('generating program to get remote hash');
|
|
391
412
|
return [4 /*yield*/, (0, fs_1.generateProgramToGetRemoteHash)(obj.remote)];
|
|
392
413
|
case 3:
|
|
@@ -400,15 +421,18 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
400
421
|
log = (_v.sent()).log;
|
|
401
422
|
return [4 /*yield*/, (0, utils_1.saveLog)(log, path_1.default.join(outputFolder, 'getRemoteHash.log'), '', false)];
|
|
402
423
|
case 6:
|
|
424
|
+
_v.sent();
|
|
425
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(log, 'fs sync')];
|
|
426
|
+
case 7:
|
|
403
427
|
_v.sent();
|
|
404
428
|
(_k = process.logger) === null || _k === void 0 ? void 0 : _k.info('extracting hashes from log');
|
|
405
429
|
remoteHashes = (0, fs_1.extractHashArray)(log);
|
|
406
430
|
return [4 /*yield*/, createHashFile(JSON.stringify(remoteHashes, null, 2), path_1.default.join(outputFolder, 'hashesBeforeSync.json'))];
|
|
407
|
-
case
|
|
431
|
+
case 8:
|
|
408
432
|
_v.sent();
|
|
409
433
|
(_l = process.logger) === null || _l === void 0 ? void 0 : _l.info('creating the hash of local folder');
|
|
410
434
|
return [4 /*yield*/, (0, fs_1.getHash)((0, utils_2.getAbsolutePath)(obj.local, process.projectDir))];
|
|
411
|
-
case
|
|
435
|
+
case 9:
|
|
412
436
|
localHash = _v.sent();
|
|
413
437
|
remoteHashMap_1 = remoteHashes.reduce(function (map, item) {
|
|
414
438
|
var from = obj.remote.replace(/\//g, path_1.default.sep);
|
|
@@ -424,25 +448,28 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
424
448
|
(_o = process.logger) === null || _o === void 0 ? void 0 : _o.info('Extract differences from local and remote hash');
|
|
425
449
|
hashedDiff = (0, fs_1.compareHashes)(localHash, remoteHashMap_1);
|
|
426
450
|
return [4 /*yield*/, createHashFile(JSON.stringify(hashedDiff, null, 2), path_1.default.join(outputFolder, 'hashesDiff.json'))];
|
|
427
|
-
case
|
|
451
|
+
case 10:
|
|
428
452
|
_v.sent();
|
|
429
453
|
(_p = process.logger) === null || _p === void 0 ? void 0 : _p.info('generating program to sync differences');
|
|
430
454
|
return [4 /*yield*/, (0, fs_1.generateProgramToSyncHashDiff)(hashedDiff, obj.remote)];
|
|
431
|
-
case
|
|
455
|
+
case 11:
|
|
432
456
|
syncProgram = _v.sent();
|
|
433
457
|
return [4 /*yield*/, createProgramFile(syncProgram, path_1.default.join(outputFolder, 'syncProgram.sas'))];
|
|
434
|
-
case
|
|
458
|
+
case 12:
|
|
435
459
|
_v.sent();
|
|
436
460
|
(_q = process.logger) === null || _q === void 0 ? void 0 : _q.info('executing program to sync differences');
|
|
437
461
|
return [4 /*yield*/, (0, executeCode_1.executeCode)(target, syncProgram)];
|
|
438
|
-
case
|
|
462
|
+
case 13:
|
|
439
463
|
syncLog = (_v.sent()).log;
|
|
440
464
|
return [4 /*yield*/, (0, utils_1.saveLog)(syncLog, path_1.default.join(outputFolder, 'sync.log'), '', false)];
|
|
441
|
-
case
|
|
465
|
+
case 14:
|
|
466
|
+
_v.sent();
|
|
467
|
+
return [4 /*yield*/, (0, logcheck_1.checkLog)(syncLog, 'fs sync')];
|
|
468
|
+
case 15:
|
|
442
469
|
_v.sent();
|
|
443
470
|
syncedHash = (0, fs_1.extractHashArray)(syncLog);
|
|
444
471
|
return [4 /*yield*/, createHashFile(JSON.stringify(syncedHash, null, 2), path_1.default.join(outputFolder, 'hashesAfterSync.json'))];
|
|
445
|
-
case
|
|
472
|
+
case 16:
|
|
446
473
|
_v.sent();
|
|
447
474
|
syncedHashMap = syncedHash.reduce(function (map, item) {
|
|
448
475
|
var from = obj.remote.replace(/\//g, path_1.default.sep);
|
|
@@ -472,16 +499,18 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
472
499
|
(_a = process.logger) === null || _a === void 0 ? void 0 : _a.log("* ".concat(item));
|
|
473
500
|
});
|
|
474
501
|
}
|
|
475
|
-
return [3 /*break*/,
|
|
476
|
-
case
|
|
502
|
+
return [3 /*break*/, 19];
|
|
503
|
+
case 17:
|
|
477
504
|
error_2 = _v.sent();
|
|
505
|
+
if ((0, logcheck_1.isLogCheckFailure)(error_2))
|
|
506
|
+
return [2 /*return*/, { value: command_1.ReturnCode.LogCheckError }];
|
|
478
507
|
logPath = path_1.default.join(outputFolder, 'error.log');
|
|
479
508
|
return [4 /*yield*/, createErrorLogFile(error_2, logPath)];
|
|
480
|
-
case
|
|
509
|
+
case 18:
|
|
481
510
|
_v.sent();
|
|
482
511
|
(_t = process.logger) === null || _t === void 0 ? void 0 : _t.error("An error has occurred. For more info see ".concat(logPath));
|
|
483
512
|
return [2 /*return*/, { value: command_1.ReturnCode.InternalError }];
|
|
484
|
-
case
|
|
513
|
+
case 19: return [2 /*return*/];
|
|
485
514
|
}
|
|
486
515
|
});
|
|
487
516
|
};
|
|
@@ -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) {
|
|
@@ -234,6 +234,11 @@ function executeJobViya(sasjs, authConfig, jobPath, target, waitForJob, output,
|
|
|
234
234
|
return [3 /*break*/, 21];
|
|
235
235
|
case 20:
|
|
236
236
|
error_1 = _c.sent();
|
|
237
|
+
// A log-check failure is the verdict the caller asked for, not a parse
|
|
238
|
+
// error: swallowing it here printed "Logcheck failed" and then exited 0,
|
|
239
|
+
// which is the green pipeline the feature exists to prevent.
|
|
240
|
+
if ((0, logcheck_1.isLogCheckFailure)(error_1))
|
|
241
|
+
throw error_1;
|
|
237
242
|
// handle use case when submitted job can't be parsed
|
|
238
243
|
result = false;
|
|
239
244
|
(0, displayResult_1.displayError)(null, 'An error has occurred when parsing an output of the job.');
|
|
@@ -79,6 +79,9 @@ 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");
|
|
82
85
|
var execute_1 = require("../internal/execute");
|
|
83
86
|
var mocks_1 = require("./mocks");
|
|
84
87
|
var sasjs = new node_1.default();
|
|
@@ -165,6 +168,32 @@ describe('executeJobViya', function () {
|
|
|
165
168
|
}
|
|
166
169
|
});
|
|
167
170
|
}); });
|
|
171
|
+
it('propagates a log-check failure instead of reporting a parse error', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
172
|
+
var failure;
|
|
173
|
+
return __generator(this, function (_a) {
|
|
174
|
+
switch (_a.label) {
|
|
175
|
+
case 0:
|
|
176
|
+
// a job whose log fails the check used to print the verdict and then exit 0,
|
|
177
|
+
// because this function's catch-all treated the failure as a bad output
|
|
178
|
+
jest.spyOn(sasjs, 'startComputeJob').mockResolvedValue({
|
|
179
|
+
job: {
|
|
180
|
+
state: 'completed',
|
|
181
|
+
links: [{ rel: 'log', method: 'GET', href: '/log' }],
|
|
182
|
+
logStatistics: { lineCount: 5 }
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
jest
|
|
186
|
+
.spyOn(fetchLogFileContentModule, 'fetchLogFileContent')
|
|
187
|
+
.mockResolvedValue('ERROR: deliberate');
|
|
188
|
+
failure = new logcheck_1.LogCheckFailure('SAS log check failed for test/job');
|
|
189
|
+
jest.spyOn(logcheckModule, 'checkLog').mockRejectedValue(failure);
|
|
190
|
+
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)];
|
|
191
|
+
case 1:
|
|
192
|
+
_a.sent();
|
|
193
|
+
return [2 /*return*/];
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}); });
|
|
168
197
|
});
|
|
169
198
|
describe('executeJobSasjs', function () {
|
|
170
199
|
beforeEach(function () { return __awaiter(void 0, void 0, void 0, function () {
|
package/package.json
CHANGED