@sasjs/cli 4.20.5 → 4.20.7
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/add/spec/addTarget.spec.js +5 -5
- package/build/commands/build/spec/build.spec.js +3 -3
- package/build/commands/compile/internal/spec/compileTestFile.spec.js +1 -1
- package/build/commands/compile/spec/compareFolders.spec.js +2 -1
- package/build/commands/compile/spec/compile.spec.js +7 -7
- package/build/commands/compile/spec/compileFile.spec.js +1 -1
- package/build/commands/compile/spec/copyFilesToBuildFolder.spec.js +1 -1
- package/build/commands/compile/spec/loadDependencies.spec.js +1 -1
- package/build/commands/create/spec/create.spec.js +45 -11
- package/build/commands/db/spec/db.spec.js +1 -1
- package/build/commands/deploy/spec/cbd.spec.js +1 -1
- package/build/commands/deploy/spec/deploy.spec.js +1 -1
- package/build/commands/deploy/spec/getDeployScripts.spec.js +1 -1
- package/build/commands/docs/generateDocs.js +2 -2
- package/build/commands/docs/spec/generateDocs.spec.js +41 -8
- package/build/commands/docs/spec/generateDot.spec.js +3 -3
- package/build/commands/docs/spec/initDocs.spec.js +1 -1
- package/build/commands/flow/internal/spec/saveToCsv.spec.js +4 -3
- package/build/commands/flow/internal/spec/validateParams.spec.js +2 -1
- package/build/commands/init/spec/init.spec.js +3 -3
- package/build/commands/testing/spec/testing.spec.js +1 -1
- package/build/commands/version/version.js +4 -9
- package/build/commands/web/internal/spec/updateScriptTag.spec.js +2 -1
- package/build/commands/web/internal/spec/updateStyleTag.spec.js +2 -1
- package/build/commands/web/spec/web.spec.js +1 -1
- package/build/utils/auth.d.ts +6 -0
- package/build/utils/auth.js +7 -1
- package/build/utils/shell.d.ts +60 -0
- package/build/utils/shell.js +155 -0
- package/build/utils/spec/auth.spec.js +13 -0
- package/build/utils/spec/config.spec.js +13 -13
- package/build/utils/spec/downloadFile.spec.js +37 -10
- package/build/utils/spec/file.spec.js +2 -1
- package/build/utils/spec/shell.spec.d.ts +1 -0
- package/build/utils/spec/shell.spec.js +156 -0
- package/build/utils/spec/utils.spec.js +5 -5
- package/build/utils/spec/zip.spec.js +2 -1
- package/build/utils/test.d.ts +10 -0
- package/build/utils/test.js +16 -1
- package/build/utils/utils.d.ts +2 -2
- package/build/utils/utils.js +65 -31
- package/package.json +5 -4
|
@@ -69,6 +69,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
69
69
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
70
70
|
var path_1 = __importDefault(require("path"));
|
|
71
71
|
var utils_1 = require("@sasjs/utils");
|
|
72
|
+
var test_1 = require("../../../../utils/test");
|
|
72
73
|
var updateScriptTag_1 = require("../updateScriptTag");
|
|
73
74
|
var utils_2 = require("../../../../utils");
|
|
74
75
|
describe('updateScriptTag', function () {
|
|
@@ -77,7 +78,7 @@ describe('updateScriptTag', function () {
|
|
|
77
78
|
return __generator(this, function (_a) {
|
|
78
79
|
switch (_a.label) {
|
|
79
80
|
case 0:
|
|
80
|
-
destinationPath = path_1.default.join(__dirname, "cli-test-web-updateScriptTag-".concat((0,
|
|
81
|
+
destinationPath = path_1.default.join(__dirname, "cli-test-web-updateScriptTag-".concat((0, test_1.generateUniqueTimestamp)()));
|
|
81
82
|
return [4 /*yield*/, (0, utils_1.createFolder)(destinationPath)];
|
|
82
83
|
case 1:
|
|
83
84
|
_a.sent();
|
|
@@ -69,6 +69,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
69
69
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
70
70
|
var path_1 = __importDefault(require("path"));
|
|
71
71
|
var utils_1 = require("@sasjs/utils");
|
|
72
|
+
var test_1 = require("../../../../utils/test");
|
|
72
73
|
var updateStyleTag_1 = require("../updateStyleTag");
|
|
73
74
|
var utils_2 = require("../../../../utils");
|
|
74
75
|
describe('updateStyleTag', function () {
|
|
@@ -77,7 +78,7 @@ describe('updateStyleTag', function () {
|
|
|
77
78
|
return __generator(this, function (_a) {
|
|
78
79
|
switch (_a.label) {
|
|
79
80
|
case 0:
|
|
80
|
-
destinationPath = path_1.default.join(__dirname, "cli-test-web-updateStyleTag-".concat((0,
|
|
81
|
+
destinationPath = path_1.default.join(__dirname, "cli-test-web-updateStyleTag-".concat((0, test_1.generateUniqueTimestamp)()));
|
|
81
82
|
return [4 /*yield*/, (0, utils_1.createFolder)(destinationPath)];
|
|
82
83
|
case 1:
|
|
83
84
|
_a.sent();
|
|
@@ -121,7 +121,7 @@ describe('sasjs web', function () {
|
|
|
121
121
|
return __generator(this, function (_b) {
|
|
122
122
|
switch (_b.label) {
|
|
123
123
|
case 0:
|
|
124
|
-
appName = "cli-test-web-minimal-".concat((0,
|
|
124
|
+
appName = "cli-test-web-minimal-".concat((0, test_1.generateUniqueTimestamp)());
|
|
125
125
|
return [4 /*yield*/, (0, test_1.createTestMinimalApp)(__dirname, appName)];
|
|
126
126
|
case 1:
|
|
127
127
|
_b.sent();
|
package/build/utils/auth.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { ServerType, Target } from '@sasjs/utils';
|
|
2
2
|
import SASjs from '@sasjs/adapter/node';
|
|
3
|
+
/**
|
|
4
|
+
* The URL a user visits to obtain an authorization code.
|
|
5
|
+
*
|
|
6
|
+
* SASjs Server serves its authorization code screen at /SASjsLogon - a real
|
|
7
|
+
* path on its single-page app. SAS Viya answers at its own /SASLogon endpoint.
|
|
8
|
+
*/
|
|
3
9
|
export declare const getAuthUrl: (serverType: ServerType, serverUrl: string, clientId: string) => string;
|
|
4
10
|
export declare function getAuthCode(authUrl: string): Promise<string>;
|
|
5
11
|
/**
|
package/build/utils/auth.js
CHANGED
|
@@ -44,9 +44,15 @@ exports.fetchLoggedInUser = fetchLoggedInUser;
|
|
|
44
44
|
exports.getNewAccessToken = getNewAccessToken;
|
|
45
45
|
var utils_1 = require("@sasjs/utils");
|
|
46
46
|
var node_1 = require("@sasjs/adapter/node");
|
|
47
|
+
/**
|
|
48
|
+
* The URL a user visits to obtain an authorization code.
|
|
49
|
+
*
|
|
50
|
+
* SASjs Server serves its authorization code screen at /SASjsLogon - a real
|
|
51
|
+
* path on its single-page app. SAS Viya answers at its own /SASLogon endpoint.
|
|
52
|
+
*/
|
|
47
53
|
var getAuthUrl = function (serverType, serverUrl, clientId) {
|
|
48
54
|
return serverType === utils_1.ServerType.Sasjs
|
|
49
|
-
? "".concat(serverUrl, "
|
|
55
|
+
? "".concat(serverUrl, "/SASjsLogon?client_id=").concat(clientId, "&response_type=code")
|
|
50
56
|
: "".concat(serverUrl, "/SASLogon/oauth/authorize?client_id=").concat(clientId, "&response_type=code");
|
|
51
57
|
};
|
|
52
58
|
exports.getAuthUrl = getAuthUrl;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell operations the CLI needs, on Node's own APIs.
|
|
3
|
+
*
|
|
4
|
+
* The CLI used shelljs for five things: run a command, copy a folder, remove a
|
|
5
|
+
* file or folder, list entries matching a pattern, and find an executable. Its
|
|
6
|
+
* current release pulls `fast-glob`, which pulls `micromatch`, which pulls
|
|
7
|
+
* `braces` - and `braces` has an unpatched stack-overflow advisory with no
|
|
8
|
+
* fixed release to move to. Nothing here needs a glob engine: the patterns are
|
|
9
|
+
* `*main`, `*master` and `*-main`, matched against one directory.
|
|
10
|
+
*
|
|
11
|
+
* The shapes match what the call sites already expect, so the behaviour they
|
|
12
|
+
* rely on - a command's stdout/stderr/exit code, and `cp -r <dir>/. <dest>`
|
|
13
|
+
* copying a directory's contents rather than the directory - is unchanged.
|
|
14
|
+
*/
|
|
15
|
+
export interface ShellResult {
|
|
16
|
+
stdout: string;
|
|
17
|
+
stderr: string;
|
|
18
|
+
code: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Runs a command through the platform shell.
|
|
22
|
+
*
|
|
23
|
+
* `silent` decides whether the command's output also reaches this process's
|
|
24
|
+
* streams, which is how shelljs' option of the same name behaved.
|
|
25
|
+
*/
|
|
26
|
+
export declare const exec: (command: string, options?: {
|
|
27
|
+
silent?: boolean;
|
|
28
|
+
}) => ShellResult;
|
|
29
|
+
/**
|
|
30
|
+
* Copies the contents of a folder into another folder, matching
|
|
31
|
+
* `cp -r <source>/. <destination>`.
|
|
32
|
+
*
|
|
33
|
+
* @param source - the folder to copy from, addressed as `<dir>/.` so that its
|
|
34
|
+
* contents land directly in the destination.
|
|
35
|
+
* @param destination - the folder to copy into.
|
|
36
|
+
*/
|
|
37
|
+
export declare const cp: (source: string, destination: string) => Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Removes files and folders. `recursive` matches `rm -rf`, and without it the
|
|
40
|
+
* call matches `rm -f`. A target containing `*` is expanded first, because
|
|
41
|
+
* shelljs expanded it and the call sites rely on that.
|
|
42
|
+
*/
|
|
43
|
+
export declare const rm: (targets: string[], recursive?: boolean) => void;
|
|
44
|
+
/**
|
|
45
|
+
* The entries of one directory whose name matches a pattern.
|
|
46
|
+
*
|
|
47
|
+
* Only `*` is supported, which is all the CLI's patterns use, and the result is
|
|
48
|
+
* addressed the same way the caller's pattern was - `./*main` answers with
|
|
49
|
+
* `./sasjs-react-seed-app-main`.
|
|
50
|
+
*/
|
|
51
|
+
export declare const ls: (pattern: string) => string[];
|
|
52
|
+
/**
|
|
53
|
+
* The path of an executable on this machine, or null when it is not there.
|
|
54
|
+
*
|
|
55
|
+
* POSIX `which`, because that is the only question asked of it: the one caller
|
|
56
|
+
* wants to know whether `wget` is available, and it only asks on Linux. Answer
|
|
57
|
+
* `where` when something needs to ask on Windows - a branch for it now would be
|
|
58
|
+
* unreachable, since the caller has already ruled Windows out.
|
|
59
|
+
*/
|
|
60
|
+
export declare const which: (command: string) => string | null;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __values = (this && this.__values) || function(o) {
|
|
3
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
4
|
+
if (m) return m.call(o);
|
|
5
|
+
if (o && typeof o.length === "number") return {
|
|
6
|
+
next: function () {
|
|
7
|
+
if (o && i >= o.length) o = void 0;
|
|
8
|
+
return { value: o && o[i++], done: !o };
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
12
|
+
};
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.which = exports.ls = exports.rm = exports.cp = exports.exec = void 0;
|
|
18
|
+
var child_process_1 = require("child_process");
|
|
19
|
+
var fs_1 = __importDefault(require("fs"));
|
|
20
|
+
var path_1 = __importDefault(require("path"));
|
|
21
|
+
var utils_1 = require("@sasjs/utils");
|
|
22
|
+
/**
|
|
23
|
+
* Runs a command through the platform shell.
|
|
24
|
+
*
|
|
25
|
+
* `silent` decides whether the command's output also reaches this process's
|
|
26
|
+
* streams, which is how shelljs' option of the same name behaved.
|
|
27
|
+
*/
|
|
28
|
+
var exec = function (command, options) {
|
|
29
|
+
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
|
+
var stdout = (_a = result.stdout) !== null && _a !== void 0 ? _a : '';
|
|
40
|
+
var stderr = (_b = result.stderr) !== null && _b !== void 0 ? _b : '';
|
|
41
|
+
if (!options.silent) {
|
|
42
|
+
if (stdout)
|
|
43
|
+
process.stdout.write(stdout);
|
|
44
|
+
if (stderr)
|
|
45
|
+
process.stderr.write(stderr);
|
|
46
|
+
}
|
|
47
|
+
// A killed command leaves status null and reports why on `error` - an
|
|
48
|
+
// exceeded buffer is ENOBUFS. Returning `status ?? 0` for that would report a
|
|
49
|
+
// command that never finished as a success.
|
|
50
|
+
if (result.error) {
|
|
51
|
+
return {
|
|
52
|
+
stdout: stdout,
|
|
53
|
+
stderr: [stderr, result.error.message].filter(Boolean).join('\n'),
|
|
54
|
+
code: 1
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return { stdout: stdout, stderr: stderr, code: (_c = result.status) !== null && _c !== void 0 ? _c : 0 };
|
|
58
|
+
};
|
|
59
|
+
exports.exec = exec;
|
|
60
|
+
/**
|
|
61
|
+
* Copies the contents of a folder into another folder, matching
|
|
62
|
+
* `cp -r <source>/. <destination>`.
|
|
63
|
+
*
|
|
64
|
+
* @param source - the folder to copy from, addressed as `<dir>/.` so that its
|
|
65
|
+
* contents land directly in the destination.
|
|
66
|
+
* @param destination - the folder to copy into.
|
|
67
|
+
*/
|
|
68
|
+
var cp = function (source, destination) {
|
|
69
|
+
return (0, utils_1.copy)(source, destination);
|
|
70
|
+
};
|
|
71
|
+
exports.cp = cp;
|
|
72
|
+
/**
|
|
73
|
+
* Removes files and folders. `recursive` matches `rm -rf`, and without it the
|
|
74
|
+
* call matches `rm -f`. A target containing `*` is expanded first, because
|
|
75
|
+
* shelljs expanded it and the call sites rely on that.
|
|
76
|
+
*/
|
|
77
|
+
var rm = function (targets, recursive) {
|
|
78
|
+
var e_1, _a, e_2, _b;
|
|
79
|
+
if (recursive === void 0) { recursive = false; }
|
|
80
|
+
try {
|
|
81
|
+
for (var targets_1 = __values(targets), targets_1_1 = targets_1.next(); !targets_1_1.done; targets_1_1 = targets_1.next()) {
|
|
82
|
+
var target = targets_1_1.value;
|
|
83
|
+
var resolved = target.includes('*') ? (0, exports.ls)(target) : [target];
|
|
84
|
+
try {
|
|
85
|
+
for (var resolved_1 = (e_2 = void 0, __values(resolved)), resolved_1_1 = resolved_1.next(); !resolved_1_1.done; resolved_1_1 = resolved_1.next()) {
|
|
86
|
+
var entry = resolved_1_1.value;
|
|
87
|
+
fs_1.default.rmSync(entry, { recursive: recursive, force: true });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
91
|
+
finally {
|
|
92
|
+
try {
|
|
93
|
+
if (resolved_1_1 && !resolved_1_1.done && (_b = resolved_1.return)) _b.call(resolved_1);
|
|
94
|
+
}
|
|
95
|
+
finally { if (e_2) throw e_2.error; }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
100
|
+
finally {
|
|
101
|
+
try {
|
|
102
|
+
if (targets_1_1 && !targets_1_1.done && (_a = targets_1.return)) _a.call(targets_1);
|
|
103
|
+
}
|
|
104
|
+
finally { if (e_1) throw e_1.error; }
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
exports.rm = rm;
|
|
108
|
+
/**
|
|
109
|
+
* The entries of one directory whose name matches a pattern.
|
|
110
|
+
*
|
|
111
|
+
* Only `*` is supported, which is all the CLI's patterns use, and the result is
|
|
112
|
+
* addressed the same way the caller's pattern was - `./*main` answers with
|
|
113
|
+
* `./sasjs-react-seed-app-main`.
|
|
114
|
+
*/
|
|
115
|
+
var ls = function (pattern) {
|
|
116
|
+
var directory = path_1.default.dirname(pattern);
|
|
117
|
+
var matcher = new RegExp('^' +
|
|
118
|
+
path_1.default
|
|
119
|
+
.basename(pattern)
|
|
120
|
+
.split('*')
|
|
121
|
+
.map(function (part) { return part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); })
|
|
122
|
+
.join('.*') +
|
|
123
|
+
'$');
|
|
124
|
+
var entries;
|
|
125
|
+
try {
|
|
126
|
+
entries = fs_1.default.readdirSync(directory);
|
|
127
|
+
}
|
|
128
|
+
catch (_a) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
var matches = entries.filter(function (entry) { return matcher.test(entry); }).sort();
|
|
132
|
+
// `path.join` would drop the `./`, and the callers pass the result straight
|
|
133
|
+
// to a shell-style path, so the prefix is rebuilt rather than normalised.
|
|
134
|
+
return matches.map(function (entry) {
|
|
135
|
+
return pattern.startsWith('./') ? "./".concat(entry) : path_1.default.join(directory, entry);
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
exports.ls = ls;
|
|
139
|
+
/**
|
|
140
|
+
* The path of an executable on this machine, or null when it is not there.
|
|
141
|
+
*
|
|
142
|
+
* POSIX `which`, because that is the only question asked of it: the one caller
|
|
143
|
+
* wants to know whether `wget` is available, and it only asks on Linux. Answer
|
|
144
|
+
* `where` when something needs to ask on Windows - a branch for it now would be
|
|
145
|
+
* unreachable, since the caller has already ruled Windows out.
|
|
146
|
+
*/
|
|
147
|
+
var which = function (command) {
|
|
148
|
+
var _a;
|
|
149
|
+
var result = (0, child_process_1.spawnSync)('which', [command], { encoding: 'utf8' });
|
|
150
|
+
if (result.status !== 0)
|
|
151
|
+
return null;
|
|
152
|
+
var first = ((_a = result.stdout) !== null && _a !== void 0 ? _a : '').split('\n')[0].trim();
|
|
153
|
+
return first || null;
|
|
154
|
+
};
|
|
155
|
+
exports.which = which;
|
|
@@ -86,6 +86,19 @@ var target = new utils_1.Target({
|
|
|
86
86
|
appLoc: '/Public/test',
|
|
87
87
|
contextName: 'test context'
|
|
88
88
|
});
|
|
89
|
+
describe('getAuthUrl', function () {
|
|
90
|
+
it('should address the SASjs Server authorization screen as a path', function () {
|
|
91
|
+
expect((0, auth_1.getAuthUrl)(utils_1.ServerType.Sasjs, 'https://server.example.com', 'my-client')).toBe('https://server.example.com/SASjsLogon?client_id=my-client&response_type=code');
|
|
92
|
+
});
|
|
93
|
+
it('should build the Viya authorize URL for a SAS Viya server', function () {
|
|
94
|
+
expect((0, auth_1.getAuthUrl)(utils_1.ServerType.SasViya, 'https://viya.example.com', 'my-client')).toBe('https://viya.example.com/SASLogon/oauth/authorize?client_id=my-client&response_type=code');
|
|
95
|
+
});
|
|
96
|
+
it('should keep the route out of the fragment', function () {
|
|
97
|
+
// The screen used to live at /#/SASjsLogon. A fragment is not sent to the
|
|
98
|
+
// server, so the path is what makes a deep link work.
|
|
99
|
+
expect((0, auth_1.getAuthUrl)(utils_1.ServerType.Sasjs, 'https://server.example.com', 'my-client')).not.toContain('#');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
89
102
|
describe('fetchLoggedInUser', function () {
|
|
90
103
|
afterEach(function () {
|
|
91
104
|
jest.clearAllMocks();
|
|
@@ -456,7 +456,7 @@ describe('getAuthConfig - opaque refresh tokens', function () {
|
|
|
456
456
|
return __generator(this, function (_a) {
|
|
457
457
|
switch (_a.label) {
|
|
458
458
|
case 0:
|
|
459
|
-
projectDir = path_1.default.join(process.env.TEMP || process.env.TMP || '/tmp', "sasjs-cli-test-".concat((0,
|
|
459
|
+
projectDir = path_1.default.join(process.env.TEMP || process.env.TMP || '/tmp', "sasjs-cli-test-".concat((0, test_1.generateUniqueTimestamp)()));
|
|
460
460
|
process.projectDir = projectDir;
|
|
461
461
|
return [4 /*yield*/, (0, setConstants_1.setConstants)()];
|
|
462
462
|
case 1:
|
|
@@ -752,7 +752,7 @@ describe('saveToGlobalConfig', function () {
|
|
|
752
752
|
return __generator(this, function (_b) {
|
|
753
753
|
switch (_b.label) {
|
|
754
754
|
case 0:
|
|
755
|
-
appName = 'cli-tests-config-' + (0,
|
|
755
|
+
appName = 'cli-tests-config-' + (0, test_1.generateUniqueTimestamp)();
|
|
756
756
|
target = (0, test_1.generateTestTarget)(appName, "/Public/app/cli-tests/".concat(appName));
|
|
757
757
|
return [4 /*yield*/, (0, config_1.saveToGlobalConfig)(target, true)];
|
|
758
758
|
case 1:
|
|
@@ -776,7 +776,7 @@ describe('saveToGlobalConfig', function () {
|
|
|
776
776
|
return __generator(this, function (_b) {
|
|
777
777
|
switch (_b.label) {
|
|
778
778
|
case 0:
|
|
779
|
-
appName = 'cli-tests-config-' + (0,
|
|
779
|
+
appName = 'cli-tests-config-' + (0, test_1.generateUniqueTimestamp)();
|
|
780
780
|
target = (0, test_1.generateTestTarget)(appName, "/Public/app/cli-tests/".concat(appName));
|
|
781
781
|
return [4 /*yield*/, (0, config_1.saveToGlobalConfig)(target, false)];
|
|
782
782
|
case 1:
|
|
@@ -814,7 +814,7 @@ describe('removeFromGlobalConfig', function () {
|
|
|
814
814
|
return __generator(this, function (_b) {
|
|
815
815
|
switch (_b.label) {
|
|
816
816
|
case 0:
|
|
817
|
-
appName = 'cli-tests-config-' + (0,
|
|
817
|
+
appName = 'cli-tests-config-' + (0, test_1.generateUniqueTimestamp)();
|
|
818
818
|
target = (0, test_1.generateTestTarget)(appName, "/Public/app/cli-tests/".concat(appName));
|
|
819
819
|
return [4 /*yield*/, (0, config_1.saveToGlobalConfig)(target, true)];
|
|
820
820
|
case 1:
|
|
@@ -842,8 +842,8 @@ describe('removeFromGlobalConfig', function () {
|
|
|
842
842
|
return __generator(this, function (_d) {
|
|
843
843
|
switch (_d.label) {
|
|
844
844
|
case 0:
|
|
845
|
-
appName1 = 'cli-tests-config-1-' + (0,
|
|
846
|
-
appName2 = 'cli-tests-config-2-' + (0,
|
|
845
|
+
appName1 = 'cli-tests-config-1-' + (0, test_1.generateUniqueTimestamp)();
|
|
846
|
+
appName2 = 'cli-tests-config-2-' + (0, test_1.generateUniqueTimestamp)();
|
|
847
847
|
target1 = (0, test_1.generateTestTarget)(appName1, "/Public/app/cli-tests/".concat(appName1));
|
|
848
848
|
target2 = (0, test_1.generateTestTarget)(appName2, "/Public/app/cli-tests/".concat(appName2));
|
|
849
849
|
return [4 /*yield*/, (0, config_1.saveToGlobalConfig)(target1, true)];
|
|
@@ -880,7 +880,7 @@ describe('saveToLocalConfig', function () {
|
|
|
880
880
|
return __generator(this, function (_a) {
|
|
881
881
|
switch (_a.label) {
|
|
882
882
|
case 0:
|
|
883
|
-
appName = "cli-tests-config-".concat((0,
|
|
883
|
+
appName = "cli-tests-config-".concat((0, test_1.generateUniqueTimestamp)());
|
|
884
884
|
return [4 /*yield*/, (0, test_1.createTestMinimalApp)(__dirname, appName)];
|
|
885
885
|
case 1:
|
|
886
886
|
_a.sent();
|
|
@@ -904,7 +904,7 @@ describe('saveToLocalConfig', function () {
|
|
|
904
904
|
return __generator(this, function (_b) {
|
|
905
905
|
switch (_b.label) {
|
|
906
906
|
case 0:
|
|
907
|
-
appName = 'cli-tests-cb-' + (0,
|
|
907
|
+
appName = 'cli-tests-cb-' + (0, test_1.generateUniqueTimestamp)();
|
|
908
908
|
target = (0, test_1.generateTestTarget)(appName, "/Public/app/cli-tests/".concat(appName));
|
|
909
909
|
return [4 /*yield*/, (0, config_1.saveToLocalConfig)(target, true)];
|
|
910
910
|
case 1:
|
|
@@ -928,7 +928,7 @@ describe('saveToLocalConfig', function () {
|
|
|
928
928
|
return __generator(this, function (_b) {
|
|
929
929
|
switch (_b.label) {
|
|
930
930
|
case 0:
|
|
931
|
-
appName = 'cli-tests-cb-' + (0,
|
|
931
|
+
appName = 'cli-tests-cb-' + (0, test_1.generateUniqueTimestamp)();
|
|
932
932
|
target = (0, test_1.generateTestTarget)(appName, "/Public/app/cli-tests/".concat(appName));
|
|
933
933
|
return [4 /*yield*/, (0, config_1.saveToLocalConfig)(target, false)];
|
|
934
934
|
case 1:
|
|
@@ -953,7 +953,7 @@ describe('removeFromLocalConfig', function () {
|
|
|
953
953
|
return __generator(this, function (_a) {
|
|
954
954
|
switch (_a.label) {
|
|
955
955
|
case 0:
|
|
956
|
-
appName = "cli-tests-config-".concat((0,
|
|
956
|
+
appName = "cli-tests-config-".concat((0, test_1.generateUniqueTimestamp)());
|
|
957
957
|
return [4 /*yield*/, (0, test_1.createTestMinimalApp)(__dirname, appName)];
|
|
958
958
|
case 1:
|
|
959
959
|
_a.sent();
|
|
@@ -977,7 +977,7 @@ describe('removeFromLocalConfig', function () {
|
|
|
977
977
|
return __generator(this, function (_b) {
|
|
978
978
|
switch (_b.label) {
|
|
979
979
|
case 0:
|
|
980
|
-
appName = 'cli-tests-config-' + (0,
|
|
980
|
+
appName = 'cli-tests-config-' + (0, test_1.generateUniqueTimestamp)();
|
|
981
981
|
target = (0, test_1.generateTestTarget)(appName, "/Public/app/cli-tests/".concat(appName));
|
|
982
982
|
return [4 /*yield*/, (0, config_1.saveToLocalConfig)(target, true)];
|
|
983
983
|
case 1:
|
|
@@ -1005,8 +1005,8 @@ describe('removeFromLocalConfig', function () {
|
|
|
1005
1005
|
return __generator(this, function (_d) {
|
|
1006
1006
|
switch (_d.label) {
|
|
1007
1007
|
case 0:
|
|
1008
|
-
appName1 = 'cli-tests-config-1-' + (0,
|
|
1009
|
-
appName2 = 'cli-tests-config-2-' + (0,
|
|
1008
|
+
appName1 = 'cli-tests-config-1-' + (0, test_1.generateUniqueTimestamp)();
|
|
1009
|
+
appName2 = 'cli-tests-config-2-' + (0, test_1.generateUniqueTimestamp)();
|
|
1010
1010
|
target1 = (0, test_1.generateTestTarget)(appName1, "/Public/app/cli-tests/".concat(appName1));
|
|
1011
1011
|
target2 = (0, test_1.generateTestTarget)(appName2, "/Public/app/cli-tests/".concat(appName2));
|
|
1012
1012
|
return [4 /*yield*/, (0, config_1.saveToLocalConfig)(target1, true)];
|
|
@@ -10,21 +10,48 @@ var __assign = (this && this.__assign) || function () {
|
|
|
10
10
|
};
|
|
11
11
|
return __assign.apply(this, arguments);
|
|
12
12
|
};
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
16
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
var
|
|
47
|
+
var shell = __importStar(require("../shell"));
|
|
18
48
|
var utils_1 = require("@sasjs/utils");
|
|
19
49
|
var utils_2 = require("../utils");
|
|
20
|
-
jest.mock('
|
|
21
|
-
exec: jest.fn(function () { return ({ stdout: '', stderr: '', code: 0 }); }),
|
|
22
|
-
which: jest.fn()
|
|
23
|
-
}); });
|
|
50
|
+
jest.mock('../shell', function () { return (__assign(__assign({}, jest.requireActual('../shell')), { exec: jest.fn(function () { return ({ stdout: '', stderr: '', code: 0 }); }), which: jest.fn() })); });
|
|
24
51
|
jest.mock('@sasjs/utils', function () { return (__assign(__assign({}, jest.requireActual('@sasjs/utils')), { isLinux: jest.fn(), isWindows: jest.fn() })); });
|
|
25
52
|
describe('downloadFile', function () {
|
|
26
|
-
var exec =
|
|
27
|
-
var which =
|
|
53
|
+
var exec = shell.exec;
|
|
54
|
+
var which = shell.which;
|
|
28
55
|
var linux = utils_1.isLinux;
|
|
29
56
|
var windows = utils_1.isWindows;
|
|
30
57
|
beforeEach(function () {
|
|
@@ -41,9 +41,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
42
|
var path_1 = __importDefault(require("path"));
|
|
43
43
|
var utils_1 = require("@sasjs/utils");
|
|
44
|
+
var test_1 = require("../test");
|
|
44
45
|
describe('file utility', function () {
|
|
45
46
|
describe('createFile', function () {
|
|
46
|
-
var filename = "test-create-file-".concat((0,
|
|
47
|
+
var filename = "test-create-file-".concat((0, test_1.generateUniqueTimestamp)(), ".txt");
|
|
47
48
|
it('should create a file', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
48
49
|
var filePath, content;
|
|
49
50
|
return __generator(this, function (_a) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
var fs_1 = __importDefault(require("fs"));
|
|
7
|
+
var os_1 = __importDefault(require("os"));
|
|
8
|
+
var path_1 = __importDefault(require("path"));
|
|
9
|
+
var shell_1 = require("../shell");
|
|
10
|
+
/**
|
|
11
|
+
* The shell wrapper, directly.
|
|
12
|
+
*
|
|
13
|
+
* Every other suite replaces this module with a mock, so nothing exercised it -
|
|
14
|
+
* which is how the maxBuffer mapping shipped reporting a killed command as a
|
|
15
|
+
* success. These cover the behaviours the call sites rely on: a command's
|
|
16
|
+
* stdout/stderr/exit code, `ls` matching a suffix pattern, `rm` expanding one,
|
|
17
|
+
* and `cp` copying a directory's contents rather than the directory.
|
|
18
|
+
*/
|
|
19
|
+
describe('shell', function () {
|
|
20
|
+
var root;
|
|
21
|
+
beforeEach(function () {
|
|
22
|
+
root = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'sasjs-shell-spec-'));
|
|
23
|
+
});
|
|
24
|
+
afterEach(function () {
|
|
25
|
+
fs_1.default.rmSync(root, { recursive: true, force: true });
|
|
26
|
+
});
|
|
27
|
+
describe('exec', function () {
|
|
28
|
+
it('should answer with stdout and a zero code for a command that succeeds', function () {
|
|
29
|
+
var result = (0, shell_1.exec)("node -e \"process.stdout.write('hello')\"", {
|
|
30
|
+
silent: true
|
|
31
|
+
});
|
|
32
|
+
expect(result.stdout).toEqual('hello');
|
|
33
|
+
expect(result.stderr).toEqual('');
|
|
34
|
+
expect(result.code).toEqual(0);
|
|
35
|
+
});
|
|
36
|
+
it('should answer with the exit code and stderr for a command that fails', function () {
|
|
37
|
+
var result = (0, shell_1.exec)("node -e \"process.stderr.write('boom'); process.exit(3)\"", { silent: true });
|
|
38
|
+
expect(result.code).toEqual(3);
|
|
39
|
+
expect(result.stderr).toContain('boom');
|
|
40
|
+
});
|
|
41
|
+
it('should report a command killed by the output ceiling as a failure', function () {
|
|
42
|
+
// A command that exceeds maxBuffer is killed, and spawnSync reports that
|
|
43
|
+
// with status null and the reason on `error`. Mapping `status ?? 0` would
|
|
44
|
+
// call it a success, which is what a chatty deploy script would hit.
|
|
45
|
+
var result = (0, shell_1.exec)("node -e \"process.stdout.write('x'.repeat(25 * 1024 * 1024))\"", { silent: true });
|
|
46
|
+
expect(result.code).not.toEqual(0);
|
|
47
|
+
expect(result.stderr).not.toEqual('');
|
|
48
|
+
});
|
|
49
|
+
it('should keep a command that is merely chatty', function () {
|
|
50
|
+
// 3MB is over spawnSync's 1MB default and under the ceiling this module
|
|
51
|
+
// sets, so the command must finish rather than be killed.
|
|
52
|
+
var result = (0, shell_1.exec)("node -e \"process.stdout.write('x'.repeat(3 * 1024 * 1024))\"", { silent: true });
|
|
53
|
+
expect(result.code).toEqual(0);
|
|
54
|
+
expect(result.stdout.length).toEqual(3 * 1024 * 1024);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('ls', function () {
|
|
58
|
+
beforeEach(function () {
|
|
59
|
+
fs_1.default.mkdirSync(path_1.default.join(root, 'sasjs-react-seed-app-main'));
|
|
60
|
+
fs_1.default.mkdirSync(path_1.default.join(root, 'docs-main'));
|
|
61
|
+
fs_1.default.mkdirSync(path_1.default.join(root, 'unrelated'));
|
|
62
|
+
fs_1.default.writeFileSync(path_1.default.join(root, 'main.zip'), '');
|
|
63
|
+
});
|
|
64
|
+
it('should match entries by a suffix pattern', function () {
|
|
65
|
+
expect((0, shell_1.ls)(path_1.default.join(root, '*main')).sort()).toEqual([
|
|
66
|
+
path_1.default.join(root, 'docs-main'),
|
|
67
|
+
path_1.default.join(root, 'sasjs-react-seed-app-main')
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
it('should match a pattern that narrows the suffix', function () {
|
|
71
|
+
expect((0, shell_1.ls)(path_1.default.join(root, '*-main'))).toEqual([
|
|
72
|
+
path_1.default.join(root, 'docs-main'),
|
|
73
|
+
path_1.default.join(root, 'sasjs-react-seed-app-main')
|
|
74
|
+
]);
|
|
75
|
+
});
|
|
76
|
+
it('should keep the ./ prefix the caller passed', function () {
|
|
77
|
+
// createApp passes the result straight to a shell-style path, so the
|
|
78
|
+
// prefix survives rather than being normalised away. The pattern is
|
|
79
|
+
// relative, so it resolves against the working directory.
|
|
80
|
+
var cwd = process.cwd();
|
|
81
|
+
try {
|
|
82
|
+
process.chdir(root);
|
|
83
|
+
var matches = (0, shell_1.ls)('./*main');
|
|
84
|
+
expect(matches.sort()).toEqual([
|
|
85
|
+
'./docs-main',
|
|
86
|
+
'./sasjs-react-seed-app-main'
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
process.chdir(cwd);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
it('should answer with nothing when the directory does not exist', function () {
|
|
94
|
+
expect((0, shell_1.ls)(path_1.default.join(root, 'no-such-dir', '*main'))).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
it('should answer with nothing when nothing matches', function () {
|
|
97
|
+
expect((0, shell_1.ls)(path_1.default.join(root, '*nothing-like-this'))).toEqual([]);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('rm', function () {
|
|
101
|
+
it('should expand a pattern before removing', function () {
|
|
102
|
+
fs_1.default.mkdirSync(path_1.default.join(root, 'sasjs-react-seed-app-main'));
|
|
103
|
+
fs_1.default.mkdirSync(path_1.default.join(root, 'docs-main'));
|
|
104
|
+
fs_1.default.mkdirSync(path_1.default.join(root, 'keep-me'));
|
|
105
|
+
(0, shell_1.rm)([path_1.default.join(root, '*main')], true);
|
|
106
|
+
expect(fs_1.default.existsSync(path_1.default.join(root, 'sasjs-react-seed-app-main'))).toBe(false);
|
|
107
|
+
expect(fs_1.default.existsSync(path_1.default.join(root, 'docs-main'))).toBe(false);
|
|
108
|
+
expect(fs_1.default.existsSync(path_1.default.join(root, 'keep-me'))).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
it('should remove a directory and its contents when recursive', function () {
|
|
111
|
+
var target = path_1.default.join(root, 'nested');
|
|
112
|
+
fs_1.default.mkdirSync(path_1.default.join(target, 'deep'), { recursive: true });
|
|
113
|
+
fs_1.default.writeFileSync(path_1.default.join(target, 'deep', 'file.txt'), 'x');
|
|
114
|
+
(0, shell_1.rm)([target], true);
|
|
115
|
+
expect(fs_1.default.existsSync(target)).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
it('should remove a single file', function () {
|
|
118
|
+
var file = path_1.default.join(root, 'main.zip');
|
|
119
|
+
fs_1.default.writeFileSync(file, '');
|
|
120
|
+
(0, shell_1.rm)([file]);
|
|
121
|
+
expect(fs_1.default.existsSync(file)).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
it('should not throw for something that is not there', function () {
|
|
124
|
+
expect(function () { return (0, shell_1.rm)([path_1.default.join(root, 'missing')], true); }).not.toThrow();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
describe('cp', function () {
|
|
128
|
+
it("should copy a directory's contents into the destination", function () {
|
|
129
|
+
var source = path_1.default.join(root, 'sasjs-react-seed-app-main');
|
|
130
|
+
fs_1.default.mkdirSync(path_1.default.join(source, 'nested'), { recursive: true });
|
|
131
|
+
fs_1.default.writeFileSync(path_1.default.join(source, 'package.json'), '{}');
|
|
132
|
+
fs_1.default.writeFileSync(path_1.default.join(source, 'nested', 'file.txt'), 'x');
|
|
133
|
+
var destination = path_1.default.join(root, 'my-new-app');
|
|
134
|
+
fs_1.default.mkdirSync(destination);
|
|
135
|
+
return (0, shell_1.cp)("".concat(source, "/."), destination).then(function () {
|
|
136
|
+
// The contents land in the destination, not a folder named after the
|
|
137
|
+
// source - the difference between `cp -r <dir>/. <dest>` and
|
|
138
|
+
// `cp -r <dir> <dest>`, which the create flow depends on.
|
|
139
|
+
expect(fs_1.default.existsSync(path_1.default.join(destination, 'package.json'))).toBe(true);
|
|
140
|
+
expect(fs_1.default.existsSync(path_1.default.join(destination, 'nested', 'file.txt'))).toBe(true);
|
|
141
|
+
expect(fs_1.default.existsSync(path_1.default.join(destination, 'sasjs-react-seed-app-main'))).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
// `which` answers the POSIX question its one caller asks, and that caller
|
|
146
|
+
// only asks on Linux, so the assertions are Linux-only.
|
|
147
|
+
var describePosix = process.platform === 'win32' ? describe.skip : describe;
|
|
148
|
+
describePosix('which', function () {
|
|
149
|
+
it('should answer with the path of an executable that is there', function () {
|
|
150
|
+
expect((0, shell_1.which)('sh')).toMatch(/sh$/);
|
|
151
|
+
});
|
|
152
|
+
it('should answer with null for one that is not', function () {
|
|
153
|
+
expect((0, shell_1.which)('definitely-not-a-real-executable-xyz')).toBeNull();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|