@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.
- package/README.md +1 -1
- package/build/commands/flow/flowCommand.js +11 -1
- package/build/commands/flow/internal/executeFlow.js +44 -19
- package/build/commands/flow/internal/saveLog.d.ts +12 -1
- package/build/commands/flow/internal/saveLog.js +2 -2
- package/build/commands/flow/internal/spec/executeFlow.spec.js +44 -1
- package/build/commands/flow/spec/flowCommand.spec.js +44 -0
- package/build/commands/job/internal/execute/sas9.js +16 -13
- package/build/commands/job/internal/execute/sasjs.js +12 -9
- package/build/commands/job/internal/execute/viya.js +41 -36
- package/build/commands/job/jobCommand.js +13 -0
- package/build/commands/job/spec/executeLogOrder.spec.d.ts +1 -0
- package/build/commands/job/spec/executeLogOrder.spec.js +119 -0
- package/build/commands/job/spec/jobCommand.spec.js +32 -0
- package/build/commands/logcheck/logCheckConfig.d.ts +11 -0
- package/build/commands/logcheck/logCheckConfig.js +86 -26
- package/build/commands/logcheck/renderLogCheck.d.ts +1 -0
- package/build/commands/logcheck/renderLogCheck.js +1 -0
- package/build/commands/logcheck/spec/logCheckConfig.spec.js +63 -2
- package/build/commands/logcheck/types.d.ts +8 -0
- package/build/commands/request/request.js +66 -27
- package/build/commands/request/requestCommand.js +11 -1
- package/build/commands/request/spec/requestCommand.spec.js +40 -0
- package/build/commands/run/run.d.ts +1 -3
- package/build/commands/run/run.js +20 -5
- package/build/commands/run/runCommand.js +9 -0
- package/build/commands/run/spec/runCommand.spec.js +50 -0
- package/build/utils/index.d.ts +1 -0
- package/build/utils/index.js +1 -0
- package/build/utils/logcheck.d.ts +44 -0
- package/build/utils/logcheck.js +224 -0
- package/build/utils/spec/logcheck.spec.d.ts +1 -0
- package/build/utils/spec/logcheck.spec.js +297 -0
- 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 (
|
|
174
|
-
var
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
145
|
-
|
|
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) {
|
package/build/utils/index.d.ts
CHANGED
package/build/utils/index.js
CHANGED
|
@@ -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>;
|