libdragon 10.7.1 → 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 +153 -29
- package/index.js +0 -0
- 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 +10 -7
- package/modules/actions/init.js +166 -94
- package/modules/actions/install.js +8 -12
- package/modules/actions/make.js +5 -2
- package/modules/actions/npm-utils.js +8 -2
- package/modules/actions/start.js +22 -14
- package/modules/actions/stop.js +6 -2
- package/modules/actions/update-and-start.js +5 -2
- package/modules/actions/update.js +17 -16
- package/modules/actions/utils.js +31 -9
- 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 +56 -17
- package/package.json +57 -4
- package/CHANGELOG.md +0 -582
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,39 +115,50 @@ 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
|
*/
|
|
113
|
-
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
*
|
|
121
|
+
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
122
|
+
* @param {string[]} params
|
|
123
|
+
* @param {import('../helpers').SpawnOptions} options
|
|
124
|
+
* @returns
|
|
125
|
+
*/
|
|
126
|
+
async function runGitMaybeHost(libdragonInfo, params, options = {}) {
|
|
114
127
|
assert(
|
|
115
128
|
libdragonInfo.vendorStrategy !== 'manual',
|
|
116
129
|
new Error('Should never run git if vendoring strategy is manual.')
|
|
117
130
|
);
|
|
118
131
|
try {
|
|
119
132
|
const isWin = /^win/.test(process.platform);
|
|
120
|
-
await spawnProcess(
|
|
133
|
+
return await spawnProcess(
|
|
121
134
|
'git',
|
|
122
135
|
['-C', libdragonInfo.root, ...params],
|
|
123
136
|
// Windows git is breaking the TTY somehow - disable TTY for now
|
|
124
137
|
// We are not able to display progress for the initial clone b/c of this
|
|
125
138
|
// Enable progress otherwise.
|
|
126
139
|
isWin
|
|
127
|
-
? { inheritStdin: false }
|
|
128
|
-
: { inheritStdout: true, inheritStderr: true }
|
|
140
|
+
? { inheritStdin: false, ...options }
|
|
141
|
+
: { inheritStdout: true, inheritStderr: true, ...options }
|
|
129
142
|
);
|
|
130
143
|
} catch (e) {
|
|
131
144
|
if (e instanceof CommandError) {
|
|
132
145
|
throw e;
|
|
133
146
|
}
|
|
134
147
|
|
|
135
|
-
await dockerExec(
|
|
148
|
+
return await dockerExec(
|
|
136
149
|
libdragonInfo,
|
|
137
150
|
// Use the host user when initializing git as we will need access
|
|
138
151
|
[...dockerHostUserParams(libdragonInfo)],
|
|
139
152
|
['git', ...params],
|
|
140
153
|
// Let's enable tty here to show the progress
|
|
141
|
-
{ inheritStdout: true, inheritStderr: true }
|
|
154
|
+
{ inheritStdout: true, inheritStderr: true, ...options }
|
|
142
155
|
);
|
|
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)) {
|
|
@@ -66,17 +93,24 @@ async function findContainerId(libdragonInfo) {
|
|
|
66
93
|
const idIndex = str.indexOf(shortId);
|
|
67
94
|
const longId = str.slice(idIndex, idIndex + 64);
|
|
68
95
|
if (longId.length === 64) {
|
|
69
|
-
const newInfo = { ...libdragonInfo, containerId: longId };
|
|
70
96
|
// This shouldn't happen but if the user somehow deleted the .git folder
|
|
71
97
|
// (we don't have the container id file at this point) we can recover the
|
|
72
98
|
// project. `git init` is safe anyways and it is not executed if strategy
|
|
73
99
|
// is `manual`
|
|
74
|
-
await initGitAndCacheContainerId(
|
|
100
|
+
await initGitAndCacheContainerId({
|
|
101
|
+
...libdragonInfo,
|
|
102
|
+
containerId: longId,
|
|
103
|
+
});
|
|
75
104
|
return longId;
|
|
76
105
|
}
|
|
77
106
|
}
|
|
78
107
|
}
|
|
79
108
|
|
|
109
|
+
/**
|
|
110
|
+
* @param {string} start
|
|
111
|
+
* @param {string} relativeFile
|
|
112
|
+
* @return {Promise<string>}
|
|
113
|
+
*/
|
|
80
114
|
async function findLibdragonRoot(
|
|
81
115
|
start = '.',
|
|
82
116
|
relativeFile = path.join(LIBDRAGON_PROJECT_MANIFEST, CONFIG_FILE)
|
|
@@ -104,18 +138,19 @@ async function findGitRoot() {
|
|
|
104
138
|
}
|
|
105
139
|
}
|
|
106
140
|
|
|
107
|
-
|
|
141
|
+
/**
|
|
142
|
+
* @param {LibdragonInfo} optionInfo
|
|
143
|
+
*/
|
|
144
|
+
const readProjectInfo = async function (optionInfo) {
|
|
108
145
|
// No need to do anything here if the action does not depend on the project
|
|
109
146
|
// The only exception is the init and destroy actions, which do not need an
|
|
110
147
|
// existing project but readProjectInfo must always run to analyze the situation
|
|
111
|
-
const forceReadProjectInfo = [initAction, destroyAction].includes(
|
|
112
|
-
info.options.CURRENT_ACTION
|
|
113
|
-
);
|
|
114
148
|
if (
|
|
115
|
-
|
|
116
|
-
|
|
149
|
+
NO_PROJECT_ACTIONS.includes(
|
|
150
|
+
/** @type {ActionsNoProject} */ (optionInfo.options.CURRENT_ACTION.name)
|
|
151
|
+
)
|
|
117
152
|
) {
|
|
118
|
-
return
|
|
153
|
+
return /** @type {CLIInfo} */ (optionInfo);
|
|
119
154
|
}
|
|
120
155
|
|
|
121
156
|
const migratedRoot = await findLibdragonRoot();
|
|
@@ -128,15 +163,19 @@ async function readProjectInfo(info) {
|
|
|
128
163
|
path.join(LIBDRAGON_PROJECT_MANIFEST, IMAGE_FILE)
|
|
129
164
|
));
|
|
130
165
|
|
|
131
|
-
if (
|
|
166
|
+
if (
|
|
167
|
+
!projectRoot &&
|
|
168
|
+
!['init', 'destroy'].includes(optionInfo.options.CURRENT_ACTION.name)
|
|
169
|
+
) {
|
|
132
170
|
throw new ParameterError(
|
|
133
171
|
'This is not a libdragon project. Initialize with `libdragon init` first.',
|
|
134
|
-
|
|
172
|
+
optionInfo.options.CURRENT_ACTION.name
|
|
135
173
|
);
|
|
136
174
|
}
|
|
137
175
|
|
|
138
|
-
|
|
139
|
-
|
|
176
|
+
/** @type {LibdragonInfo} */
|
|
177
|
+
let info = {
|
|
178
|
+
...optionInfo,
|
|
140
179
|
root: projectRoot ?? (await findNPMRoot()) ?? (await findGitRoot()),
|
|
141
180
|
userInfo: os.userInfo(),
|
|
142
181
|
|
|
@@ -209,10 +248,10 @@ async function readProjectInfo(info) {
|
|
|
209
248
|
log(`Active vendor strategy: ${info.vendorStrategy}`, true);
|
|
210
249
|
|
|
211
250
|
return info;
|
|
212
|
-
}
|
|
251
|
+
};
|
|
213
252
|
|
|
214
253
|
/**
|
|
215
|
-
* @param info This is only the base info without options
|
|
254
|
+
* @param { LibdragonInfo | void } info This is only the base info without options
|
|
216
255
|
* fn and command line options
|
|
217
256
|
*/
|
|
218
257
|
async function writeProjectInfo(info) {
|