libdragon 10.8.0 → 10.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -29
- package/modules/actions/destroy.js +6 -3
- package/modules/actions/disasm.js +10 -2
- package/modules/actions/docker-utils.js +5 -0
- package/modules/actions/exec.js +36 -5
- package/modules/actions/help.js +9 -6
- package/modules/actions/init.js +44 -18
- package/modules/actions/install.js +4 -8
- package/modules/actions/make.js +5 -2
- package/modules/actions/npm-utils.js +8 -2
- package/modules/actions/start.js +10 -3
- package/modules/actions/stop.js +6 -2
- package/modules/actions/update-and-start.js +3 -0
- package/modules/actions/update.js +6 -2
- package/modules/actions/utils.js +25 -3
- package/modules/actions/version.js +3 -3
- package/modules/constants.js +4 -2
- package/modules/helpers.js +126 -7
- package/modules/parameters.js +44 -13
- package/modules/project-info.js +52 -15
- package/package.json +9 -5
package/modules/actions/start.js
CHANGED
|
@@ -11,11 +11,11 @@ const {
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Create a new container
|
|
14
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
14
15
|
*/
|
|
15
16
|
const initContainer = async (libdragonInfo) => {
|
|
16
17
|
let newId;
|
|
17
18
|
try {
|
|
18
|
-
// Create a new container
|
|
19
19
|
log('Creating new container...');
|
|
20
20
|
newId = (
|
|
21
21
|
await spawnProcess('docker', [
|
|
@@ -75,6 +75,9 @@ const initContainer = async (libdragonInfo) => {
|
|
|
75
75
|
return newId;
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
80
|
+
*/
|
|
78
81
|
const start = async (libdragonInfo) => {
|
|
79
82
|
const running =
|
|
80
83
|
libdragonInfo.containerId &&
|
|
@@ -99,14 +102,18 @@ const start = async (libdragonInfo) => {
|
|
|
99
102
|
return id;
|
|
100
103
|
};
|
|
101
104
|
|
|
102
|
-
module.exports = {
|
|
105
|
+
module.exports = /** @type {const} */ ({
|
|
103
106
|
name: 'start',
|
|
107
|
+
/**
|
|
108
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
109
|
+
*/
|
|
104
110
|
fn: async (libdragonInfo) => {
|
|
105
111
|
const containerId = await start(libdragonInfo);
|
|
106
112
|
print(containerId);
|
|
107
113
|
return { ...libdragonInfo, containerId };
|
|
108
114
|
},
|
|
109
115
|
start,
|
|
116
|
+
forwardsRestParams: false,
|
|
110
117
|
usage: {
|
|
111
118
|
name: 'start',
|
|
112
119
|
summary: 'Start the container for current project.',
|
|
@@ -114,4 +121,4 @@ module.exports = {
|
|
|
114
121
|
|
|
115
122
|
Must be run in an initialized libdragon project.`,
|
|
116
123
|
},
|
|
117
|
-
};
|
|
124
|
+
});
|
package/modules/actions/stop.js
CHANGED
|
@@ -2,6 +2,9 @@ const { spawnProcess } = require('../helpers');
|
|
|
2
2
|
|
|
3
3
|
const { checkContainerRunning } = require('./utils');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
7
|
+
*/
|
|
5
8
|
const stop = async (libdragonInfo) => {
|
|
6
9
|
const running =
|
|
7
10
|
libdragonInfo.containerId &&
|
|
@@ -14,9 +17,10 @@ const stop = async (libdragonInfo) => {
|
|
|
14
17
|
return libdragonInfo;
|
|
15
18
|
};
|
|
16
19
|
|
|
17
|
-
module.exports = {
|
|
20
|
+
module.exports = /** @type {const} */ ({
|
|
18
21
|
name: 'stop',
|
|
19
22
|
fn: stop,
|
|
23
|
+
forwardsRestParams: false,
|
|
20
24
|
usage: {
|
|
21
25
|
name: 'stop',
|
|
22
26
|
summary: 'Stop the container for current project.',
|
|
@@ -24,4 +28,4 @@ module.exports = {
|
|
|
24
28
|
|
|
25
29
|
Must be run in an initialized libdragon project.`,
|
|
26
30
|
},
|
|
27
|
-
};
|
|
31
|
+
});
|
|
@@ -2,6 +2,9 @@ const { log } = require('../helpers');
|
|
|
2
2
|
const { updateImage, destroyContainer } = require('./utils');
|
|
3
3
|
const { start } = require('./start');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
7
|
+
*/
|
|
5
8
|
async function syncImageAndStart(libdragonInfo) {
|
|
6
9
|
const oldImageName = libdragonInfo.imageName;
|
|
7
10
|
const imageName = libdragonInfo.options.DOCKER_IMAGE ?? oldImageName;
|
|
@@ -3,6 +3,9 @@ const { LIBDRAGON_GIT, LIBDRAGON_BRANCH } = require('../constants');
|
|
|
3
3
|
const { runGitMaybeHost, installDependencies } = require('./utils');
|
|
4
4
|
const { syncImageAndStart } = require('./update-and-start');
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @param {import('../project-info').LibdragonInfo} info
|
|
8
|
+
*/
|
|
6
9
|
const update = async (info) => {
|
|
7
10
|
info = await syncImageAndStart(info);
|
|
8
11
|
|
|
@@ -33,9 +36,10 @@ const update = async (info) => {
|
|
|
33
36
|
await installDependencies(info);
|
|
34
37
|
};
|
|
35
38
|
|
|
36
|
-
module.exports = {
|
|
39
|
+
module.exports = /** @type {const} */ ({
|
|
37
40
|
name: 'update',
|
|
38
41
|
fn: update,
|
|
42
|
+
forwardsRestParams: false,
|
|
39
43
|
usage: {
|
|
40
44
|
name: 'update',
|
|
41
45
|
summary: 'Update libdragon and do an install.',
|
|
@@ -44,4 +48,4 @@ module.exports = {
|
|
|
44
48
|
Must be run in an initialized libdragon project.`,
|
|
45
49
|
group: ['docker'],
|
|
46
50
|
},
|
|
47
|
-
};
|
|
51
|
+
});
|
package/modules/actions/utils.js
CHANGED
|
@@ -21,6 +21,9 @@ const {
|
|
|
21
21
|
const { dockerHostUserParams } = require('./docker-utils');
|
|
22
22
|
const { installNPMDependencies } = require('./npm-utils');
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
26
|
+
*/
|
|
24
27
|
const installDependencies = async (libdragonInfo) => {
|
|
25
28
|
const buildScriptPath = path.join(
|
|
26
29
|
libdragonInfo.root,
|
|
@@ -51,9 +54,8 @@ const installDependencies = async (libdragonInfo) => {
|
|
|
51
54
|
/**
|
|
52
55
|
* Downloads the given docker image. Returns false if the local image is the
|
|
53
56
|
* same, new image name otherwise.
|
|
54
|
-
* @param libdragonInfo
|
|
55
|
-
* @param newImageName
|
|
56
|
-
* @returns false | string
|
|
57
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
58
|
+
* @param {string} newImageName
|
|
57
59
|
*/
|
|
58
60
|
const updateImage = async (libdragonInfo, newImageName) => {
|
|
59
61
|
// Will not take too much time if already have the same
|
|
@@ -90,6 +92,9 @@ const updateImage = async (libdragonInfo, newImageName) => {
|
|
|
90
92
|
return newImageName;
|
|
91
93
|
};
|
|
92
94
|
|
|
95
|
+
/**
|
|
96
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
97
|
+
*/
|
|
93
98
|
const destroyContainer = async (libdragonInfo) => {
|
|
94
99
|
if (libdragonInfo.containerId) {
|
|
95
100
|
await spawnProcess('docker', [
|
|
@@ -110,6 +115,14 @@ const destroyContainer = async (libdragonInfo) => {
|
|
|
110
115
|
* Invokes host git with provided params. If host does not have git, falls back
|
|
111
116
|
* to the docker git, with the nix user set to the user running libdragon.
|
|
112
117
|
*/
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
*
|
|
121
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
122
|
+
* @param {string[]} params
|
|
123
|
+
* @param {import('../helpers').SpawnOptions} options
|
|
124
|
+
* @returns
|
|
125
|
+
*/
|
|
113
126
|
async function runGitMaybeHost(libdragonInfo, params, options = {}) {
|
|
114
127
|
assert(
|
|
115
128
|
libdragonInfo.vendorStrategy !== 'manual',
|
|
@@ -143,6 +156,9 @@ async function runGitMaybeHost(libdragonInfo, params, options = {}) {
|
|
|
143
156
|
}
|
|
144
157
|
}
|
|
145
158
|
|
|
159
|
+
/**
|
|
160
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
161
|
+
*/
|
|
146
162
|
async function checkContainerAndClean(libdragonInfo) {
|
|
147
163
|
const id =
|
|
148
164
|
libdragonInfo.containerId &&
|
|
@@ -169,6 +185,9 @@ async function checkContainerAndClean(libdragonInfo) {
|
|
|
169
185
|
return id ? libdragonInfo.containerId : undefined;
|
|
170
186
|
}
|
|
171
187
|
|
|
188
|
+
/**
|
|
189
|
+
* @param {string} containerId
|
|
190
|
+
*/
|
|
172
191
|
async function checkContainerRunning(containerId) {
|
|
173
192
|
const running = (
|
|
174
193
|
await spawnProcess('docker', [
|
|
@@ -181,6 +200,9 @@ async function checkContainerRunning(containerId) {
|
|
|
181
200
|
return running ? containerId : undefined;
|
|
182
201
|
}
|
|
183
202
|
|
|
203
|
+
/**
|
|
204
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
205
|
+
*/
|
|
184
206
|
async function initGitAndCacheContainerId(libdragonInfo) {
|
|
185
207
|
// If there is managed vendoring, make sure we have a git repo. `git init` is
|
|
186
208
|
// safe anyways...
|
|
@@ -5,13 +5,13 @@ const printVersion = async () => {
|
|
|
5
5
|
log(`libdragon-cli v${version}`);
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
module.exports = {
|
|
8
|
+
module.exports = /** @type {const} */ ({
|
|
9
9
|
name: 'version',
|
|
10
10
|
fn: printVersion,
|
|
11
|
-
|
|
11
|
+
forwardsRestParams: false,
|
|
12
12
|
usage: {
|
|
13
13
|
name: 'version',
|
|
14
14
|
summary: 'Display cli version.',
|
|
15
15
|
description: `Displays currently running cli version.`,
|
|
16
16
|
},
|
|
17
|
-
};
|
|
17
|
+
});
|
package/modules/constants.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module.exports = {
|
|
1
|
+
module.exports = /** @type {const} */ ({
|
|
2
2
|
DOCKER_HUB_IMAGE: 'ghcr.io/dragonminded/libdragon:latest',
|
|
3
3
|
LIBDRAGON_GIT: 'https://github.com/DragonMinded/libdragon',
|
|
4
4
|
LIBDRAGON_BRANCH: 'trunk',
|
|
@@ -9,6 +9,8 @@ module.exports = {
|
|
|
9
9
|
CONFIG_FILE: 'config.json',
|
|
10
10
|
DEFAULT_STRATEGY: 'submodule',
|
|
11
11
|
|
|
12
|
+
ACCEPTED_STRATEGIES: ['submodule', 'subtree', 'manual'],
|
|
13
|
+
|
|
12
14
|
// cli exit codes
|
|
13
15
|
STATUS_OK: 0,
|
|
14
16
|
STATUS_ERROR: 1,
|
|
@@ -16,4 +18,4 @@ module.exports = {
|
|
|
16
18
|
STATUS_VALIDATION_ERROR: 4,
|
|
17
19
|
|
|
18
20
|
IMAGE_FILE: 'docker-image', // deprecated
|
|
19
|
-
};
|
|
21
|
+
});
|
package/modules/helpers.js
CHANGED
|
@@ -6,31 +6,86 @@ const { spawn } = require('child_process');
|
|
|
6
6
|
|
|
7
7
|
const { globals } = require('./globals');
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* A structure to keep additional error information
|
|
11
|
+
* @typedef {Object} ErrorState
|
|
12
|
+
* @property {number} code
|
|
13
|
+
* @property {string} out
|
|
14
|
+
* @property {boolean} userCommand
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* An error caused by a command exiting with a non-zero exit code
|
|
19
|
+
* @class
|
|
20
|
+
*/
|
|
10
21
|
class CommandError extends Error {
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} message Error message to report
|
|
24
|
+
* @param {ErrorState} errorState
|
|
25
|
+
*/
|
|
11
26
|
constructor(message, { code, out, userCommand }) {
|
|
12
27
|
super(message);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Exit code
|
|
31
|
+
* @type {number}
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
13
34
|
this.code = code;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Error output as a single concatanated string
|
|
38
|
+
* @type {string}
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
14
41
|
this.out = out;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* true when the error caused by a command explicitly run by the end user
|
|
45
|
+
* @type {boolean}
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
15
48
|
this.userCommand = userCommand;
|
|
16
49
|
}
|
|
17
50
|
}
|
|
18
51
|
|
|
19
|
-
|
|
52
|
+
/**
|
|
53
|
+
* An error caused by the user providing an unexpected input
|
|
54
|
+
* @class
|
|
55
|
+
*/
|
|
20
56
|
class ParameterError extends Error {
|
|
57
|
+
/**
|
|
58
|
+
* @param {string} message Error message to report
|
|
59
|
+
* @param {string} actionName
|
|
60
|
+
*/
|
|
21
61
|
constructor(message, actionName) {
|
|
22
62
|
super(message);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* true when the error caused by a command explicitly run by the end user
|
|
66
|
+
* @type {string}
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
23
69
|
this.actionName = actionName;
|
|
24
70
|
}
|
|
25
71
|
}
|
|
26
72
|
|
|
27
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Something was not as expected to continue the operation
|
|
75
|
+
* @class
|
|
76
|
+
*/
|
|
28
77
|
class ValidationError extends Error {
|
|
78
|
+
/**
|
|
79
|
+
* @param {string} message
|
|
80
|
+
*/
|
|
29
81
|
constructor(message) {
|
|
30
82
|
super(message);
|
|
31
83
|
}
|
|
32
84
|
}
|
|
33
85
|
|
|
86
|
+
/**
|
|
87
|
+
* @param {string} path
|
|
88
|
+
*/
|
|
34
89
|
async function fileExists(path) {
|
|
35
90
|
return fs
|
|
36
91
|
.stat(path)
|
|
@@ -41,6 +96,9 @@ async function fileExists(path) {
|
|
|
41
96
|
});
|
|
42
97
|
}
|
|
43
98
|
|
|
99
|
+
/**
|
|
100
|
+
* @param {string} path
|
|
101
|
+
*/
|
|
44
102
|
async function dirExists(path) {
|
|
45
103
|
return fs
|
|
46
104
|
.stat(path)
|
|
@@ -51,9 +109,27 @@ async function dirExists(path) {
|
|
|
51
109
|
});
|
|
52
110
|
}
|
|
53
111
|
|
|
112
|
+
/**
|
|
113
|
+
* @typedef {{
|
|
114
|
+
* userCommand?: boolean,
|
|
115
|
+
* inheritStdin?: boolean,
|
|
116
|
+
* inheritStdout?: boolean,
|
|
117
|
+
* inheritStderr?: boolean,
|
|
118
|
+
* spawnOptions?: import('child_process').SpawnOptions,
|
|
119
|
+
* stdin?: fsClassic.ReadStream,
|
|
120
|
+
* }} SpawnOptions
|
|
121
|
+
*/
|
|
122
|
+
|
|
54
123
|
// A simple Promise wrapper for child_process.spawn. Return the err/out streams
|
|
55
124
|
// from the process by default. Specify inheritStdout / inheritStderr to disable
|
|
56
125
|
// this and inherit the parent process's stream, passing through the TTY if any.
|
|
126
|
+
/**
|
|
127
|
+
*
|
|
128
|
+
* @param {string} cmd
|
|
129
|
+
* @param {string[]} params
|
|
130
|
+
* @param {SpawnOptions} options
|
|
131
|
+
* @returns {Promise<string>}
|
|
132
|
+
*/
|
|
57
133
|
function spawnProcess(
|
|
58
134
|
cmd,
|
|
59
135
|
params = [],
|
|
@@ -81,7 +157,10 @@ function spawnProcess(
|
|
|
81
157
|
}
|
|
82
158
|
) {
|
|
83
159
|
return new Promise((resolve, reject) => {
|
|
160
|
+
/** @type {Buffer[]} */
|
|
84
161
|
const stdout = [];
|
|
162
|
+
|
|
163
|
+
/** @type {Buffer[]} */
|
|
85
164
|
const stderr = [];
|
|
86
165
|
|
|
87
166
|
log(chalk.grey(`Spawning: ${cmd} ${params.join(' ')}`), true);
|
|
@@ -102,6 +181,9 @@ function spawnProcess(
|
|
|
102
181
|
// See a very old related issue: https://github.com/nodejs/node/issues/947
|
|
103
182
|
// When the stream is not fully consumed by the pipe target and it exits,
|
|
104
183
|
// an EPIPE or EOF is thrown. We don't care about those.
|
|
184
|
+
/**
|
|
185
|
+
* @param {Error & {code: string}} err
|
|
186
|
+
*/
|
|
105
187
|
const eatEpipe = (err) => {
|
|
106
188
|
if (err.code !== 'EPIPE' && err.code !== 'EOF') {
|
|
107
189
|
throw err;
|
|
@@ -140,11 +222,17 @@ function spawnProcess(
|
|
|
140
222
|
});
|
|
141
223
|
}
|
|
142
224
|
|
|
225
|
+
/**
|
|
226
|
+
* @param {Error} err
|
|
227
|
+
*/
|
|
143
228
|
const errorHandler = (err) => {
|
|
144
229
|
command.off('close', closeHandler);
|
|
145
230
|
reject(err);
|
|
146
231
|
};
|
|
147
232
|
|
|
233
|
+
/**
|
|
234
|
+
* @param {number} code
|
|
235
|
+
*/
|
|
148
236
|
const closeHandler = function (code) {
|
|
149
237
|
// The stream was fully consumed, if there is this an additional error on
|
|
150
238
|
// stdout, it must be a legitimate error
|
|
@@ -170,9 +258,21 @@ function spawnProcess(
|
|
|
170
258
|
});
|
|
171
259
|
}
|
|
172
260
|
|
|
173
|
-
|
|
261
|
+
/**
|
|
262
|
+
* @typedef {{
|
|
263
|
+
* (libdragonInfo: import('./project-info').LibdragonInfo, dockerParams: string[], cmdWithParams: string[], options?: SpawnOptions): Promise<string>;
|
|
264
|
+
* (libdragonInfo: import('./project-info').LibdragonInfo, cmdWithParams: string[], options?: SpawnOptions, unused?: unknown): Promise<string>;
|
|
265
|
+
* }} DockerExec
|
|
266
|
+
*/
|
|
267
|
+
const dockerExec = /** @type {DockerExec} */ function (
|
|
268
|
+
libdragonInfo,
|
|
269
|
+
dockerParams,
|
|
270
|
+
cmdWithParams,
|
|
271
|
+
/** @type {SpawnOptions} */
|
|
272
|
+
options
|
|
273
|
+
) {
|
|
174
274
|
assert(
|
|
175
|
-
libdragonInfo.containerId,
|
|
275
|
+
!!libdragonInfo.containerId,
|
|
176
276
|
new Error('Trying to invoke dockerExec without a containerId.')
|
|
177
277
|
);
|
|
178
278
|
|
|
@@ -181,7 +281,7 @@ function dockerExec(libdragonInfo, dockerParams, cmdWithParams, options) {
|
|
|
181
281
|
Array.isArray(dockerParams) && Array.isArray(cmdWithParams);
|
|
182
282
|
|
|
183
283
|
if (!haveDockerParams) {
|
|
184
|
-
options = cmdWithParams;
|
|
284
|
+
options = /** @type {SpawnOptions} */ (cmdWithParams);
|
|
185
285
|
}
|
|
186
286
|
|
|
187
287
|
const additionalParams = [];
|
|
@@ -215,10 +315,12 @@ function dockerExec(libdragonInfo, dockerParams, cmdWithParams, options) {
|
|
|
215
315
|
],
|
|
216
316
|
options
|
|
217
317
|
);
|
|
218
|
-
}
|
|
318
|
+
};
|
|
219
319
|
|
|
220
320
|
/**
|
|
221
321
|
* Recursively copies directories and files
|
|
322
|
+
* @param {string} src
|
|
323
|
+
* @param {string} dst
|
|
222
324
|
*/
|
|
223
325
|
async function copyDirContents(src, dst) {
|
|
224
326
|
log(`Copying from ${src} to ${dst}`, true);
|
|
@@ -269,14 +371,24 @@ async function copyDirContents(src, dst) {
|
|
|
269
371
|
);
|
|
270
372
|
}
|
|
271
373
|
|
|
374
|
+
/**
|
|
375
|
+
* @param {string} p
|
|
376
|
+
*/
|
|
272
377
|
function toPosixPath(p) {
|
|
273
378
|
return p.replace(new RegExp('\\' + path.sep, 'g'), path.posix.sep);
|
|
274
379
|
}
|
|
275
380
|
|
|
381
|
+
/**
|
|
382
|
+
* @param {string} p
|
|
383
|
+
*/
|
|
276
384
|
function toNativePath(p) {
|
|
277
385
|
return p.replace(new RegExp('\\' + path.posix.sep, 'g'), path.sep);
|
|
278
386
|
}
|
|
279
387
|
|
|
388
|
+
/**
|
|
389
|
+
* @param {boolean} condition
|
|
390
|
+
* @param {Error} error
|
|
391
|
+
*/
|
|
280
392
|
function assert(condition, error) {
|
|
281
393
|
if (!condition) {
|
|
282
394
|
error.message = `[ASSERTION FAILED] ${error.message}`;
|
|
@@ -284,12 +396,19 @@ function assert(condition, error) {
|
|
|
284
396
|
}
|
|
285
397
|
}
|
|
286
398
|
|
|
399
|
+
/**
|
|
400
|
+
* @param {string} text
|
|
401
|
+
*/
|
|
287
402
|
function print(text) {
|
|
288
403
|
// eslint-disable-next-line no-console
|
|
289
404
|
console.log(text);
|
|
290
405
|
return;
|
|
291
406
|
}
|
|
292
407
|
|
|
408
|
+
/**
|
|
409
|
+
* @param {string} text
|
|
410
|
+
* @param {boolean} verboseOnly
|
|
411
|
+
*/
|
|
293
412
|
function log(text, verboseOnly = false) {
|
|
294
413
|
if (!verboseOnly) {
|
|
295
414
|
// eslint-disable-next-line no-console
|
package/modules/parameters.js
CHANGED
|
@@ -2,10 +2,31 @@ const chalk = require('chalk').stderr;
|
|
|
2
2
|
|
|
3
3
|
const { log } = require('./helpers');
|
|
4
4
|
const actions = require('./actions');
|
|
5
|
-
const { STATUS_BAD_PARAM } = require('./constants');
|
|
5
|
+
const { STATUS_BAD_PARAM, ACCEPTED_STRATEGIES } = require('./constants');
|
|
6
6
|
const { globals } = require('./globals');
|
|
7
7
|
|
|
8
|
+
/** @typedef { import('./actions') } Actions */
|
|
9
|
+
|
|
10
|
+
/** @typedef { typeof import('./constants').ACCEPTED_STRATEGIES[number] } VendorStrategy */
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @template {keyof Actions} [T=keyof Actions]
|
|
14
|
+
* @typedef {{
|
|
15
|
+
* EXTRA_PARAMS: string[];
|
|
16
|
+
* CURRENT_ACTION: Actions[T];
|
|
17
|
+
* VERBOSE?: boolean;
|
|
18
|
+
* DOCKER_IMAGE?: string;
|
|
19
|
+
* VENDOR_DIR?: string;
|
|
20
|
+
* VENDOR_STRAT?: VendorStrategy;
|
|
21
|
+
* FILE?: string;
|
|
22
|
+
* }} CommandlineOptions
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string[]} argv
|
|
27
|
+
*/
|
|
8
28
|
const parseParameters = async (argv) => {
|
|
29
|
+
/** @type CommandlineOptions */
|
|
9
30
|
const options = {
|
|
10
31
|
EXTRA_PARAMS: [],
|
|
11
32
|
CURRENT_ACTION: undefined,
|
|
@@ -38,12 +59,19 @@ const parseParameters = async (argv) => {
|
|
|
38
59
|
}
|
|
39
60
|
|
|
40
61
|
if (['--strategy', '-s'].includes(val)) {
|
|
41
|
-
options.VENDOR_STRAT = argv[++i];
|
|
62
|
+
options.VENDOR_STRAT = /** @type VendorStrategy */ (argv[++i]);
|
|
42
63
|
continue;
|
|
43
64
|
} else if (val.indexOf('--strategy=') === 0) {
|
|
44
|
-
options.VENDOR_STRAT = val.split('=')[1];
|
|
65
|
+
options.VENDOR_STRAT = /** @type VendorStrategy */ (val.split('=')[1]);
|
|
45
66
|
continue;
|
|
46
67
|
}
|
|
68
|
+
if (
|
|
69
|
+
options.VENDOR_STRAT &&
|
|
70
|
+
!ACCEPTED_STRATEGIES.includes(options.VENDOR_STRAT)
|
|
71
|
+
) {
|
|
72
|
+
log(chalk.red(`Invalid strategy \`${options.VENDOR_STRAT}\``));
|
|
73
|
+
process.exit(STATUS_BAD_PARAM);
|
|
74
|
+
}
|
|
47
75
|
|
|
48
76
|
if (['--file', '-f'].includes(val)) {
|
|
49
77
|
options.FILE = argv[++i];
|
|
@@ -63,7 +91,7 @@ const parseParameters = async (argv) => {
|
|
|
63
91
|
process.exit(STATUS_BAD_PARAM);
|
|
64
92
|
}
|
|
65
93
|
|
|
66
|
-
options.CURRENT_ACTION = actions[val];
|
|
94
|
+
options.CURRENT_ACTION = actions[/** @type {keyof Actions} */ (val)];
|
|
67
95
|
|
|
68
96
|
if (!options.CURRENT_ACTION) {
|
|
69
97
|
log(chalk.red(`Invalid action \`${val}\``));
|
|
@@ -90,8 +118,12 @@ const parseParameters = async (argv) => {
|
|
|
90
118
|
}
|
|
91
119
|
|
|
92
120
|
if (
|
|
93
|
-
!
|
|
94
|
-
|
|
121
|
+
!(
|
|
122
|
+
/** @type {typeof actions[keyof actions][]} */ ([
|
|
123
|
+
actions.init,
|
|
124
|
+
actions.install,
|
|
125
|
+
actions.update,
|
|
126
|
+
]).includes(options.CURRENT_ACTION)
|
|
95
127
|
) &&
|
|
96
128
|
options.DOCKER_IMAGE
|
|
97
129
|
) {
|
|
@@ -100,14 +132,13 @@ const parseParameters = async (argv) => {
|
|
|
100
132
|
}
|
|
101
133
|
|
|
102
134
|
if (
|
|
103
|
-
|
|
104
|
-
|
|
135
|
+
!(
|
|
136
|
+
/** @type {typeof actions[keyof actions][]} */ ([
|
|
137
|
+
actions.disasm,
|
|
138
|
+
]).includes(options.CURRENT_ACTION)
|
|
139
|
+
) &&
|
|
140
|
+
options.FILE
|
|
105
141
|
) {
|
|
106
|
-
log(chalk.red(`Invalid strategy \`${options.VENDOR_STRAT}\``));
|
|
107
|
-
process.exit(STATUS_BAD_PARAM);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (![actions.disasm].includes(options.CURRENT_ACTION) && options.FILE) {
|
|
111
142
|
log(chalk.red('Invalid flag: file'));
|
|
112
143
|
process.exit(STATUS_BAD_PARAM);
|
|
113
144
|
}
|
package/modules/project-info.js
CHANGED
|
@@ -29,9 +29,36 @@ const {
|
|
|
29
29
|
ParameterError,
|
|
30
30
|
} = require('./helpers');
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
const
|
|
32
|
+
// These do not need a project to exist.
|
|
33
|
+
const NO_PROJECT_ACTIONS = /** @type {const} */ (['help', 'version']);
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* @typedef { typeof NO_PROJECT_ACTIONS[number] } ActionsNoProject
|
|
37
|
+
* @typedef { Exclude<keyof import('./parameters').Actions, ActionsNoProject> } ActionsWithProject
|
|
38
|
+
* @typedef { import('./parameters').CommandlineOptions<ActionsNoProject> } ActionsNoProjectOptions
|
|
39
|
+
* @typedef { import('./parameters').CommandlineOptions<ActionsWithProject> } ActionsWithProjectOptions
|
|
40
|
+
*
|
|
41
|
+
* @typedef { {
|
|
42
|
+
* options: ActionsNoProjectOptions
|
|
43
|
+
* } } CLIInfo
|
|
44
|
+
*
|
|
45
|
+
* @typedef { {
|
|
46
|
+
* root: string;
|
|
47
|
+
* userInfo: os.UserInfo<string>;
|
|
48
|
+
* haveProjectConfig: boolean;
|
|
49
|
+
* imageName: string;
|
|
50
|
+
* vendorDirectory: string;
|
|
51
|
+
* vendorStrategy: import('./parameters').VendorStrategy;
|
|
52
|
+
* containerId: string
|
|
53
|
+
* } } ExtendedInfo
|
|
54
|
+
*
|
|
55
|
+
* @typedef { CLIInfo & Partial<ExtendedInfo> } LibdragonInfo
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {LibdragonInfo} libdragonInfo
|
|
60
|
+
* @returns string
|
|
61
|
+
*/
|
|
35
62
|
async function findContainerId(libdragonInfo) {
|
|
36
63
|
const idFile = path.join(libdragonInfo.root, '.git', CACHED_CONTAINER_FILE);
|
|
37
64
|
if (await fileExists(idFile)) {
|
|
@@ -79,6 +106,11 @@ async function findContainerId(libdragonInfo) {
|
|
|
79
106
|
}
|
|
80
107
|
}
|
|
81
108
|
|
|
109
|
+
/**
|
|
110
|
+
* @param {string} start
|
|
111
|
+
* @param {string} relativeFile
|
|
112
|
+
* @return {Promise<string>}
|
|
113
|
+
*/
|
|
82
114
|
async function findLibdragonRoot(
|
|
83
115
|
start = '.',
|
|
84
116
|
relativeFile = path.join(LIBDRAGON_PROJECT_MANIFEST, CONFIG_FILE)
|
|
@@ -106,18 +138,19 @@ async function findGitRoot() {
|
|
|
106
138
|
}
|
|
107
139
|
}
|
|
108
140
|
|
|
109
|
-
|
|
141
|
+
/**
|
|
142
|
+
* @param {LibdragonInfo} optionInfo
|
|
143
|
+
*/
|
|
144
|
+
const readProjectInfo = async function (optionInfo) {
|
|
110
145
|
// No need to do anything here if the action does not depend on the project
|
|
111
146
|
// The only exception is the init and destroy actions, which do not need an
|
|
112
147
|
// existing project but readProjectInfo must always run to analyze the situation
|
|
113
|
-
const forceReadProjectInfo = [initAction, destroyAction].includes(
|
|
114
|
-
info.options.CURRENT_ACTION
|
|
115
|
-
);
|
|
116
148
|
if (
|
|
117
|
-
|
|
118
|
-
|
|
149
|
+
NO_PROJECT_ACTIONS.includes(
|
|
150
|
+
/** @type {ActionsNoProject} */ (optionInfo.options.CURRENT_ACTION.name)
|
|
151
|
+
)
|
|
119
152
|
) {
|
|
120
|
-
return
|
|
153
|
+
return /** @type {CLIInfo} */ (optionInfo);
|
|
121
154
|
}
|
|
122
155
|
|
|
123
156
|
const migratedRoot = await findLibdragonRoot();
|
|
@@ -130,15 +163,19 @@ async function readProjectInfo(info) {
|
|
|
130
163
|
path.join(LIBDRAGON_PROJECT_MANIFEST, IMAGE_FILE)
|
|
131
164
|
));
|
|
132
165
|
|
|
133
|
-
if (
|
|
166
|
+
if (
|
|
167
|
+
!projectRoot &&
|
|
168
|
+
!['init', 'destroy'].includes(optionInfo.options.CURRENT_ACTION.name)
|
|
169
|
+
) {
|
|
134
170
|
throw new ParameterError(
|
|
135
171
|
'This is not a libdragon project. Initialize with `libdragon init` first.',
|
|
136
|
-
|
|
172
|
+
optionInfo.options.CURRENT_ACTION.name
|
|
137
173
|
);
|
|
138
174
|
}
|
|
139
175
|
|
|
140
|
-
|
|
141
|
-
|
|
176
|
+
/** @type {LibdragonInfo} */
|
|
177
|
+
let info = {
|
|
178
|
+
...optionInfo,
|
|
142
179
|
root: projectRoot ?? (await findNPMRoot()) ?? (await findGitRoot()),
|
|
143
180
|
userInfo: os.userInfo(),
|
|
144
181
|
|
|
@@ -211,10 +248,10 @@ async function readProjectInfo(info) {
|
|
|
211
248
|
log(`Active vendor strategy: ${info.vendorStrategy}`, true);
|
|
212
249
|
|
|
213
250
|
return info;
|
|
214
|
-
}
|
|
251
|
+
};
|
|
215
252
|
|
|
216
253
|
/**
|
|
217
|
-
* @param info This is only the base info without options
|
|
254
|
+
* @param { LibdragonInfo | void } info This is only the base info without options
|
|
218
255
|
* fn and command line options
|
|
219
256
|
*/
|
|
220
257
|
async function writeProjectInfo(info) {
|