@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.
@@ -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, 15, , 17]);
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 7:
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 8:
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 9:
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 10:
455
+ case 11:
432
456
  syncProgram = _v.sent();
433
457
  return [4 /*yield*/, createProgramFile(syncProgram, path_1.default.join(outputFolder, 'syncProgram.sas'))];
434
- case 11:
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 12:
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 13:
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 14:
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*/, 17];
476
- case 15:
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 16:
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 17: return [2 /*return*/];
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
@@ -123,5 +123,5 @@
123
123
  "ws": "8.21.0",
124
124
  "nanoid": "3.3.18"
125
125
  },
126
- "version": "6.8.0"
126
+ "version": "6.8.1"
127
127
  }