@sasjs/cli 4.20.9 → 5.0.3
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/deploy/deploy.js +17 -10
- package/build/commands/deploy/spec/deploy.spec.js +26 -0
- package/build/commands/docs/generateDocs.js +6 -17
- package/build/commands/docs/spec/generateDocs.spec.js +2 -2
- package/build/commands/fs/fsCommand.js +14 -9
- package/build/utils/config.d.ts +14 -0
- package/build/utils/config.js +16 -2
- package/build/utils/shell.d.ts +18 -6
- package/build/utils/shell.js +42 -15
- package/build/utils/spec/config.spec.js +109 -0
- package/build/utils/spec/shell.spec.js +27 -0
- package/build/utils/spec/utils.spec.js +0 -29
- package/build/utils/spec/zip.spec.js +54 -0
- package/build/utils/utils.d.ts +0 -1
- package/build/utils/utils.js +0 -10
- package/package.json +16 -21
|
@@ -53,42 +53,49 @@ var internal_1 = require("./internal");
|
|
|
53
53
|
*/
|
|
54
54
|
function deploy(target, isLocal) {
|
|
55
55
|
return __awaiter(this, void 0, void 0, function () {
|
|
56
|
-
var streamConfig, buildDestinationFolder, finalFilePathJSON, appLoc, jsonObject, servicesFolder, webIndexFileName, buildOutputFileName, deployScriptPath, deployScripts;
|
|
56
|
+
var streamConfig, deployServicePack, buildDestinationFolder, finalFilePathJSON, appLoc, jsonObject, servicesFolder, webIndexFileName, buildOutputFileName, deployScriptPath, deployScripts;
|
|
57
57
|
var _this = this;
|
|
58
58
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
59
59
|
return __generator(this, function (_l) {
|
|
60
60
|
switch (_l.label) {
|
|
61
|
-
case 0: return [4 /*yield*/, (0, config_1.getStreamConfig)(target)
|
|
61
|
+
case 0: return [4 /*yield*/, (0, config_1.getStreamConfig)(target)
|
|
62
|
+
// A target that declares no deployConfig used to have one injected by the
|
|
63
|
+
// @sasjs/utils Target constructor. That injection is gone (sasjs/utils#277),
|
|
64
|
+
// so an absent deployConfig is treated as the historical default - deploy the
|
|
65
|
+
// service pack - rather than refusing to deploy. An explicit
|
|
66
|
+
// deployServicePack: false is still honoured.
|
|
67
|
+
];
|
|
62
68
|
case 1:
|
|
63
69
|
streamConfig = _l.sent();
|
|
64
|
-
|
|
70
|
+
deployServicePack = (_b = (_a = target.deployConfig) === null || _a === void 0 ? void 0 : _a.deployServicePack) !== null && _b !== void 0 ? _b : true;
|
|
71
|
+
if (!deployServicePack) return [3 /*break*/, 7];
|
|
65
72
|
buildDestinationFolder = process.sasjsConstants.buildDestinationFolder;
|
|
66
73
|
finalFilePathJSON = path_1.default.join(buildDestinationFolder, "".concat(target.name, ".json"));
|
|
67
74
|
if (!(target.serverType === utils_1.ServerType.SasViya)) return [3 /*break*/, 3];
|
|
68
75
|
appLoc = encodeURI(target.appLoc);
|
|
69
|
-
(
|
|
76
|
+
(_c = process.logger) === null || _c === void 0 ? void 0 : _c.info("Deploying service pack to ".concat(target.serverUrl, " at location ").concat(appLoc, " ."));
|
|
70
77
|
return [4 /*yield*/, (0, deployToSasViyaWithServicePack_1.deployToSasViyaWithServicePack)(finalFilePathJSON, target, isLocal, true)];
|
|
71
78
|
case 2:
|
|
72
79
|
jsonObject = _l.sent();
|
|
73
80
|
servicesFolder = jsonObject === null || jsonObject === void 0 ? void 0 : jsonObject.members.find(function (member) {
|
|
74
81
|
return (member === null || member === void 0 ? void 0 : member.name) === 'services' && (member === null || member === void 0 ? void 0 : member.type) === utils_1.MemberType.folder;
|
|
75
82
|
});
|
|
76
|
-
webIndexFileName = (
|
|
77
|
-
(
|
|
78
|
-
(
|
|
83
|
+
webIndexFileName = (_f = (_e = (_d = servicesFolder === null || servicesFolder === void 0 ? void 0 : servicesFolder.members) === null || _d === void 0 ? void 0 : _d.find(function (member) { return (member === null || member === void 0 ? void 0 : member.type) === utils_1.MemberType.file; })) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : '';
|
|
84
|
+
(_g = process.logger) === null || _g === void 0 ? void 0 : _g.success('Build pack has been successfully deployed.');
|
|
85
|
+
(_h = process.logger) === null || _h === void 0 ? void 0 : _h.success(target.serverType === utils_1.ServerType.SasViya && webIndexFileName
|
|
79
86
|
? "".concat(target.serverUrl, "/SASJobExecution?_file=").concat(appLoc, "/services/").concat(webIndexFileName)
|
|
80
87
|
: "".concat(target.serverUrl, "/SASJobExecution?_folder=").concat(appLoc));
|
|
81
88
|
return [3 /*break*/, 7];
|
|
82
89
|
case 3:
|
|
83
90
|
if (!(target.serverType === utils_1.ServerType.Sasjs)) return [3 /*break*/, 5];
|
|
84
|
-
(
|
|
91
|
+
(_j = process.logger) === null || _j === void 0 ? void 0 : _j.info("Deploying service pack to ".concat(target.serverUrl, " at location ").concat(target.appLoc, " ."));
|
|
85
92
|
return [4 /*yield*/, (0, internal_1.deployToSasjsWithServicePack)(finalFilePathJSON, target, streamConfig)];
|
|
86
93
|
case 4:
|
|
87
94
|
_l.sent();
|
|
88
95
|
return [3 /*break*/, 7];
|
|
89
96
|
case 5:
|
|
90
97
|
if (!(target.serverType === utils_1.ServerType.Sas9)) return [3 /*break*/, 7];
|
|
91
|
-
buildOutputFileName = ((
|
|
98
|
+
buildOutputFileName = ((_k = target.buildConfig) === null || _k === void 0 ? void 0 : _k.buildOutputFileName) || "".concat(target.name, ".sas");
|
|
92
99
|
deployScriptPath = path_1.default.join(buildDestinationFolder, buildOutputFileName);
|
|
93
100
|
return [4 /*yield*/, (0, internal_1.executeSasScript)(deployScriptPath, target, streamConfig)];
|
|
94
101
|
case 6:
|
|
@@ -97,7 +104,7 @@ function deploy(target, isLocal) {
|
|
|
97
104
|
case 7: return [4 /*yield*/, (0, internal_1.getDeployScripts)(target)];
|
|
98
105
|
case 8:
|
|
99
106
|
deployScripts = _l.sent();
|
|
100
|
-
if (deployScripts.length === 0 && !
|
|
107
|
+
if (deployScripts.length === 0 && !deployServicePack) {
|
|
101
108
|
throw new Error("Deployment failed.\nPlease either enable the 'deployServicePack' option or add deployment script paths to 'deployScripts' in your target's 'deployConfig'.");
|
|
102
109
|
}
|
|
103
110
|
return [4 /*yield*/, (0, utils_1.asyncForEach)(deployScripts, function (deployScript) { return __awaiter(_this, void 0, void 0, function () {
|
|
@@ -154,6 +154,32 @@ describe('deploy', function () {
|
|
|
154
154
|
}
|
|
155
155
|
});
|
|
156
156
|
}); });
|
|
157
|
+
it('should deploy the service pack when the target declares no deployConfig', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
158
|
+
var target, fileTree;
|
|
159
|
+
return __generator(this, function (_a) {
|
|
160
|
+
switch (_a.label) {
|
|
161
|
+
case 0:
|
|
162
|
+
target = new utils_1.Target(__assign(__assign({}, commonTargetFields), { serverType: utils_1.ServerType.SasViya }));
|
|
163
|
+
fileTree = {
|
|
164
|
+
members: [
|
|
165
|
+
{
|
|
166
|
+
name: 'mockedFile',
|
|
167
|
+
type: utils_1.MemberType.file,
|
|
168
|
+
code: 'mocked code'
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
};
|
|
172
|
+
jest
|
|
173
|
+
.spyOn(deployToSasViyaWithServicePackModule, 'deployToSasViyaWithServicePack')
|
|
174
|
+
.mockImplementation(function () { return Promise.resolve(fileTree); });
|
|
175
|
+
return [4 /*yield*/, (0, deploy_1.deploy)(target, true)];
|
|
176
|
+
case 1:
|
|
177
|
+
_a.sent();
|
|
178
|
+
expect(deployToSasViyaWithServicePackModule.deployToSasViyaWithServicePack).toHaveBeenCalledWith(path_1.default.join(process.sasjsConstants.buildDestinationFolder, "".concat(target.name, ".json")), target, true, true);
|
|
179
|
+
return [2 /*return*/];
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}); });
|
|
157
183
|
it('should deploy services to SASJS as a service pack', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
158
184
|
var target;
|
|
159
185
|
return __generator(this, function (_a) {
|
|
@@ -94,7 +94,7 @@ var shell_1 = require("../../utils/shell");
|
|
|
94
94
|
*/
|
|
95
95
|
function generateDocs(target, config, outDirectory) {
|
|
96
96
|
return __awaiter(this, void 0, void 0, function () {
|
|
97
|
-
var _a, serverUrl, newOutDirectory, enableLineage, doxyContentFromConfig, _b, macroCoreFolders, macroFolders, programFolders, serviceFolders, jobFolders, combinedFolders, PROJECT_NAME, PROJECT_BRIEF, packageJsonContent, packageJson, e_1, doxyContent, layoutFilePath, LAYOUT_FILE, readMePath,
|
|
97
|
+
var _a, serverUrl, newOutDirectory, enableLineage, doxyContentFromConfig, _b, macroCoreFolders, macroFolders, programFolders, serviceFolders, jobFolders, combinedFolders, PROJECT_NAME, PROJECT_BRIEF, packageJsonContent, packageJson, e_1, doxyContent, layoutFilePath, LAYOUT_FILE, readMePath, doxyVariables, doxyConfigPath, spinner, pathExists, _c, stderr, code, foldersListForDot;
|
|
98
98
|
var _this = this;
|
|
99
99
|
var _d, _e;
|
|
100
100
|
return __generator(this, function (_f) {
|
|
@@ -163,7 +163,7 @@ function generateDocs(target, config, outDirectory) {
|
|
|
163
163
|
case 5:
|
|
164
164
|
LAYOUT_FILE = _f.sent();
|
|
165
165
|
readMePath = path_1.default.join(doxyContent.path, doxyContent.readMe);
|
|
166
|
-
|
|
166
|
+
doxyVariables = {
|
|
167
167
|
DOXY_HTML_OUTPUT: newOutDirectory,
|
|
168
168
|
DOXY_INPUT: "\"".concat(readMePath, "\" ").concat(combinedFolders),
|
|
169
169
|
DOXY_MAINPAGE: readMePath,
|
|
@@ -175,7 +175,7 @@ function generateDocs(target, config, outDirectory) {
|
|
|
175
175
|
PROJECT_BRIEF: PROJECT_BRIEF,
|
|
176
176
|
PROJECT_LOGO: path_1.default.join(doxyContent.path, doxyContent.logo),
|
|
177
177
|
PROJECT_NAME: PROJECT_NAME
|
|
178
|
-
}
|
|
178
|
+
};
|
|
179
179
|
return [4 /*yield*/, getDoxyConfigPath(doxyContent.path)];
|
|
180
180
|
case 6:
|
|
181
181
|
doxyConfigPath = _f.sent();
|
|
@@ -194,8 +194,9 @@ function generateDocs(target, config, outDirectory) {
|
|
|
194
194
|
case 9: return [4 /*yield*/, (0, utils_1.createFolder)(newOutDirectory)];
|
|
195
195
|
case 10:
|
|
196
196
|
_f.sent();
|
|
197
|
-
_c = (0, shell_1.
|
|
198
|
-
silent: process.env.LOG_LEVEL !== utils_1.LogLevel.Debug
|
|
197
|
+
_c = (0, shell_1.execFile)('doxygen', [doxyConfigPath], {
|
|
198
|
+
silent: process.env.LOG_LEVEL !== utils_1.LogLevel.Debug,
|
|
199
|
+
env: doxyVariables
|
|
199
200
|
}), stderr = _c.stderr, code = _c.code;
|
|
200
201
|
if (process.env.LOG_LEVEL !== utils_1.LogLevel.Debug)
|
|
201
202
|
spinner.stop();
|
|
@@ -228,18 +229,6 @@ function generateDocs(target, config, outDirectory) {
|
|
|
228
229
|
});
|
|
229
230
|
});
|
|
230
231
|
}
|
|
231
|
-
function setVariableCmd(params) {
|
|
232
|
-
var command = '';
|
|
233
|
-
if ((0, utils_1.isWindows)()) {
|
|
234
|
-
for (var param in params)
|
|
235
|
-
command += "set ".concat(param, "=").concat(params[param], " && ");
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
for (var param in params)
|
|
239
|
-
command += "".concat(param, "='").concat(params[param], "' ");
|
|
240
|
-
}
|
|
241
|
-
return command;
|
|
242
|
-
}
|
|
243
232
|
var getDoxyConfigPath = function (doxyContentPath) { return __awaiter(void 0, void 0, void 0, function () {
|
|
244
233
|
var doxyFilePath;
|
|
245
234
|
return __generator(this, function (_a) {
|
|
@@ -555,7 +555,7 @@ describe('sasjs doc', function () {
|
|
|
555
555
|
return __generator(this, function (_a) {
|
|
556
556
|
switch (_a.label) {
|
|
557
557
|
case 0:
|
|
558
|
-
jest.spyOn(shell, '
|
|
558
|
+
jest.spyOn(shell, 'execFile').mockReturnValueOnce({
|
|
559
559
|
code: 1,
|
|
560
560
|
stderr: 'error: bad config',
|
|
561
561
|
stdout: ''
|
|
@@ -571,7 +571,7 @@ describe('sasjs doc', function () {
|
|
|
571
571
|
return __generator(this, function (_a) {
|
|
572
572
|
switch (_a.label) {
|
|
573
573
|
case 0:
|
|
574
|
-
jest.spyOn(shell, '
|
|
574
|
+
jest.spyOn(shell, 'execFile').mockReturnValueOnce({
|
|
575
575
|
code: 1,
|
|
576
576
|
stderr: 'command not found',
|
|
577
577
|
stdout: ''
|
|
@@ -107,6 +107,11 @@ var targetCommand_1 = require("../../types/command/targetCommand");
|
|
|
107
107
|
var utils_1 = require("../../utils");
|
|
108
108
|
var path_1 = __importDefault(require("path"));
|
|
109
109
|
var utils_2 = require("@sasjs/utils");
|
|
110
|
+
// The hash and sync helpers live in the fs subpath. The barrel no longer
|
|
111
|
+
// re-exports them, because it is the only module in @sasjs/utils that imports
|
|
112
|
+
// crypto, which put the whole AES/RSA/ECDSA polyfill suite into every browser
|
|
113
|
+
// bundle that imported anything from the barrel (sasjs/utils#275, #283).
|
|
114
|
+
var fs_1 = require("@sasjs/utils/fs");
|
|
110
115
|
var executeCode_1 = require("./internal/executeCode");
|
|
111
116
|
var FsSubCommand;
|
|
112
117
|
(function (FsSubCommand) {
|
|
@@ -269,7 +274,7 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
269
274
|
return [4 /*yield*/, this.getOutputPath()];
|
|
270
275
|
case 2:
|
|
271
276
|
outputPath = _a.sent();
|
|
272
|
-
return [4 /*yield*/, (0,
|
|
277
|
+
return [4 /*yield*/, (0, fs_1.generateCompileProgram)(folderPath)];
|
|
273
278
|
case 3:
|
|
274
279
|
programContent = _a.sent();
|
|
275
280
|
return [4 /*yield*/, (0, utils_2.createFile)(outputPath, programContent)
|
|
@@ -306,7 +311,7 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
306
311
|
case 3:
|
|
307
312
|
_f.trys.push([3, 7, , 9]);
|
|
308
313
|
(_a = process.logger) === null || _a === void 0 ? void 0 : _a.info('generating compile program');
|
|
309
|
-
return [4 /*yield*/, (0,
|
|
314
|
+
return [4 /*yield*/, (0, fs_1.generateCompileProgram)(localFolderPath)];
|
|
310
315
|
case 4:
|
|
311
316
|
code = _f.sent();
|
|
312
317
|
(_b = process.logger) === null || _b === void 0 ? void 0 : _b.info('setting fsTaregt at the start of compiled program');
|
|
@@ -383,7 +388,7 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
383
388
|
case 2:
|
|
384
389
|
_v.trys.push([2, 15, , 17]);
|
|
385
390
|
(_h = process.logger) === null || _h === void 0 ? void 0 : _h.info('generating program to get remote hash');
|
|
386
|
-
return [4 /*yield*/, (0,
|
|
391
|
+
return [4 /*yield*/, (0, fs_1.generateProgramToGetRemoteHash)(obj.remote)];
|
|
387
392
|
case 3:
|
|
388
393
|
program = _v.sent();
|
|
389
394
|
return [4 /*yield*/, createProgramFile(program, path_1.default.join(outputFolder, 'getRemoteHash.sas'))];
|
|
@@ -397,12 +402,12 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
397
402
|
case 6:
|
|
398
403
|
_v.sent();
|
|
399
404
|
(_k = process.logger) === null || _k === void 0 ? void 0 : _k.info('extracting hashes from log');
|
|
400
|
-
remoteHashes = (0,
|
|
405
|
+
remoteHashes = (0, fs_1.extractHashArray)(log);
|
|
401
406
|
return [4 /*yield*/, createHashFile(JSON.stringify(remoteHashes, null, 2), path_1.default.join(outputFolder, 'hashesBeforeSync.json'))];
|
|
402
407
|
case 7:
|
|
403
408
|
_v.sent();
|
|
404
409
|
(_l = process.logger) === null || _l === void 0 ? void 0 : _l.info('creating the hash of local folder');
|
|
405
|
-
return [4 /*yield*/, (0,
|
|
410
|
+
return [4 /*yield*/, (0, fs_1.getHash)((0, utils_2.getAbsolutePath)(obj.local, process.projectDir))];
|
|
406
411
|
case 8:
|
|
407
412
|
localHash = _v.sent();
|
|
408
413
|
remoteHashMap_1 = remoteHashes.reduce(function (map, item) {
|
|
@@ -417,12 +422,12 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
417
422
|
return [2 /*return*/, { value: command_1.ReturnCode.Success }];
|
|
418
423
|
}
|
|
419
424
|
(_o = process.logger) === null || _o === void 0 ? void 0 : _o.info('Extract differences from local and remote hash');
|
|
420
|
-
hashedDiff = (0,
|
|
425
|
+
hashedDiff = (0, fs_1.compareHashes)(localHash, remoteHashMap_1);
|
|
421
426
|
return [4 /*yield*/, createHashFile(JSON.stringify(hashedDiff, null, 2), path_1.default.join(outputFolder, 'hashesDiff.json'))];
|
|
422
427
|
case 9:
|
|
423
428
|
_v.sent();
|
|
424
429
|
(_p = process.logger) === null || _p === void 0 ? void 0 : _p.info('generating program to sync differences');
|
|
425
|
-
return [4 /*yield*/, (0,
|
|
430
|
+
return [4 /*yield*/, (0, fs_1.generateProgramToSyncHashDiff)(hashedDiff, obj.remote)];
|
|
426
431
|
case 10:
|
|
427
432
|
syncProgram = _v.sent();
|
|
428
433
|
return [4 /*yield*/, createProgramFile(syncProgram, path_1.default.join(outputFolder, 'syncProgram.sas'))];
|
|
@@ -435,7 +440,7 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
435
440
|
return [4 /*yield*/, (0, utils_1.saveLog)(syncLog, path_1.default.join(outputFolder, 'sync.log'), '', false)];
|
|
436
441
|
case 13:
|
|
437
442
|
_v.sent();
|
|
438
|
-
syncedHash = (0,
|
|
443
|
+
syncedHash = (0, fs_1.extractHashArray)(syncLog);
|
|
439
444
|
return [4 /*yield*/, createHashFile(JSON.stringify(syncedHash, null, 2), path_1.default.join(outputFolder, 'hashesAfterSync.json'))];
|
|
440
445
|
case 14:
|
|
441
446
|
_v.sent();
|
|
@@ -459,7 +464,7 @@ var FSCommand = /** @class */ (function (_super) {
|
|
|
459
464
|
(_a = process.logger) === null || _a === void 0 ? void 0 : _a.log("* ".concat(item));
|
|
460
465
|
});
|
|
461
466
|
}
|
|
462
|
-
resourcesNotPresentLocally = (0,
|
|
467
|
+
resourcesNotPresentLocally = (0, fs_1.findResourcesNotPresentLocally)(localHash, syncedHashMap);
|
|
463
468
|
if (resourcesNotPresentLocally.length) {
|
|
464
469
|
(_s = process.logger) === null || _s === void 0 ? void 0 : _s.log('The following resources are present in remote directory but not in local:');
|
|
465
470
|
resourcesNotPresentLocally.forEach(function (item) {
|
package/build/utils/config.d.ts
CHANGED
|
@@ -23,6 +23,13 @@ export declare function findTargetInConfiguration(targetName: string, targetScop
|
|
|
23
23
|
export declare function getGlobalRcFile(): Promise<any>;
|
|
24
24
|
export declare function getLocalRcFile(): Promise<Configuration | null>;
|
|
25
25
|
export declare function saveGlobalRcFile(content: string): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Saves a target to the global SASjs config.
|
|
28
|
+
*
|
|
29
|
+
* `saveWithDefaultValues` defaults to false, so omitting it persists a minimal
|
|
30
|
+
* target - the mandatory attributes only. Pass true to write the full set of
|
|
31
|
+
* injected defaults, including a deployConfig the caller never declared.
|
|
32
|
+
*/
|
|
26
33
|
export declare function saveToGlobalConfig(target: Target, isDefault?: boolean, saveWithDefaultValues?: boolean): Promise<string>;
|
|
27
34
|
export declare function removeFromGlobalConfig(targetName: string): Promise<void>;
|
|
28
35
|
export declare function removeFromLocalConfig(targetName: string): Promise<void>;
|
|
@@ -33,6 +40,13 @@ export declare function getLocalOrGlobalConfig(): Promise<{
|
|
|
33
40
|
}>;
|
|
34
41
|
export declare function getSyncDirectories(target: Target, isLocal: boolean): Promise<SyncDirectoryMap[]>;
|
|
35
42
|
export declare function saveLocalConfigFile(content: string): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Saves a target to the project's local sasjsconfig.json.
|
|
45
|
+
*
|
|
46
|
+
* `saveWithDefaultValues` defaults to false, so omitting it persists a minimal
|
|
47
|
+
* target - the mandatory attributes only. Pass true to write the full set of
|
|
48
|
+
* injected defaults, including a deployConfig the caller never declared.
|
|
49
|
+
*/
|
|
36
50
|
export declare function saveToLocalConfig(target: Target, isDefault?: boolean, saveWithDefaultValues?: boolean): Promise<string>;
|
|
37
51
|
export declare function getFolders(): Promise<any>;
|
|
38
52
|
/**
|
package/build/utils/config.js
CHANGED
|
@@ -447,12 +447,19 @@ function saveGlobalRcFile(content) {
|
|
|
447
447
|
});
|
|
448
448
|
});
|
|
449
449
|
}
|
|
450
|
+
/**
|
|
451
|
+
* Saves a target to the global SASjs config.
|
|
452
|
+
*
|
|
453
|
+
* `saveWithDefaultValues` defaults to false, so omitting it persists a minimal
|
|
454
|
+
* target - the mandatory attributes only. Pass true to write the full set of
|
|
455
|
+
* injected defaults, including a deployConfig the caller never declared.
|
|
456
|
+
*/
|
|
450
457
|
function saveToGlobalConfig(target_1) {
|
|
451
458
|
return __awaiter(this, arguments, void 0, function (target, isDefault, saveWithDefaultValues) {
|
|
452
459
|
var globalConfig, targetJson, existingTargetIndex;
|
|
453
460
|
var _a;
|
|
454
461
|
if (isDefault === void 0) { isDefault = false; }
|
|
455
|
-
if (saveWithDefaultValues === void 0) { saveWithDefaultValues =
|
|
462
|
+
if (saveWithDefaultValues === void 0) { saveWithDefaultValues = false; }
|
|
456
463
|
return __generator(this, function (_b) {
|
|
457
464
|
switch (_b.label) {
|
|
458
465
|
case 0: return [4 /*yield*/, getGlobalRcFile()];
|
|
@@ -606,12 +613,19 @@ function saveLocalConfigFile(content) {
|
|
|
606
613
|
});
|
|
607
614
|
});
|
|
608
615
|
}
|
|
616
|
+
/**
|
|
617
|
+
* Saves a target to the project's local sasjsconfig.json.
|
|
618
|
+
*
|
|
619
|
+
* `saveWithDefaultValues` defaults to false, so omitting it persists a minimal
|
|
620
|
+
* target - the mandatory attributes only. Pass true to write the full set of
|
|
621
|
+
* injected defaults, including a deployConfig the caller never declared.
|
|
622
|
+
*/
|
|
609
623
|
function saveToLocalConfig(target_1) {
|
|
610
624
|
return __awaiter(this, arguments, void 0, function (target, isDefault, saveWithDefaultValues) {
|
|
611
625
|
var targetJson, config, existingTargetIndex, configPath;
|
|
612
626
|
var _a, _b;
|
|
613
627
|
if (isDefault === void 0) { isDefault = false; }
|
|
614
|
-
if (saveWithDefaultValues === void 0) { saveWithDefaultValues =
|
|
628
|
+
if (saveWithDefaultValues === void 0) { saveWithDefaultValues = false; }
|
|
615
629
|
return __generator(this, function (_c) {
|
|
616
630
|
switch (_c.label) {
|
|
617
631
|
case 0:
|
package/build/utils/shell.d.ts
CHANGED
|
@@ -17,15 +17,27 @@ export interface ShellResult {
|
|
|
17
17
|
stderr: string;
|
|
18
18
|
code: number;
|
|
19
19
|
}
|
|
20
|
+
export interface ExecOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Whether the command's output also reaches this process's streams, which is
|
|
23
|
+
* how shelljs' option of the same name behaved.
|
|
24
|
+
*/
|
|
25
|
+
silent?: boolean;
|
|
26
|
+
/** Extra variables for the child, on top of this process's environment. */
|
|
27
|
+
env?: NodeJS.ProcessEnv;
|
|
28
|
+
}
|
|
29
|
+
/** Runs a command through the platform shell. */
|
|
30
|
+
export declare const exec: (command: string, options?: ExecOptions) => ShellResult;
|
|
20
31
|
/**
|
|
21
|
-
* Runs
|
|
32
|
+
* Runs an executable with an argument array, without a shell.
|
|
22
33
|
*
|
|
23
|
-
* `
|
|
24
|
-
*
|
|
34
|
+
* `exec` hands its command to a shell, which re-parses everything in it: a path
|
|
35
|
+
* containing a quote, `$` or `&` changes the command's meaning, and a variable
|
|
36
|
+
* assigned on the command line is no different - on Windows the assignment form
|
|
37
|
+
* does not even quote the value. Arguments and `env` given here are passed to
|
|
38
|
+
* the child as they are, so they are safe whatever they contain.
|
|
25
39
|
*/
|
|
26
|
-
export declare const
|
|
27
|
-
silent?: boolean;
|
|
28
|
-
}) => ShellResult;
|
|
40
|
+
export declare const execFile: (command: string, args: string[], options?: ExecOptions) => ShellResult;
|
|
29
41
|
/**
|
|
30
42
|
* Copies the contents of a folder into another folder, matching
|
|
31
43
|
* `cp -r <source>/. <destination>`.
|
package/build/utils/shell.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
2
13
|
var __values = (this && this.__values) || function(o) {
|
|
3
14
|
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
4
15
|
if (m) return m.call(o);
|
|
@@ -14,28 +25,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
25
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
26
|
};
|
|
16
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.which = exports.ls = exports.rm = exports.cp = exports.exec = void 0;
|
|
28
|
+
exports.which = exports.ls = exports.rm = exports.cp = exports.execFile = exports.exec = void 0;
|
|
18
29
|
var child_process_1 = require("child_process");
|
|
19
30
|
var fs_1 = __importDefault(require("fs"));
|
|
20
31
|
var path_1 = __importDefault(require("path"));
|
|
21
32
|
var utils_1 = require("@sasjs/utils");
|
|
22
33
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* streams, which is how shelljs' option of the same name behaved.
|
|
34
|
+
* The options both runners share. shelljs allowed 20MB of output; spawnSync's
|
|
35
|
+
* 1MB default kills a chatty command mid-run, and a deploy script can easily be
|
|
36
|
+
* chatty.
|
|
27
37
|
*/
|
|
28
|
-
var
|
|
38
|
+
var spawnOptions = function (options) { return ({
|
|
39
|
+
encoding: 'utf8',
|
|
40
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
41
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
42
|
+
env: options.env ? __assign(__assign({}, process.env), options.env) : undefined
|
|
43
|
+
}); };
|
|
44
|
+
/** The result both runners answer with, from one place. */
|
|
45
|
+
var toShellResult = function (result, options) {
|
|
29
46
|
var _a, _b, _c;
|
|
30
|
-
if (options === void 0) { options = {}; }
|
|
31
|
-
var result = (0, child_process_1.spawnSync)(command, {
|
|
32
|
-
shell: true,
|
|
33
|
-
encoding: 'utf8',
|
|
34
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
35
|
-
// shelljs allowed 20MB of output; spawnSync's 1MB default kills a chatty
|
|
36
|
-
// command mid-run, and a deploy script can easily be chatty.
|
|
37
|
-
maxBuffer: 20 * 1024 * 1024
|
|
38
|
-
});
|
|
39
47
|
var stdout = (_a = result.stdout) !== null && _a !== void 0 ? _a : '';
|
|
40
48
|
var stderr = (_b = result.stderr) !== null && _b !== void 0 ? _b : '';
|
|
41
49
|
if (!options.silent) {
|
|
@@ -56,7 +64,26 @@ var exec = function (command, options) {
|
|
|
56
64
|
}
|
|
57
65
|
return { stdout: stdout, stderr: stderr, code: (_c = result.status) !== null && _c !== void 0 ? _c : 0 };
|
|
58
66
|
};
|
|
67
|
+
/** Runs a command through the platform shell. */
|
|
68
|
+
var exec = function (command, options) {
|
|
69
|
+
if (options === void 0) { options = {}; }
|
|
70
|
+
return toShellResult((0, child_process_1.spawnSync)(command, __assign(__assign({}, spawnOptions(options)), { shell: true })), options);
|
|
71
|
+
};
|
|
59
72
|
exports.exec = exec;
|
|
73
|
+
/**
|
|
74
|
+
* Runs an executable with an argument array, without a shell.
|
|
75
|
+
*
|
|
76
|
+
* `exec` hands its command to a shell, which re-parses everything in it: a path
|
|
77
|
+
* containing a quote, `$` or `&` changes the command's meaning, and a variable
|
|
78
|
+
* assigned on the command line is no different - on Windows the assignment form
|
|
79
|
+
* does not even quote the value. Arguments and `env` given here are passed to
|
|
80
|
+
* the child as they are, so they are safe whatever they contain.
|
|
81
|
+
*/
|
|
82
|
+
var execFile = function (command, args, options) {
|
|
83
|
+
if (options === void 0) { options = {}; }
|
|
84
|
+
return toShellResult((0, child_process_1.spawnSync)(command, args, spawnOptions(options)), options);
|
|
85
|
+
};
|
|
86
|
+
exports.execFile = execFile;
|
|
60
87
|
/**
|
|
61
88
|
* Copies the contents of a folder into another folder, matching
|
|
62
89
|
* `cp -r <source>/. <destination>`.
|
|
@@ -770,6 +770,61 @@ describe('saveToGlobalConfig', function () {
|
|
|
770
770
|
}
|
|
771
771
|
});
|
|
772
772
|
}); });
|
|
773
|
+
it('should persist the target with injected defaults when saveWithDefaultValues is true', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
774
|
+
var appName, target, config, configTarget;
|
|
775
|
+
var _a;
|
|
776
|
+
return __generator(this, function (_b) {
|
|
777
|
+
switch (_b.label) {
|
|
778
|
+
case 0:
|
|
779
|
+
appName = 'cli-tests-config-' + (0, test_1.generateUniqueTimestamp)();
|
|
780
|
+
target = (0, test_1.generateTestTarget)(appName, "/Public/app/cli-tests/".concat(appName));
|
|
781
|
+
return [4 /*yield*/, (0, config_1.saveToGlobalConfig)(target, true, true)];
|
|
782
|
+
case 1:
|
|
783
|
+
_b.sent();
|
|
784
|
+
return [4 /*yield*/, (0, config_1.getGlobalRcFile)()];
|
|
785
|
+
case 2:
|
|
786
|
+
config = (_b.sent());
|
|
787
|
+
configTarget = (_a = config.targets) === null || _a === void 0 ? void 0 : _a.find(function (t) { return t.name === target.name; });
|
|
788
|
+
expect(configTarget).toBeTruthy();
|
|
789
|
+
expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.deployConfig).toBeTruthy();
|
|
790
|
+
return [4 /*yield*/, (0, config_1.removeFromGlobalConfig)(target.name)];
|
|
791
|
+
case 3:
|
|
792
|
+
_b.sent();
|
|
793
|
+
return [2 /*return*/];
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
}); });
|
|
797
|
+
it('should persist a minimal target when saveWithDefaultValues is false', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
798
|
+
var appName, target, config, configTarget;
|
|
799
|
+
var _a;
|
|
800
|
+
return __generator(this, function (_b) {
|
|
801
|
+
switch (_b.label) {
|
|
802
|
+
case 0:
|
|
803
|
+
appName = 'cli-tests-config-' + (0, test_1.generateUniqueTimestamp)();
|
|
804
|
+
target = new utils_1.Target({
|
|
805
|
+
name: appName,
|
|
806
|
+
serverUrl: 'http://test.sasjs',
|
|
807
|
+
appLoc: "/Public/app/cli-tests/".concat(appName),
|
|
808
|
+
serverType: utils_1.ServerType.SasViya
|
|
809
|
+
});
|
|
810
|
+
return [4 /*yield*/, (0, config_1.saveToGlobalConfig)(target, true)];
|
|
811
|
+
case 1:
|
|
812
|
+
_b.sent();
|
|
813
|
+
return [4 /*yield*/, (0, config_1.getGlobalRcFile)()];
|
|
814
|
+
case 2:
|
|
815
|
+
config = (_b.sent());
|
|
816
|
+
expect(config.defaultTarget).toEqual(target.name);
|
|
817
|
+
configTarget = (_a = config.targets) === null || _a === void 0 ? void 0 : _a.find(function (t) { return t.name === target.name; });
|
|
818
|
+
expect(configTarget).toBeTruthy();
|
|
819
|
+
expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.deployConfig).toBeFalsy();
|
|
820
|
+
expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.buildConfig).toBeFalsy();
|
|
821
|
+
return [4 /*yield*/, (0, config_1.removeFromGlobalConfig)(target.name)];
|
|
822
|
+
case 3:
|
|
823
|
+
_b.sent();
|
|
824
|
+
return [2 /*return*/];
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
}); });
|
|
773
828
|
it('should not set the target as default when isDefault is false', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
774
829
|
var appName, target, config, configTarget;
|
|
775
830
|
var _a;
|
|
@@ -922,6 +977,60 @@ describe('saveToLocalConfig', function () {
|
|
|
922
977
|
}
|
|
923
978
|
});
|
|
924
979
|
}); });
|
|
980
|
+
it('should persist the target with injected defaults when saveWithDefaultValues is true', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
981
|
+
var appName, target, config, configTarget;
|
|
982
|
+
var _a;
|
|
983
|
+
return __generator(this, function (_b) {
|
|
984
|
+
switch (_b.label) {
|
|
985
|
+
case 0:
|
|
986
|
+
appName = 'cli-tests-cb-' + (0, test_1.generateUniqueTimestamp)();
|
|
987
|
+
target = (0, test_1.generateTestTarget)(appName, "/Public/app/cli-tests/".concat(appName));
|
|
988
|
+
return [4 /*yield*/, (0, config_1.saveToLocalConfig)(target, true, true)];
|
|
989
|
+
case 1:
|
|
990
|
+
_b.sent();
|
|
991
|
+
return [4 /*yield*/, (0, config_1.getLocalConfig)()];
|
|
992
|
+
case 2:
|
|
993
|
+
config = (_b.sent());
|
|
994
|
+
configTarget = (_a = config.targets) === null || _a === void 0 ? void 0 : _a.find(function (t) { return t.name === target.name; });
|
|
995
|
+
expect(configTarget).toBeTruthy();
|
|
996
|
+
expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.deployConfig).toBeTruthy();
|
|
997
|
+
return [4 /*yield*/, (0, config_1.removeFromLocalConfig)(target.name)];
|
|
998
|
+
case 3:
|
|
999
|
+
_b.sent();
|
|
1000
|
+
return [2 /*return*/];
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
}); });
|
|
1004
|
+
it('should persist a minimal target when saveWithDefaultValues is omitted', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
1005
|
+
var appName, target, config, configTarget;
|
|
1006
|
+
var _a;
|
|
1007
|
+
return __generator(this, function (_b) {
|
|
1008
|
+
switch (_b.label) {
|
|
1009
|
+
case 0:
|
|
1010
|
+
appName = 'cli-tests-cb-' + (0, test_1.generateUniqueTimestamp)();
|
|
1011
|
+
target = new utils_1.Target({
|
|
1012
|
+
name: appName,
|
|
1013
|
+
serverUrl: 'http://test.sasjs',
|
|
1014
|
+
appLoc: "/Public/app/cli-tests/".concat(appName),
|
|
1015
|
+
serverType: utils_1.ServerType.SasViya
|
|
1016
|
+
});
|
|
1017
|
+
return [4 /*yield*/, (0, config_1.saveToLocalConfig)(target, true)];
|
|
1018
|
+
case 1:
|
|
1019
|
+
_b.sent();
|
|
1020
|
+
return [4 /*yield*/, (0, config_1.getLocalConfig)()];
|
|
1021
|
+
case 2:
|
|
1022
|
+
config = (_b.sent());
|
|
1023
|
+
configTarget = (_a = config.targets) === null || _a === void 0 ? void 0 : _a.find(function (t) { return t.name === target.name; });
|
|
1024
|
+
expect(configTarget).toBeTruthy();
|
|
1025
|
+
expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.deployConfig).toBeFalsy();
|
|
1026
|
+
expect(configTarget === null || configTarget === void 0 ? void 0 : configTarget.buildConfig).toBeFalsy();
|
|
1027
|
+
return [4 /*yield*/, (0, config_1.removeFromLocalConfig)(target.name)];
|
|
1028
|
+
case 3:
|
|
1029
|
+
_b.sent();
|
|
1030
|
+
return [2 /*return*/];
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
}); });
|
|
925
1034
|
it('should not set the target as default when isDefault is false', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
926
1035
|
var appName, target, config, configTarget;
|
|
927
1036
|
var _a;
|
|
@@ -54,6 +54,33 @@ describe('shell', function () {
|
|
|
54
54
|
expect(result.stdout.length).toEqual(3 * 1024 * 1024);
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
|
+
describe('execFile', function () {
|
|
58
|
+
// The values below are the point: a shell would interpret each of them -
|
|
59
|
+
// `&` and `;` separate commands, `$(...)` and backticks substitute, `'`
|
|
60
|
+
// closes a quote - and on Windows the `set NAME=value` form does not quote
|
|
61
|
+
// the value at all. Nothing here is parsed, so each arrives intact.
|
|
62
|
+
var metacharacters = "a'b&c;$(echo pwned)`id`|d>e";
|
|
63
|
+
it('should answer with stdout for an executable run with an argument array', function () {
|
|
64
|
+
var result = (0, shell_1.execFile)(process.execPath, ['-e', "process.stdout.write('hello')"], { silent: true });
|
|
65
|
+
expect(result.stdout).toEqual('hello');
|
|
66
|
+
expect(result.code).toEqual(0);
|
|
67
|
+
});
|
|
68
|
+
it('should pass an argument through without the shell interpreting it', function () {
|
|
69
|
+
var result = (0, shell_1.execFile)(process.execPath, ['-e', 'process.stdout.write(process.argv[1])', metacharacters], { silent: true });
|
|
70
|
+
expect(result.stdout).toEqual(metacharacters);
|
|
71
|
+
expect(result.code).toEqual(0);
|
|
72
|
+
});
|
|
73
|
+
it('should pass an environment value through without the shell interpreting it', function () {
|
|
74
|
+
var result = (0, shell_1.execFile)(process.execPath, ['-e', 'process.stdout.write(process.env.PROBE)'], { silent: true, env: { PROBE: metacharacters } });
|
|
75
|
+
expect(result.stdout).toEqual(metacharacters);
|
|
76
|
+
expect(result.code).toEqual(0);
|
|
77
|
+
});
|
|
78
|
+
it('should answer with the exit code and stderr for an executable that fails', function () {
|
|
79
|
+
var result = (0, shell_1.execFile)(process.execPath, ['-e', "process.stderr.write('boom'); process.exit(3)"], { silent: true });
|
|
80
|
+
expect(result.code).toEqual(3);
|
|
81
|
+
expect(result.stderr).toContain('boom');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
57
84
|
describe('ls', function () {
|
|
58
85
|
beforeEach(function () {
|
|
59
86
|
fs_1.default.mkdirSync(path_1.default.join(root, 'sasjs-react-seed-app-main'));
|
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __assign = (this && this.__assign) || function () {
|
|
3
|
-
__assign = Object.assign || function(t) {
|
|
4
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
-
s = arguments[i];
|
|
6
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
-
t[p] = s[p];
|
|
8
|
-
}
|
|
9
|
-
return t;
|
|
10
|
-
};
|
|
11
|
-
return __assign.apply(this, arguments);
|
|
12
|
-
};
|
|
13
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -273,24 +262,6 @@ describe('utils', function () {
|
|
|
273
262
|
});
|
|
274
263
|
});
|
|
275
264
|
});
|
|
276
|
-
describe('checkNodeVersion', function () {
|
|
277
|
-
afterEach(function () {
|
|
278
|
-
jest.resetAllMocks();
|
|
279
|
-
});
|
|
280
|
-
it('should exit the process when node version is less than 12', function () {
|
|
281
|
-
var originalProcess = global.process;
|
|
282
|
-
global.process = __assign(__assign({}, originalProcess), { versions: __assign(__assign({}, originalProcess.versions), { node: '11.0.0' }) });
|
|
283
|
-
var mockExit = (0, test_1.mockProcessExit)();
|
|
284
|
-
(0, utils_1.checkNodeVersion)();
|
|
285
|
-
expect(mockExit).toHaveBeenCalledWith(1);
|
|
286
|
-
global.process = originalProcess;
|
|
287
|
-
});
|
|
288
|
-
it('should not exit the process when node version greater than 12', function () {
|
|
289
|
-
jest.spyOn(process, 'exit');
|
|
290
|
-
(0, utils_1.checkNodeVersion)();
|
|
291
|
-
expect(process.exit).toHaveBeenCalledTimes(0);
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
265
|
describe('getAdapterInstance', function () {
|
|
295
266
|
it('should throw an error when target is not valid', function () {
|
|
296
267
|
expect(function () { return (0, utils_1.getAdapterInstance)(null); }).toThrow('Unable to create SASjs adapter instance: Invalid target.');
|
|
@@ -84,6 +84,32 @@ var setEntryPlatform = function (zipPath, platform) {
|
|
|
84
84
|
}
|
|
85
85
|
fs_1.default.writeFileSync(zipPath, buffer);
|
|
86
86
|
};
|
|
87
|
+
/**
|
|
88
|
+
* Rewrites an entry name in the archive bytes, in both the local file header
|
|
89
|
+
* and the central directory record.
|
|
90
|
+
*
|
|
91
|
+
* yazl refuses to create an entry whose name is absolute or contains `..`, so
|
|
92
|
+
* a traversal entry has to be patched in after the fact. The replacement must
|
|
93
|
+
* be the same length as the original: both records carry the name length, and
|
|
94
|
+
* changing it would move every offset that follows.
|
|
95
|
+
*/
|
|
96
|
+
var replaceEntryName = function (zipPath, from, to) {
|
|
97
|
+
if (from.length !== to.length)
|
|
98
|
+
throw new Error('the replacement name must be the same length');
|
|
99
|
+
var buffer = fs_1.default.readFileSync(zipPath);
|
|
100
|
+
var fromBuffer = Buffer.from(from, 'utf8');
|
|
101
|
+
var toBuffer = Buffer.from(to, 'utf8');
|
|
102
|
+
var index = buffer.indexOf(fromBuffer);
|
|
103
|
+
var replaced = 0;
|
|
104
|
+
while (index !== -1) {
|
|
105
|
+
toBuffer.copy(buffer, index);
|
|
106
|
+
replaced++;
|
|
107
|
+
index = buffer.indexOf(fromBuffer, index + 1);
|
|
108
|
+
}
|
|
109
|
+
if (!replaced)
|
|
110
|
+
throw new Error("entry name not found in the archive: ".concat(from));
|
|
111
|
+
fs_1.default.writeFileSync(zipPath, buffer);
|
|
112
|
+
};
|
|
87
113
|
describe('zip', function () {
|
|
88
114
|
var tmpRoot = path_1.default.join(__dirname, "zip-spec-".concat((0, test_1.generateUniqueTimestamp)()));
|
|
89
115
|
var destDir = path_1.default.join(tmpRoot, 'dest');
|
|
@@ -279,5 +305,33 @@ describe('zip', function () {
|
|
|
279
305
|
}
|
|
280
306
|
});
|
|
281
307
|
}); });
|
|
308
|
+
/**
|
|
309
|
+
* The safety of `path.join(destDir, entry.fileName)` rests on the entry
|
|
310
|
+
* name never containing `..` or a leading slash. That is yauzl's
|
|
311
|
+
* validateFileName, which only runs because the reader is opened with
|
|
312
|
+
* decodeStrings - so the property is pinned here rather than left to the
|
|
313
|
+
* comment that documents it.
|
|
314
|
+
*/
|
|
315
|
+
it('should refuse an entry whose name traverses out of the destination', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
316
|
+
var zipPath;
|
|
317
|
+
return __generator(this, function (_a) {
|
|
318
|
+
switch (_a.label) {
|
|
319
|
+
case 0:
|
|
320
|
+
zipPath = path_1.default.join(tmpRoot, 'traversal.zip');
|
|
321
|
+
return [4 /*yield*/, makeZip([{ name: 'aa/evil.txt', content: 'pwned' }], zipPath)];
|
|
322
|
+
case 1:
|
|
323
|
+
_a.sent();
|
|
324
|
+
replaceEntryName(zipPath, 'aa/evil.txt', '../evil.txt');
|
|
325
|
+
return [4 /*yield*/, expect((0, zip_1.extractZip)(zipPath, destDir)).rejects.toThrow()
|
|
326
|
+
// destDir/../evil.txt resolves to tmpRoot/evil.txt
|
|
327
|
+
];
|
|
328
|
+
case 2:
|
|
329
|
+
_a.sent();
|
|
330
|
+
// destDir/../evil.txt resolves to tmpRoot/evil.txt
|
|
331
|
+
expect(fs_1.default.existsSync(path_1.default.join(tmpRoot, 'evil.txt'))).toBeFalse();
|
|
332
|
+
return [2 /*return*/];
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}); });
|
|
282
336
|
});
|
|
283
337
|
});
|
package/build/utils/utils.d.ts
CHANGED
|
@@ -30,7 +30,6 @@ export declare function parseLogLines(logJson: LogJson): string;
|
|
|
30
30
|
export declare function convertToSASStatements(json: MacroVars): string;
|
|
31
31
|
export declare const arrToObj: (arr: any[]) => any;
|
|
32
32
|
export declare const millisecondsToDdHhMmSs: (milliseconds: number) => string;
|
|
33
|
-
export declare function checkNodeVersion(): void;
|
|
34
33
|
export declare function getAdapterInstance(target: Target): SASjs;
|
|
35
34
|
export declare function displaySasjsRunnerError(username: string): void;
|
|
36
35
|
export declare const loadEnvVariables: (fileName: string) => Promise<void>;
|
package/build/utils/utils.js
CHANGED
|
@@ -106,7 +106,6 @@ exports.executeShellScript = executeShellScript;
|
|
|
106
106
|
exports.executePowerShellScript = executePowerShellScript;
|
|
107
107
|
exports.parseLogLines = parseLogLines;
|
|
108
108
|
exports.convertToSASStatements = convertToSASStatements;
|
|
109
|
-
exports.checkNodeVersion = checkNodeVersion;
|
|
110
109
|
exports.getAdapterInstance = getAdapterInstance;
|
|
111
110
|
exports.displaySasjsRunnerError = displaySasjsRunnerError;
|
|
112
111
|
exports.prefixAppLoc = prefixAppLoc;
|
|
@@ -646,15 +645,6 @@ var millisecondsToDdHhMmSs = function (milliseconds) {
|
|
|
646
645
|
return "".concat(days, " day(s); ").concat(hours, " hour(s); ").concat(minutes, " minute(s); ").concat(seconds, " second(s)");
|
|
647
646
|
};
|
|
648
647
|
exports.millisecondsToDdHhMmSs = millisecondsToDdHhMmSs;
|
|
649
|
-
function checkNodeVersion() {
|
|
650
|
-
var _a;
|
|
651
|
-
var nodeVersion = process.versions.node;
|
|
652
|
-
var majorVersion = parseInt(nodeVersion.substr(0, 2));
|
|
653
|
-
if (majorVersion < 12) {
|
|
654
|
-
(_a = process.logger) === null || _a === void 0 ? void 0 : _a.error('SASjs CLI requires at least NodeJS version 12. Please upgrade NodeJS and try again.');
|
|
655
|
-
process.exit(1);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
648
|
function getAdapterInstance(target) {
|
|
659
649
|
if (!target) {
|
|
660
650
|
throw new Error('Unable to create SASjs adapter instance: Invalid target.');
|
package/package.json
CHANGED
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
"name": "@sasjs/cli",
|
|
3
3
|
"description": "Command line interface for SASjs",
|
|
4
4
|
"engines": {
|
|
5
|
-
"node": "
|
|
5
|
+
"node": ">=22"
|
|
6
6
|
},
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"files": [
|
|
9
9
|
"build/**/*"
|
|
10
10
|
],
|
|
11
11
|
"scripts": {
|
|
12
|
-
"nodeVersionMessage": "echo \u001b[33m make sure you are running node lts version \u001b[0m",
|
|
13
|
-
"checkNodeVersion": "node -e \"console.log('\\u001b[36m', 'Checking your Node.js version...'); const v = process.versions && process.versions.node; const ok = v && (() => { const [m, n] = v.split('.').map(Number); if (m === 14) return n >= 18; if (m === 15) return false; if (m === 16) return n >= 10; return m > 16 })(); if (!ok) { console.log('\\u001b[31m', '\\u274c You must be running Node.js ^14.18.0 || >=16.10.0 to install SASjs CLI.\\nYour current version is ' + v + '.\\nPlease install a more recent version and try again.'); process.exit(1) } else { console.log('\\u001b[32m', '\\u2705 Node.js version check passed. Continuing...') } console.log('\\u001b[0m', '');\"",
|
|
14
12
|
"start:ci": "npm run build && npm run set:permissions",
|
|
15
13
|
"start": "nodemon --watch \"src/**/*\" --exec \"npm run build && npm run set:permissions\"",
|
|
16
14
|
"set:permissions": "node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
@@ -71,16 +69,15 @@
|
|
|
71
69
|
"registry": "https://registry.npmjs.org"
|
|
72
70
|
},
|
|
73
71
|
"dependencies": {
|
|
74
|
-
"@sasjs/adapter": "
|
|
75
|
-
"@sasjs/core": "5.
|
|
76
|
-
"@sasjs/lint": "4.1.
|
|
77
|
-
"@sasjs/utils": "
|
|
72
|
+
"@sasjs/adapter": "5.1.1",
|
|
73
|
+
"@sasjs/core": "5.5.2",
|
|
74
|
+
"@sasjs/lint": "4.1.4",
|
|
75
|
+
"@sasjs/utils": "4.0.1",
|
|
76
|
+
"axios": "1.20.0",
|
|
78
77
|
"chalk": "4.1.2",
|
|
79
|
-
"dotenv": "
|
|
80
|
-
"js-base64": "3.9.
|
|
78
|
+
"dotenv": "18.0.8",
|
|
79
|
+
"js-base64": "3.9.4",
|
|
81
80
|
"jsdom": "26.0.0",
|
|
82
|
-
"jwt-decode": "3.1.2",
|
|
83
|
-
"lodash.groupby": "4.6.0",
|
|
84
81
|
"lodash.uniqby": "4.7.0",
|
|
85
82
|
"node-graphviz": "0.1.1",
|
|
86
83
|
"node-powershell": "5.0.1",
|
|
@@ -101,7 +98,6 @@
|
|
|
101
98
|
"@types/find": "0.2.4",
|
|
102
99
|
"@types/jest": "30.0.0",
|
|
103
100
|
"@types/jsdom": "21.1.1",
|
|
104
|
-
"@types/lodash.groupby": "4.6.9",
|
|
105
101
|
"@types/lodash.uniqby": "4.7.9",
|
|
106
102
|
"@types/node": "22.10.2",
|
|
107
103
|
"@types/prompts": "2.4.9",
|
|
@@ -109,17 +105,16 @@
|
|
|
109
105
|
"@types/xml": "1.0.11",
|
|
110
106
|
"@types/yauzl": "3.4.0",
|
|
111
107
|
"@types/yazl": "3.3.1",
|
|
112
|
-
"babel-jest": "30.5.
|
|
108
|
+
"babel-jest": "30.5.2",
|
|
113
109
|
"copyfiles": "2.4.1",
|
|
114
|
-
"jest": "30.5.
|
|
115
|
-
"jest-cli": "30.5.
|
|
116
|
-
"jest-environment-jsdom": "30.5.
|
|
117
|
-
"jest-extended": "
|
|
110
|
+
"jest": "30.5.2",
|
|
111
|
+
"jest-cli": "30.5.2",
|
|
112
|
+
"jest-environment-jsdom": "30.5.2",
|
|
113
|
+
"jest-extended": "7.0.0",
|
|
118
114
|
"nodemon": "3.1.14",
|
|
119
|
-
"prettier": "3.9.
|
|
120
|
-
"ts-jest": "29.4.
|
|
115
|
+
"prettier": "3.9.10",
|
|
116
|
+
"ts-jest": "29.4.15",
|
|
121
117
|
"ts-node": "10.9.2",
|
|
122
|
-
"tslib": "2.8.1",
|
|
123
118
|
"typescript": "5.9.3",
|
|
124
119
|
"util": "0.12.5"
|
|
125
120
|
},
|
|
@@ -127,5 +122,5 @@
|
|
|
127
122
|
"ws": "8.21.0",
|
|
128
123
|
"nanoid": "3.3.18"
|
|
129
124
|
},
|
|
130
|
-
"version": "
|
|
125
|
+
"version": "5.0.3"
|
|
131
126
|
}
|