@sasjs/cli 6.7.0 → 6.8.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.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/build/commands/flow/flowCommand.js +11 -1
  3. package/build/commands/flow/internal/executeFlow.js +44 -19
  4. package/build/commands/flow/internal/saveLog.d.ts +12 -1
  5. package/build/commands/flow/internal/saveLog.js +2 -2
  6. package/build/commands/flow/internal/spec/executeFlow.spec.js +44 -1
  7. package/build/commands/flow/spec/flowCommand.spec.js +44 -0
  8. package/build/commands/job/internal/execute/sas9.js +16 -13
  9. package/build/commands/job/internal/execute/sasjs.js +12 -9
  10. package/build/commands/job/internal/execute/viya.js +41 -36
  11. package/build/commands/job/jobCommand.js +13 -0
  12. package/build/commands/job/spec/executeLogOrder.spec.d.ts +1 -0
  13. package/build/commands/job/spec/executeLogOrder.spec.js +119 -0
  14. package/build/commands/job/spec/jobCommand.spec.js +32 -0
  15. package/build/commands/logcheck/logCheckConfig.d.ts +11 -0
  16. package/build/commands/logcheck/logCheckConfig.js +86 -26
  17. package/build/commands/logcheck/renderLogCheck.d.ts +1 -0
  18. package/build/commands/logcheck/renderLogCheck.js +1 -0
  19. package/build/commands/logcheck/spec/logCheckConfig.spec.js +63 -2
  20. package/build/commands/logcheck/types.d.ts +8 -0
  21. package/build/commands/request/request.js +66 -27
  22. package/build/commands/request/requestCommand.js +11 -1
  23. package/build/commands/request/spec/requestCommand.spec.js +40 -0
  24. package/build/commands/run/run.d.ts +1 -3
  25. package/build/commands/run/run.js +20 -5
  26. package/build/commands/run/runCommand.js +9 -0
  27. package/build/commands/run/spec/runCommand.spec.js +50 -0
  28. package/build/utils/index.d.ts +1 -0
  29. package/build/utils/index.js +1 -0
  30. package/build/utils/logcheck.d.ts +44 -0
  31. package/build/utils/logcheck.js +224 -0
  32. package/build/utils/spec/logcheck.spec.d.ts +1 -0
  33. package/build/utils/spec/logcheck.spec.js +297 -0
  34. package/package.json +1 -1
@@ -75,16 +75,21 @@ describe('parseLogCheckSection', function () {
75
75
  maxWarnings: 4,
76
76
  ignoreCodes: ['49-169'],
77
77
  ignorePaths: ['sasjsresults/legacy'],
78
- folder: 'logs'
78
+ folder: 'logs',
79
+ autoCheck: true
79
80
  });
80
81
  expect(config).toEqual({
81
82
  failOn: 'warning',
82
83
  maxWarnings: 4,
83
84
  ignoreCodes: ['49-169'],
84
85
  ignorePaths: ['sasjsresults/legacy'],
85
- folder: 'logs'
86
+ folder: 'logs',
87
+ autoCheck: true
86
88
  });
87
89
  });
90
+ it('refuses an autoCheck that is not a boolean', function () {
91
+ expect(function () { return (0, logCheckConfig_1.parseLogCheckSection)({ autoCheck: 'yes' }); }).toThrow('must be true or false');
92
+ });
88
93
  it('refuses a failOn that is not one of the four policies', function () {
89
94
  expect(function () { return (0, logCheckConfig_1.parseLogCheckSection)({ failOn: 'warnings' }); }).toThrow('must be one of error, warning, note, never');
90
95
  });
@@ -150,6 +155,54 @@ describe('readLogCheckConfig', function () {
150
155
  }
151
156
  });
152
157
  }); });
158
+ it('reads the config from the SASjs convention, sasjs/sasjsconfig.json', function () { return __awaiter(void 0, void 0, void 0, function () {
159
+ var folder, config;
160
+ return __generator(this, function (_a) {
161
+ switch (_a.label) {
162
+ case 0: return [4 /*yield*/, tempFolder()];
163
+ case 1:
164
+ folder = _a.sent();
165
+ process.projectDir = folder;
166
+ return [4 /*yield*/, fs_1.promises.mkdir(path_1.default.join(folder, 'sasjs'))];
167
+ case 2:
168
+ _a.sent();
169
+ return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjs', 'sasjsconfig.json'), JSON.stringify({ logCheck: { failOn: 'warning', autoCheck: true } }))];
170
+ case 3:
171
+ _a.sent();
172
+ return [4 /*yield*/, (0, logCheckConfig_1.readLogCheckConfig)()];
173
+ case 4:
174
+ config = _a.sent();
175
+ expect(config.failOn).toEqual('warning');
176
+ expect(config.autoCheck).toBeTrue();
177
+ return [2 /*return*/];
178
+ }
179
+ });
180
+ }); });
181
+ it('prefers sasjs/sasjsconfig.json when a root file also exists', function () { return __awaiter(void 0, void 0, void 0, function () {
182
+ var folder, _a;
183
+ return __generator(this, function (_b) {
184
+ switch (_b.label) {
185
+ case 0: return [4 /*yield*/, tempFolder()];
186
+ case 1:
187
+ folder = _b.sent();
188
+ process.projectDir = folder;
189
+ return [4 /*yield*/, fs_1.promises.mkdir(path_1.default.join(folder, 'sasjs'))];
190
+ case 2:
191
+ _b.sent();
192
+ return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjs', 'sasjsconfig.json'), JSON.stringify({ logCheck: { failOn: 'note' } }))];
193
+ case 3:
194
+ _b.sent();
195
+ return [4 /*yield*/, fs_1.promises.writeFile(path_1.default.join(folder, 'sasjsconfig.json'), JSON.stringify({ logCheck: { failOn: 'never' } }))];
196
+ case 4:
197
+ _b.sent();
198
+ _a = expect;
199
+ return [4 /*yield*/, (0, logCheckConfig_1.readLogCheckConfig)()];
200
+ case 5:
201
+ _a.apply(void 0, [(_b.sent()).failOn]).toEqual('note');
202
+ return [2 /*return*/];
203
+ }
204
+ });
205
+ }); });
153
206
  it('reads the section from the top level of the config', function () { return __awaiter(void 0, void 0, void 0, function () {
154
207
  var _a;
155
208
  return __generator(this, function (_b) {
@@ -227,6 +280,14 @@ describe('readLogCheckConfig', function () {
227
280
  });
228
281
  }); });
229
282
  });
283
+ describe('configCandidates', function () {
284
+ it('tries the SASjs convention before the project root', function () {
285
+ expect((0, logCheckConfig_1.configCandidates)(path_1.default.join('some', 'project'))).toEqual([
286
+ path_1.default.join('some', 'project', 'sasjs', 'sasjsconfig.json'),
287
+ path_1.default.join('some', 'project', 'sasjsconfig.json')
288
+ ]);
289
+ });
290
+ });
230
291
  describe('mergeConfig', function () {
231
292
  var base = __assign(__assign({}, types_1.defaultLogCheckConfig), { ignoreCodes: ['49-169'], ignorePaths: ['legacy'] });
232
293
  it('prefers the command line over the file', function () {
@@ -41,6 +41,14 @@ export interface LogCheckConfig {
41
41
  * against the project folder.
42
42
  */
43
43
  folder?: string;
44
+ /**
45
+ * Whether the commands that receive a SAS log (`run`, `request`, `job
46
+ * execute`, `flow execute`) fail on it as well as reporting it. Off by
47
+ * default: those commands' exit codes are already load-bearing in pipelines,
48
+ * and a job that prints a notable note is not a failed job. `--logcheck` and
49
+ * `--no-logcheck` override it per run.
50
+ */
51
+ autoCheck?: boolean;
44
52
  }
45
53
  export declare const defaultLogCheckConfig: LogCheckConfig;
46
54
  /** Counts of the issues a log carries, after suppression. */
@@ -35,6 +35,31 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
35
35
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
36
  }
37
37
  };
38
+ var __read = (this && this.__read) || function (o, n) {
39
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
40
+ if (!m) return o;
41
+ var i = m.call(o), r, ar = [], e;
42
+ try {
43
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
44
+ }
45
+ catch (error) { e = { error: error }; }
46
+ finally {
47
+ try {
48
+ if (r && !r.done && (m = i["return"])) m.call(i);
49
+ }
50
+ finally { if (e) throw e.error; }
51
+ }
52
+ return ar;
53
+ };
54
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
55
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
56
+ if (ar || !(i in from)) {
57
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
58
+ ar[i] = from[i];
59
+ }
60
+ }
61
+ return to.concat(ar || Array.prototype.slice.call(from));
62
+ };
38
63
  var __importDefault = (this && this.__importDefault) || function (mod) {
39
64
  return (mod && mod.__esModule) ? mod : { "default": mod };
40
65
  };
@@ -145,7 +170,7 @@ function runSasJob(target, isLocal, sasJobLocation, dataFilePath, configFilePath
145
170
  case 4:
146
171
  _a.sent();
147
172
  _a.label = 5;
148
- case 5: return [4 /*yield*/, saveLogFile(sasjs, sasJobLocation, logFile, jobPath)];
173
+ case 5: return [4 /*yield*/, saveLogFile(sasjs, sasJobLocation, logFile, jobPath, true)];
149
174
  case 6:
150
175
  _a.sent();
151
176
  result = true;
@@ -170,33 +195,47 @@ function runSasJob(target, isLocal, sasJobLocation, dataFilePath, configFilePath
170
195
  });
171
196
  });
172
197
  }
173
- var saveLogFile = function (sasjs, sasJobLocation, logFile, jobPath) { return __awaiter(void 0, void 0, void 0, function () {
174
- var sasRequests, currentRequestLog, logPath, timestamp, filename;
175
- return __generator(this, function (_a) {
176
- switch (_a.label) {
177
- case 0:
178
- sasRequests = sasjs.getSasRequests();
179
- currentRequestLog = sasRequests.find(function (x) { return x.serviceLink === sasJobLocation; });
180
- if (!(currentRequestLog && jobPath)) return [3 /*break*/, 4];
181
- if (!!logFile) return [3 /*break*/, 2];
182
- return [4 /*yield*/, (0, utils_2.isSASjsProject)()];
183
- case 1:
184
- logPath = (_a.sent())
185
- ? path_1.default.join(process.sasjsConstants.buildDestinationResultsFolder, 'requests')
186
- : process.projectDir;
187
- timestamp = (0, utils_1.generateTimestamp)();
188
- filename = sasJobLocation.split('/').pop() // always use forward slash in SAS Drive
189
- ;
190
- logFile = path_1.default.join(logPath, "".concat(filename, "-").concat(timestamp, ".log"));
191
- _a.label = 2;
192
- case 2: return [4 /*yield*/, (0, utils_2.saveLog)(currentRequestLog.logFile, logFile, jobPath || '', false)];
193
- case 3:
194
- _a.sent();
195
- _a.label = 4;
196
- case 4: return [2 /*return*/];
197
- }
198
+ var saveLogFile = function (sasjs_1, sasJobLocation_1, logFile_1, jobPath_1) {
199
+ var args_1 = [];
200
+ for (var _i = 4; _i < arguments.length; _i++) {
201
+ args_1[_i - 4] = arguments[_i];
202
+ }
203
+ return __awaiter(void 0, __spreadArray([sasjs_1, sasJobLocation_1, logFile_1, jobPath_1], __read(args_1), false), void 0, function (sasjs, sasJobLocation, logFile, jobPath, check) {
204
+ var sasRequests, currentRequestLog, logPath, timestamp, filename;
205
+ if (check === void 0) { check = false; }
206
+ return __generator(this, function (_a) {
207
+ switch (_a.label) {
208
+ case 0:
209
+ sasRequests = sasjs.getSasRequests();
210
+ currentRequestLog = sasRequests.find(function (x) { return x.serviceLink === sasJobLocation; });
211
+ if (!(currentRequestLog && jobPath)) return [3 /*break*/, 5];
212
+ if (!!logFile) return [3 /*break*/, 2];
213
+ return [4 /*yield*/, (0, utils_2.isSASjsProject)()];
214
+ case 1:
215
+ logPath = (_a.sent())
216
+ ? path_1.default.join(process.sasjsConstants.buildDestinationResultsFolder, 'requests')
217
+ : process.projectDir;
218
+ timestamp = (0, utils_1.generateTimestamp)();
219
+ filename = sasJobLocation.split('/').pop() // always use forward slash in SAS Drive
220
+ ;
221
+ logFile = path_1.default.join(logPath, "".concat(filename, "-").concat(timestamp, ".log"));
222
+ _a.label = 2;
223
+ case 2: return [4 /*yield*/, (0, utils_2.saveLog)(currentRequestLog.logFile, logFile, jobPath || '', false)
224
+ // after the write, not before: a request that fails the check is the one
225
+ // whose log is wanted on disk
226
+ ];
227
+ case 3:
228
+ _a.sent();
229
+ if (!check) return [3 /*break*/, 5];
230
+ return [4 /*yield*/, (0, utils_2.checkLog)(currentRequestLog.logFile, sasJobLocation)];
231
+ case 4:
232
+ _a.sent();
233
+ _a.label = 5;
234
+ case 5: return [2 /*return*/];
235
+ }
236
+ });
198
237
  });
199
- }); };
238
+ };
200
239
  /**
201
240
  * Writes output to the file
202
241
  * @param outputPathParam path to output file
@@ -58,6 +58,7 @@ var targetCommand_1 = require("../../types/command/targetCommand");
58
58
  var utils_2 = require("../../utils");
59
59
  var request_1 = require("./request");
60
60
  var getLogFilePath_1 = require("../../utils/getLogFilePath");
61
+ var logcheck_1 = require("../../utils/logcheck");
61
62
  var syntax = 'request <sasProgramPath> [options]';
62
63
  var usage = 'sasjs request <sasProgramPath> --data <path-to-datafile> --config <path-to-configfile> --target <target-name> --output <path-to-outputfile> --log <path-to-logfile>';
63
64
  var description = "Lets the user run a SAS job against a specified target.\n" +
@@ -104,6 +105,11 @@ var RequestCommand = /** @class */ (function (_super) {
104
105
  type: 'string',
105
106
  alias: 'o',
106
107
  description: 'path where output of the finished job execution will be saved.'
108
+ },
109
+ logcheck: {
110
+ type: 'boolean',
111
+ default: undefined,
112
+ description: 'Check the SAS log this request received, and fail when logCheck in sasjsconfig.json says so. --no-logcheck switches both the report and the verdict off.'
107
113
  }
108
114
  },
109
115
  syntax: syntax,
@@ -117,7 +123,9 @@ var RequestCommand = /** @class */ (function (_super) {
117
123
  var _a, target, isLocal, jobPath, log, output, authConfig, _b, e_1, _c, sasProgramPath, data, config;
118
124
  return __generator(this, function (_d) {
119
125
  switch (_d.label) {
120
- case 0: return [4 /*yield*/, this.getTargetInfo()];
126
+ case 0:
127
+ (0, logcheck_1.setLogCheckMode)((0, logcheck_1.logCheckModeOf)(this.parsed.logcheck));
128
+ return [4 /*yield*/, this.getTargetInfo()];
121
129
  case 1:
122
130
  _a = _d.sent(), target = _a.target, isLocal = _a.isLocal;
123
131
  jobPath = (0, utils_2.prefixAppLoc)(target.appLoc, process.currentDir);
@@ -152,6 +160,8 @@ var RequestCommand = /** @class */ (function (_super) {
152
160
  return command_1.ReturnCode.Success;
153
161
  })
154
162
  .catch(function (err) {
163
+ if ((0, logcheck_1.isLogCheckFailure)(err))
164
+ return command_1.ReturnCode.LogCheckError;
155
165
  (0, utils_2.displayError)(err, 'An error has occurred when running your SAS job.');
156
166
  return command_1.ReturnCode.InternalError;
157
167
  })];
@@ -100,6 +100,7 @@ var utils_1 = require("@sasjs/utils");
100
100
  var configUtils = __importStar(require("../../../utils/config"));
101
101
  var setConstantsUtils = __importStar(require("../../../utils/setConstants"));
102
102
  var command_1 = require("../../../types/command");
103
+ var logcheck_1 = require("../../../utils/logcheck");
103
104
  var defaultArgs = ['node', 'sasjs'];
104
105
  var target = new utils_1.Target({
105
106
  name: 'test',
@@ -116,6 +117,45 @@ describe('RequestCommand', function () {
116
117
  beforeEach(function () {
117
118
  setupMocks();
118
119
  });
120
+ afterEach(function () { return (0, logcheck_1.setLogCheckMode)('auto'); });
121
+ it('should return the logcheck code when the log check failed the request', function () { return __awaiter(void 0, void 0, void 0, function () {
122
+ var command, _a;
123
+ return __generator(this, function (_b) {
124
+ switch (_b.label) {
125
+ case 0:
126
+ jest
127
+ .spyOn(requestModule, 'runSasJob')
128
+ .mockImplementation(function () { return Promise.reject(new logcheck_1.LogCheckFailure('1 error')); });
129
+ command = new requestCommand_1.RequestCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [
130
+ 'request',
131
+ sasProgramPath
132
+ ], false));
133
+ _a = expect;
134
+ return [4 /*yield*/, command.execute()];
135
+ case 1:
136
+ _a.apply(void 0, [_b.sent()]).toEqual(command_1.ReturnCode.LogCheckError);
137
+ return [2 /*return*/];
138
+ }
139
+ });
140
+ }); });
141
+ it('should switch the log check off for --no-logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
142
+ var command;
143
+ return __generator(this, function (_a) {
144
+ switch (_a.label) {
145
+ case 0:
146
+ command = new requestCommand_1.RequestCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [
147
+ 'request',
148
+ sasProgramPath,
149
+ '--no-logcheck'
150
+ ], false));
151
+ return [4 /*yield*/, command.execute()];
152
+ case 1:
153
+ _a.sent();
154
+ expect((0, logcheck_1.getLogCheckMode)()).toEqual('off');
155
+ return [2 /*return*/];
156
+ }
157
+ });
158
+ }); });
119
159
  it('should parse sasjs request command', function () { return __awaiter(void 0, void 0, void 0, function () {
120
160
  var args, command, targetInfo;
121
161
  return __generator(this, function (_a) {
@@ -6,6 +6,4 @@ import { Target } from '@sasjs/utils';
6
6
  * @param {boolean} compile - compiles sas file present at 'filePath' before running code.
7
7
  * @param {string} logFile - (Optional) Path to log file.
8
8
  */
9
- export declare function runSasCode(target: Target, filePath: string, compile?: boolean, logFile?: string, source?: string): Promise<{
10
- log: any;
11
- }>;
9
+ export declare function runSasCode(target: Target, filePath: string, compile?: boolean, logFile?: string, source?: string): Promise<any>;
@@ -74,6 +74,7 @@ var utils_2 = require("../../utils/");
74
74
  var axios_1 = __importDefault(require("axios"));
75
75
  var saveLog_1 = require("../../utils/saveLog");
76
76
  var parseSourceFile_1 = require("../../utils/parseSourceFile");
77
+ var logcheck_1 = require("../../utils/logcheck");
77
78
  /**
78
79
  * Runs SAS code from a given file on the specified target.
79
80
  * @param {Target} target - the target to run the SAS code on.
@@ -83,7 +84,7 @@ var parseSourceFile_1 = require("../../utils/parseSourceFile");
83
84
  */
84
85
  function runSasCode(target_1, filePath_1) {
85
86
  return __awaiter(this, arguments, void 0, function (target, filePath, compile, logFile, source) {
86
- var isTempFile, sasFilePath, sasFileContent, linesToExecute, macroVars, macroVarStatements, _a;
87
+ var isTempFile, sasFilePath, sasFileContent, linesToExecute, macroVars, macroVarStatements, result, _a;
87
88
  var _b;
88
89
  var _c;
89
90
  if (compile === void 0) { compile = false; }
@@ -137,12 +138,26 @@ function runSasCode(target_1, filePath_1) {
137
138
  }
138
139
  return [3 /*break*/, 17];
139
140
  case 11: return [4 /*yield*/, executeOnSasViya(filePath, target, linesToExecute, logFile)];
140
- case 12: return [2 /*return*/, _d.sent()];
141
+ case 12:
142
+ result = _d.sent();
143
+ return [3 /*break*/, 17];
141
144
  case 13: return [4 /*yield*/, executeOnSas9(target, linesToExecute, logFile)];
142
- case 14: return [2 /*return*/, _d.sent()];
145
+ case 14:
146
+ result = _d.sent();
147
+ return [3 /*break*/, 17];
143
148
  case 15: return [4 /*yield*/, executeOnSasJS(filePath, target, linesToExecute, logFile)];
144
- case 16: return [2 /*return*/, _d.sent()];
145
- case 17: return [2 /*return*/];
149
+ case 16:
150
+ result = _d.sent();
151
+ _d.label = 17;
152
+ case 17:
153
+ // Only a run that claims to have worked is worth checking: an executor that
154
+ // threw has already written the log it failed on, truncated at the failure.
155
+ return [4 /*yield*/, (0, logcheck_1.checkLog)(result === null || result === void 0 ? void 0 : result.log, path_1.default.basename(filePath))];
156
+ case 18:
157
+ // Only a run that claims to have worked is worth checking: an executor that
158
+ // threw has already written the log it failed on, truncated at the failure.
159
+ _d.sent();
160
+ return [2 /*return*/, result];
146
161
  }
147
162
  });
148
163
  });
@@ -55,6 +55,7 @@ exports.RunCommand = void 0;
55
55
  var command_1 = require("../../types/command");
56
56
  var targetCommand_1 = require("../../types/command/targetCommand");
57
57
  var utils_1 = require("../../utils");
58
+ var logcheck_1 = require("../../utils/logcheck");
58
59
  var run_1 = require("./run");
59
60
  var syntax = 'run <sasFilePath> [options]';
60
61
  var usage = 'sasjs run <sasFilePath> --target <target-name>';
@@ -103,6 +104,11 @@ var RunCommand = /** @class */ (function (_super) {
103
104
  type: 'string',
104
105
  alias: 's',
105
106
  description: 'Path to an input JSON containing job variables.'
107
+ },
108
+ logcheck: {
109
+ type: 'boolean',
110
+ default: undefined,
111
+ 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.'
106
112
  }
107
113
  },
108
114
  syntax: syntax,
@@ -120,11 +126,14 @@ var RunCommand = /** @class */ (function (_super) {
120
126
  case 1:
121
127
  target = (_b.sent()).target;
122
128
  _a = this.parsed, sasFilePath = _a.sasFilePath, compile = _a.compile, log = _a.log, source = _a.source;
129
+ (0, logcheck_1.setLogCheckMode)((0, logcheck_1.logCheckModeOf)(this.parsed.logcheck));
123
130
  return [4 /*yield*/, (0, run_1.runSasCode)(target, sasFilePath, compile, log, source)
124
131
  .then(function () {
125
132
  return command_1.ReturnCode.Success;
126
133
  })
127
134
  .catch(function (err) {
135
+ if ((0, logcheck_1.isLogCheckFailure)(err))
136
+ return command_1.ReturnCode.LogCheckError;
128
137
  (0, utils_1.displayError)(err, 'An error has occurred when running your SAS code.');
129
138
  return command_1.ReturnCode.InternalError;
130
139
  })];
@@ -100,6 +100,7 @@ var utils_1 = require("@sasjs/utils");
100
100
  var configUtils = __importStar(require("../../../utils/config"));
101
101
  var setConstantsUtils = __importStar(require("../../../utils/setConstants"));
102
102
  var command_1 = require("../../../types/command");
103
+ var logcheck_1 = require("../../../utils/logcheck");
103
104
  var defaultArgs = ['node', 'sasjs', 'run'];
104
105
  var target = new utils_1.Target({
105
106
  name: 'test',
@@ -114,6 +115,55 @@ describe('RunCommand', function () {
114
115
  beforeEach(function () {
115
116
  setupMocks();
116
117
  });
118
+ afterEach(function () { return (0, logcheck_1.setLogCheckMode)('auto'); });
119
+ it('should return the logcheck code when the log check failed the run', function () { return __awaiter(void 0, void 0, void 0, function () {
120
+ var command, _a;
121
+ return __generator(this, function (_b) {
122
+ switch (_b.label) {
123
+ case 0:
124
+ jest
125
+ .spyOn(runModule, 'runSasCode')
126
+ .mockImplementation(function () { return Promise.reject(new logcheck_1.LogCheckFailure('2 errors')); });
127
+ command = new runCommand_1.RunCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [sasFilePath], false));
128
+ _a = expect;
129
+ return [4 /*yield*/, command.execute()];
130
+ case 1:
131
+ _a.apply(void 0, [_b.sent()]).toEqual(command_1.ReturnCode.LogCheckError);
132
+ return [2 /*return*/];
133
+ }
134
+ });
135
+ }); });
136
+ it('should switch the log check off for --no-logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
137
+ var command;
138
+ return __generator(this, function (_a) {
139
+ switch (_a.label) {
140
+ case 0:
141
+ command = new runCommand_1.RunCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [
142
+ sasFilePath,
143
+ '--no-logcheck'
144
+ ], false));
145
+ return [4 /*yield*/, command.execute()];
146
+ case 1:
147
+ _a.sent();
148
+ expect((0, logcheck_1.getLogCheckMode)()).toEqual('off');
149
+ return [2 /*return*/];
150
+ }
151
+ });
152
+ }); });
153
+ it('should switch the log check on for --logcheck', function () { return __awaiter(void 0, void 0, void 0, function () {
154
+ var command;
155
+ return __generator(this, function (_a) {
156
+ switch (_a.label) {
157
+ case 0:
158
+ command = new runCommand_1.RunCommand(__spreadArray(__spreadArray([], __read(defaultArgs), false), [sasFilePath, '--logcheck'], false));
159
+ return [4 /*yield*/, command.execute()];
160
+ case 1:
161
+ _a.sent();
162
+ expect((0, logcheck_1.getLogCheckMode)()).toEqual('on');
163
+ return [2 /*return*/];
164
+ }
165
+ });
166
+ }); });
117
167
  it('should parse sasjs run command', function () { return __awaiter(void 0, void 0, void 0, function () {
118
168
  return __generator(this, function (_a) {
119
169
  switch (_a.label) {
@@ -3,6 +3,7 @@ export * from './config';
3
3
  export * from './displayResult';
4
4
  export * from './file';
5
5
  export * from './loadEnvVariables';
6
+ export * from './logcheck';
6
7
  export * from './saveLog';
7
8
  export * from './setConstants';
8
9
  export * from './setProjectDir';
@@ -19,6 +19,7 @@ __exportStar(require("./config"), exports);
19
19
  __exportStar(require("./displayResult"), exports);
20
20
  __exportStar(require("./file"), exports);
21
21
  __exportStar(require("./loadEnvVariables"), exports);
22
+ __exportStar(require("./logcheck"), exports);
22
23
  __exportStar(require("./saveLog"), exports);
23
24
  __exportStar(require("./setConstants"), exports);
24
25
  __exportStar(require("./setProjectDir"), exports);
@@ -0,0 +1,44 @@
1
+ /**
2
+ * How the log a command just received is treated.
3
+ *
4
+ * `auto` decides from `logCheck.autoCheck` in the project config, which is off
5
+ * by default; `on` and `off` are the `--logcheck` / `--no-logcheck` flags. The
6
+ * distinction matters because the two halves of the behaviour are separate:
7
+ * reporting what SAS printed is always worth doing, while changing the exit
8
+ * code of a command whose status a pipeline already trusts is not something to
9
+ * do behind a project's back.
10
+ */
11
+ export type LogCheckMode = 'auto' | 'on' | 'off';
12
+ export declare const setLogCheckMode: (value: LogCheckMode) => void;
13
+ export declare const getLogCheckMode: () => LogCheckMode;
14
+ /** Reads a tri-state yargs boolean: absent is `undefined`, not `false`. */
15
+ export declare const logCheckModeOf: (flag: unknown) => LogCheckMode;
16
+ /**
17
+ * A run whose log broke the policy, once `autoCheck` or `--logcheck` asked for
18
+ * the verdict to count.
19
+ *
20
+ * It is an error rather than a return value because it is raised several
21
+ * frames below the command, from whichever executor received the log, and the
22
+ * command is the only place that can turn it into an exit code.
23
+ */
24
+ export declare class LogCheckFailure extends Error {
25
+ /**
26
+ * Marker rather than `instanceof`: the CLI is compiled to a target where a
27
+ * subclass of a built-in loses its prototype chain, so `instanceof` answers
28
+ * false in the built artefact while answering true under the test transform.
29
+ * The flag is read by {@link isLogCheckFailure} and cannot be faked by an
30
+ * unrelated error.
31
+ */
32
+ readonly logCheckFailure = true;
33
+ constructor(message: string);
34
+ }
35
+ export declare const isLogCheckFailure: (error: unknown) => error is LogCheckFailure;
36
+ /**
37
+ * Reports the issues in a log a command just received, and throws when the
38
+ * policy says the run should fail.
39
+ *
40
+ * Nothing is printed for a clean log: the commands this runs inside already
41
+ * have their own success output, and a check that announces itself on every
42
+ * green run is noise that gets switched off.
43
+ */
44
+ export declare const checkLog: (logText: string | undefined | null, label: string) => Promise<void>;