libdragon 10.8.1 → 10.8.2
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 +2 -7
- package/index.js +22 -1
- package/modules/actions/disasm.js +1 -1
- package/modules/actions/docker-utils.js +0 -1
- package/modules/actions/exec.js +20 -19
- package/modules/actions/init.js +6 -2
- package/modules/actions/npm-utils.js +50 -48
- package/modules/actions/stop.js +1 -0
- package/modules/actions/utils.js +1 -2
- package/modules/constants.js +5 -0
- package/modules/helpers.js +70 -53
- package/modules/parameters.js +2 -2
- package/modules/project-info.js +31 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Docker Libdragon
|
|
2
2
|
|
|
3
|
-
[](https://github.com/anacierdem/libdragon-docker/actions/workflows/release.yml)
|
|
4
4
|
|
|
5
5
|
This is a wrapper for a docker container to make managing the libdragon toolchain easier. It has the additional advantage that libdragon toolchain and library can be installed on a per-project basis instead of managing system-wide installations.
|
|
6
6
|
|
|
@@ -191,7 +191,7 @@ When you are happy with your changes, you can verify you conform to the coding s
|
|
|
191
191
|
npm run format-check
|
|
192
192
|
npm run lint-check
|
|
193
193
|
|
|
194
|
-
You can auto-fix applicable errors by running `format` and `lint` scripts instead. Additionally, typescript is used as the type system. To be able to get away with transpiling the code during development, jsDoc flavor of types are used instead of inline ones. To check
|
|
194
|
+
You can auto-fix applicable errors by running `format` and `lint` scripts instead. Additionally, typescript is used as the type system. To be able to get away with transpiling the code during development, jsDoc flavor of types are used instead of inline ones. To check your types, run:
|
|
195
195
|
|
|
196
196
|
npm run tsc
|
|
197
197
|
|
|
@@ -242,11 +242,6 @@ will init the container for this project and run `make && make install` for `ed6
|
|
|
242
242
|
|
|
243
243
|
This is an experimental dependency management.
|
|
244
244
|
|
|
245
|
-
## TODOS
|
|
246
|
-
|
|
247
|
-
- [ ] Skip CI checks for irrelevant changes.
|
|
248
|
-
- [ ] Verify the NPM dependency mechanism is still working and add a test.
|
|
249
|
-
|
|
250
245
|
## Funding
|
|
251
246
|
|
|
252
247
|
If this tool helped you, consider supporting its development by sponsoring it!
|
package/index.js
CHANGED
|
@@ -18,9 +18,30 @@ const {
|
|
|
18
18
|
const { parseParameters } = require('./modules/parameters');
|
|
19
19
|
const { readProjectInfo, writeProjectInfo } = require('./modules/project-info');
|
|
20
20
|
|
|
21
|
+
// Note: it is not possible to merge these type definitions in a single comment
|
|
22
|
+
/**
|
|
23
|
+
* @template {any} [U=any]
|
|
24
|
+
* @typedef {(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never} UnionToIntersection
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* @template {any} [K=any]
|
|
28
|
+
* never does not break the call `info.options.CURRENT_ACTION.fn`
|
|
29
|
+
* @typedef {[K] extends [UnionToIntersection<K>] ? any : unknown} NoUnion
|
|
30
|
+
* @typedef {NoUnion<Exclude<Parameters<import('./modules/parameters').Actions[import('./modules/project-info').ActionsNoProject]['fn']>[0], undefined>>} EitherCLIOrLibdragonInfo
|
|
31
|
+
*/
|
|
32
|
+
|
|
21
33
|
parseParameters(process.argv)
|
|
22
34
|
.then(readProjectInfo)
|
|
23
|
-
.then((info) =>
|
|
35
|
+
.then((info) => {
|
|
36
|
+
return info.options.CURRENT_ACTION.fn(
|
|
37
|
+
/** @type {EitherCLIOrLibdragonInfo} */ (info)
|
|
38
|
+
);
|
|
39
|
+
// This type make sure a similar restriction to this code block is enforced
|
|
40
|
+
// without adding unnecessary javascript.
|
|
41
|
+
// return isProjectAction(info)
|
|
42
|
+
// ? info.options.CURRENT_ACTION.fn(info)
|
|
43
|
+
// : info.options.CURRENT_ACTION.fn(info);
|
|
44
|
+
})
|
|
24
45
|
.catch((e) => {
|
|
25
46
|
if (e instanceof ParameterError) {
|
|
26
47
|
log(chalk.red(e.message));
|
package/modules/actions/exec.js
CHANGED
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
toPosixPath,
|
|
9
9
|
fileExists,
|
|
10
10
|
dirExists,
|
|
11
|
+
CommandError,
|
|
11
12
|
} = require('../helpers');
|
|
12
13
|
|
|
13
14
|
const { start } = require('./start');
|
|
@@ -16,7 +17,6 @@ const { installDependencies } = require('./utils');
|
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
19
|
-
* @returns
|
|
20
20
|
*/
|
|
21
21
|
function dockerRelativeWorkdir(libdragonInfo) {
|
|
22
22
|
return (
|
|
@@ -27,18 +27,14 @@ function dockerRelativeWorkdir(libdragonInfo) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
31
30
|
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
32
|
-
* @returns
|
|
33
31
|
*/
|
|
34
32
|
function dockerRelativeWorkdirParams(libdragonInfo) {
|
|
35
33
|
return ['--workdir', dockerRelativeWorkdir(libdragonInfo)];
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
/**
|
|
39
|
-
*
|
|
40
37
|
* @param {import('../project-info').LibdragonInfo} info
|
|
41
|
-
* @returns
|
|
42
38
|
*/
|
|
43
39
|
const exec = async (info) => {
|
|
44
40
|
const parameters = info.options.EXTRA_PARAMS.slice(1);
|
|
@@ -52,17 +48,22 @@ const exec = async (info) => {
|
|
|
52
48
|
const stdin = new PassThrough();
|
|
53
49
|
|
|
54
50
|
/** @type {string[]} */
|
|
55
|
-
const paramsWithConvertedPaths =
|
|
56
|
-
|
|
51
|
+
const paramsWithConvertedPaths = await Promise.all(
|
|
52
|
+
parameters.map(async (item) => {
|
|
53
|
+
if (item.startsWith('-')) {
|
|
54
|
+
return item;
|
|
55
|
+
}
|
|
56
|
+
if (
|
|
57
|
+
item.includes(path.sep) &&
|
|
58
|
+
((await fileExists(item)) || (await dirExists(item)))
|
|
59
|
+
) {
|
|
60
|
+
return toPosixPath(
|
|
61
|
+
path.isAbsolute(item) ? path.relative(process.cwd(), item) : item
|
|
62
|
+
);
|
|
63
|
+
}
|
|
57
64
|
return item;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return toPosixPath(
|
|
61
|
-
path.isAbsolute(item) ? path.relative(process.cwd(), item) : item
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
return item;
|
|
65
|
-
});
|
|
65
|
+
})
|
|
66
|
+
);
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
69
|
*
|
|
@@ -101,9 +102,7 @@ const exec = async (info) => {
|
|
|
101
102
|
|
|
102
103
|
let started = false;
|
|
103
104
|
/**
|
|
104
|
-
*
|
|
105
105
|
* @param {import('fs').ReadStream=} stdin
|
|
106
|
-
* @returns
|
|
107
106
|
*/
|
|
108
107
|
const startOnceAndCmd = async (stdin) => {
|
|
109
108
|
if (!started) {
|
|
@@ -147,14 +146,16 @@ const exec = async (info) => {
|
|
|
147
146
|
// In the first run, pass the stdin to the process if it is not a TTY
|
|
148
147
|
// o/w we loose a user input unnecesarily somehow.
|
|
149
148
|
stdin:
|
|
150
|
-
!process.stdin.isTTY &&
|
|
149
|
+
(!process.stdin.isTTY || undefined) &&
|
|
151
150
|
/** @type {import('fs').ReadStream} */ (
|
|
152
151
|
/** @type {unknown} */ (process.stdin)
|
|
153
152
|
),
|
|
154
153
|
});
|
|
155
154
|
} catch (e) {
|
|
155
|
+
if (!(e instanceof CommandError)) {
|
|
156
|
+
throw e;
|
|
157
|
+
}
|
|
156
158
|
if (
|
|
157
|
-
!e.out ||
|
|
158
159
|
// TODO: is there a better way?
|
|
159
160
|
!e.out.toString().includes(info.containerId)
|
|
160
161
|
) {
|
package/modules/actions/init.js
CHANGED
|
@@ -30,7 +30,7 @@ const { syncImageAndStart } = require('./update-and-start');
|
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* @param {import('../project-info').LibdragonInfo} info
|
|
33
|
-
* @
|
|
33
|
+
* @returns {Promise<"submodule" | "subtree" | undefined>}
|
|
34
34
|
*/
|
|
35
35
|
const autoDetect = async (info) => {
|
|
36
36
|
const vendorTarget = path.relative(
|
|
@@ -188,6 +188,8 @@ const autoVendor = async (info) => {
|
|
|
188
188
|
]);
|
|
189
189
|
return info;
|
|
190
190
|
}
|
|
191
|
+
|
|
192
|
+
return info;
|
|
191
193
|
};
|
|
192
194
|
|
|
193
195
|
/**
|
|
@@ -242,7 +244,9 @@ async function init(info) {
|
|
|
242
244
|
info.containerId = await start(info);
|
|
243
245
|
|
|
244
246
|
// We have created a new container, save the new info ASAP
|
|
245
|
-
await initGitAndCacheContainerId(
|
|
247
|
+
await initGitAndCacheContainerId(
|
|
248
|
+
/** @type Parameters<initGitAndCacheContainerId>[0] */ (info)
|
|
249
|
+
);
|
|
246
250
|
|
|
247
251
|
info = await autoVendor(info);
|
|
248
252
|
|
|
@@ -55,59 +55,61 @@ const installNPMDependencies = async (libdragonInfo) => {
|
|
|
55
55
|
|
|
56
56
|
await Promise.all(
|
|
57
57
|
deps.map(({ name, paths }) => {
|
|
58
|
-
return
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
58
|
+
return /** @type Promise<void> */ (
|
|
59
|
+
new Promise((resolve, reject) => {
|
|
60
|
+
fsClassic.access(
|
|
61
|
+
path.join(paths[0], 'Makefile'),
|
|
62
|
+
fsClassic.constants.F_OK,
|
|
63
|
+
async (e) => {
|
|
64
|
+
if (e) {
|
|
65
|
+
// File does not exist - skip
|
|
66
|
+
resolve();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
if (paths.length > 1) {
|
|
71
|
+
reject(
|
|
72
|
+
new ValidationError(
|
|
73
|
+
`Using same dependency with different versions is not supported! ${name}`
|
|
74
|
+
)
|
|
75
|
+
);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
try {
|
|
80
|
+
const relativePath = toPosixPath(
|
|
81
|
+
path.relative(libdragonInfo.root, paths[0])
|
|
82
|
+
);
|
|
83
|
+
const containerPath = path.posix.join(
|
|
84
|
+
CONTAINER_TARGET_PATH,
|
|
85
|
+
relativePath
|
|
86
|
+
);
|
|
87
|
+
const makePath = path.posix.join(containerPath, 'Makefile');
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
89
|
+
await dockerExec(
|
|
90
|
+
libdragonInfo,
|
|
91
|
+
[...dockerHostUserParams(libdragonInfo)],
|
|
92
|
+
[
|
|
93
|
+
'/bin/bash',
|
|
94
|
+
'-c',
|
|
95
|
+
'[ -f ' +
|
|
96
|
+
makePath +
|
|
97
|
+
' ] && make -C ' +
|
|
98
|
+
containerPath +
|
|
99
|
+
' && make -C ' +
|
|
100
|
+
containerPath +
|
|
101
|
+
' install',
|
|
102
|
+
]
|
|
103
|
+
);
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
resolve();
|
|
106
|
+
} catch (e) {
|
|
107
|
+
reject(e);
|
|
108
|
+
}
|
|
107
109
|
}
|
|
108
|
-
|
|
109
|
-
)
|
|
110
|
-
|
|
110
|
+
);
|
|
111
|
+
})
|
|
112
|
+
);
|
|
111
113
|
})
|
|
112
114
|
);
|
|
113
115
|
}
|
package/modules/actions/stop.js
CHANGED
package/modules/actions/utils.js
CHANGED
|
@@ -121,7 +121,6 @@ const destroyContainer = async (libdragonInfo) => {
|
|
|
121
121
|
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
122
122
|
* @param {string[]} params
|
|
123
123
|
* @param {import('../helpers').SpawnOptions} options
|
|
124
|
-
* @returns
|
|
125
124
|
*/
|
|
126
125
|
async function runGitMaybeHost(libdragonInfo, params, options = {}) {
|
|
127
126
|
assert(
|
|
@@ -201,7 +200,7 @@ async function checkContainerRunning(containerId) {
|
|
|
201
200
|
}
|
|
202
201
|
|
|
203
202
|
/**
|
|
204
|
-
* @param {import('../project-info').LibdragonInfo} libdragonInfo
|
|
203
|
+
* @param {import('../project-info').LibdragonInfo & {containerId: string}} libdragonInfo
|
|
205
204
|
*/
|
|
206
205
|
async function initGitAndCacheContainerId(libdragonInfo) {
|
|
207
206
|
// If there is managed vendoring, make sure we have a git repo. `git init` is
|
package/modules/constants.js
CHANGED
|
@@ -11,6 +11,11 @@ module.exports = /** @type {const} */ ({
|
|
|
11
11
|
|
|
12
12
|
ACCEPTED_STRATEGIES: ['submodule', 'subtree', 'manual'],
|
|
13
13
|
|
|
14
|
+
// These do not need a project to exist and their actions do not need the whole
|
|
15
|
+
// structure. Actions that need the full project information should not be
|
|
16
|
+
// listed here.
|
|
17
|
+
NO_PROJECT_ACTIONS: ['help', 'version'],
|
|
18
|
+
|
|
14
19
|
// cli exit codes
|
|
15
20
|
STATUS_OK: 0,
|
|
16
21
|
STATUS_ERROR: 1,
|
package/modules/helpers.js
CHANGED
|
@@ -5,6 +5,7 @@ const chalk = require('chalk').stderr;
|
|
|
5
5
|
const { spawn } = require('child_process');
|
|
6
6
|
|
|
7
7
|
const { globals } = require('./globals');
|
|
8
|
+
const { NO_PROJECT_ACTIONS } = require('./constants');
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* A structure to keep additional error information
|
|
@@ -197,26 +198,26 @@ function spawnProcess(
|
|
|
197
198
|
resolve('');
|
|
198
199
|
};
|
|
199
200
|
|
|
200
|
-
if (!enableInTTY && stdin) {
|
|
201
|
+
if (!enableInTTY && stdin && command.stdin) {
|
|
201
202
|
stdin.pipe(command.stdin);
|
|
202
203
|
}
|
|
203
204
|
|
|
204
|
-
if (!enableOutTTY && (globals.verbose || userCommand)) {
|
|
205
|
+
if (!enableOutTTY && (globals.verbose || userCommand) && command.stdout) {
|
|
205
206
|
command.stdout.pipe(process.stdout);
|
|
206
207
|
process.stdout.once('error', eatEpipe);
|
|
207
208
|
}
|
|
208
209
|
|
|
209
|
-
if (!inheritStdout) {
|
|
210
|
+
if (!inheritStdout && command.stdout) {
|
|
210
211
|
command.stdout.on('data', function (data) {
|
|
211
212
|
stdout.push(Buffer.from(data));
|
|
212
213
|
});
|
|
213
214
|
}
|
|
214
215
|
|
|
215
|
-
if (!enableErrorTTY && (globals.verbose || userCommand)) {
|
|
216
|
+
if (!enableErrorTTY && (globals.verbose || userCommand) && command.stderr) {
|
|
216
217
|
command.stderr.pipe(process.stderr);
|
|
217
218
|
}
|
|
218
219
|
|
|
219
|
-
if (!inheritStderr) {
|
|
220
|
+
if (!inheritStderr && command.stderr) {
|
|
220
221
|
command.stderr.on('data', function (data) {
|
|
221
222
|
stderr.push(Buffer.from(data));
|
|
222
223
|
});
|
|
@@ -264,58 +265,60 @@ function spawnProcess(
|
|
|
264
265
|
* (libdragonInfo: import('./project-info').LibdragonInfo, cmdWithParams: string[], options?: SpawnOptions, unused?: unknown): Promise<string>;
|
|
265
266
|
* }} DockerExec
|
|
266
267
|
*/
|
|
267
|
-
const dockerExec = /** @type {DockerExec} */
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
268
|
+
const dockerExec = /** @type {DockerExec} */ (
|
|
269
|
+
function (
|
|
270
|
+
libdragonInfo,
|
|
271
|
+
dockerParams,
|
|
272
|
+
cmdWithParams,
|
|
273
|
+
/** @type {SpawnOptions | undefined} */
|
|
274
|
+
options
|
|
275
|
+
) {
|
|
276
|
+
assert(
|
|
277
|
+
!!libdragonInfo.containerId,
|
|
278
|
+
new Error('Trying to invoke dockerExec without a containerId.')
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// TODO: assert for invalid args
|
|
282
|
+
const haveDockerParams =
|
|
283
|
+
Array.isArray(dockerParams) && Array.isArray(cmdWithParams);
|
|
284
|
+
|
|
285
|
+
if (!haveDockerParams) {
|
|
286
|
+
options = /** @type {SpawnOptions} */ (cmdWithParams);
|
|
287
|
+
}
|
|
278
288
|
|
|
279
|
-
|
|
280
|
-
const haveDockerParams =
|
|
281
|
-
Array.isArray(dockerParams) && Array.isArray(cmdWithParams);
|
|
289
|
+
const additionalParams = [];
|
|
282
290
|
|
|
283
|
-
|
|
284
|
-
options
|
|
285
|
-
|
|
291
|
+
// Docker TTY wants in & out streams both to be a TTY
|
|
292
|
+
// If no options are provided, disable TTY as spawnProcess defaults to no
|
|
293
|
+
// inherit as well.
|
|
294
|
+
const enableTTY = options
|
|
295
|
+
? options.inheritStdout && options.inheritStdin
|
|
296
|
+
: false;
|
|
297
|
+
const ttyEnabled = enableTTY && process.stdout.isTTY && process.stdin.isTTY;
|
|
286
298
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
// If no options are provided, disable TTY as spawnProcess defaults to no
|
|
291
|
-
// inherit as well.
|
|
292
|
-
const enableTTY = options
|
|
293
|
-
? options.inheritStdout && options.inheritStdin
|
|
294
|
-
: false;
|
|
295
|
-
const ttyEnabled = enableTTY && process.stdout.isTTY && process.stdin.isTTY;
|
|
299
|
+
if (ttyEnabled) {
|
|
300
|
+
additionalParams.push('-t');
|
|
301
|
+
}
|
|
296
302
|
|
|
297
|
-
|
|
298
|
-
|
|
303
|
+
// Always enable stdin, also see; https://github.com/anacierdem/libdragon-docker/issues/45
|
|
304
|
+
// Currently we run all exec commands in stdin mode even if the actual process
|
|
305
|
+
// does not need any input. This will eat any user input by default.
|
|
306
|
+
additionalParams.push('-i');
|
|
307
|
+
|
|
308
|
+
return spawnProcess(
|
|
309
|
+
'docker',
|
|
310
|
+
[
|
|
311
|
+
'exec',
|
|
312
|
+
...(haveDockerParams
|
|
313
|
+
? [...dockerParams, ...additionalParams]
|
|
314
|
+
: additionalParams),
|
|
315
|
+
libdragonInfo.containerId,
|
|
316
|
+
...(haveDockerParams ? cmdWithParams : dockerParams),
|
|
317
|
+
],
|
|
318
|
+
options
|
|
319
|
+
);
|
|
299
320
|
}
|
|
300
|
-
|
|
301
|
-
// Always enable stdin, also see; https://github.com/anacierdem/libdragon-docker/issues/45
|
|
302
|
-
// Currently we run all exec commands in stdin mode even if the actual process
|
|
303
|
-
// does not need any input. This will eat any user input by default.
|
|
304
|
-
additionalParams.push('-i');
|
|
305
|
-
|
|
306
|
-
return spawnProcess(
|
|
307
|
-
'docker',
|
|
308
|
-
[
|
|
309
|
-
'exec',
|
|
310
|
-
...(haveDockerParams
|
|
311
|
-
? [...dockerParams, ...additionalParams]
|
|
312
|
-
: additionalParams),
|
|
313
|
-
libdragonInfo.containerId,
|
|
314
|
-
...(haveDockerParams ? cmdWithParams : dockerParams),
|
|
315
|
-
],
|
|
316
|
-
options
|
|
317
|
-
);
|
|
318
|
-
};
|
|
321
|
+
);
|
|
319
322
|
|
|
320
323
|
/**
|
|
321
324
|
* Recursively copies directories and files
|
|
@@ -386,8 +389,9 @@ function toNativePath(p) {
|
|
|
386
389
|
}
|
|
387
390
|
|
|
388
391
|
/**
|
|
389
|
-
* @param {
|
|
392
|
+
* @param {any} condition
|
|
390
393
|
* @param {Error} error
|
|
394
|
+
* @returns {asserts condition}
|
|
391
395
|
*/
|
|
392
396
|
function assert(condition, error) {
|
|
393
397
|
if (!condition) {
|
|
@@ -422,6 +426,18 @@ function log(text, verboseOnly = false) {
|
|
|
422
426
|
}
|
|
423
427
|
}
|
|
424
428
|
|
|
429
|
+
/**
|
|
430
|
+
* @param {import('./project-info').CLIInfo | import('./project-info').LibdragonInfo} info
|
|
431
|
+
* @returns {info is import('./project-info').LibdragonInfo}
|
|
432
|
+
*/
|
|
433
|
+
function isProjectAction(info) {
|
|
434
|
+
return !NO_PROJECT_ACTIONS.includes(
|
|
435
|
+
/** @type {import('./project-info').ActionsNoProject} */ (
|
|
436
|
+
info.options.CURRENT_ACTION.name
|
|
437
|
+
)
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
425
441
|
module.exports = {
|
|
426
442
|
spawnProcess,
|
|
427
443
|
toPosixPath,
|
|
@@ -436,4 +452,5 @@ module.exports = {
|
|
|
436
452
|
CommandError,
|
|
437
453
|
ParameterError,
|
|
438
454
|
ValidationError,
|
|
455
|
+
isProjectAction,
|
|
439
456
|
};
|
package/modules/parameters.js
CHANGED
|
@@ -26,7 +26,7 @@ const { globals } = require('./globals');
|
|
|
26
26
|
* @param {string[]} argv
|
|
27
27
|
*/
|
|
28
28
|
const parseParameters = async (argv) => {
|
|
29
|
-
/** @type CommandlineOptions */
|
|
29
|
+
/** @type Partial<CommandlineOptions> & {EXTRA_PARAMS: string[] } */
|
|
30
30
|
const options = {
|
|
31
31
|
EXTRA_PARAMS: [],
|
|
32
32
|
CURRENT_ACTION: undefined,
|
|
@@ -143,7 +143,7 @@ const parseParameters = async (argv) => {
|
|
|
143
143
|
process.exit(STATUS_BAD_PARAM);
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
return { options };
|
|
146
|
+
return { options: /** @type CommandlineOptions */ (options) };
|
|
147
147
|
};
|
|
148
148
|
|
|
149
149
|
module.exports = {
|
package/modules/project-info.js
CHANGED
|
@@ -27,37 +27,41 @@ const {
|
|
|
27
27
|
toPosixPath,
|
|
28
28
|
assert,
|
|
29
29
|
ParameterError,
|
|
30
|
+
isProjectAction,
|
|
30
31
|
} = require('./helpers');
|
|
31
32
|
|
|
32
|
-
// These do not need a project to exist.
|
|
33
|
-
const NO_PROJECT_ACTIONS = /** @type {const} */ (['help', 'version']);
|
|
34
|
-
|
|
35
33
|
/**
|
|
36
|
-
* @typedef { typeof NO_PROJECT_ACTIONS[number] } ActionsNoProject
|
|
34
|
+
* @typedef { typeof import('./constants').NO_PROJECT_ACTIONS[number] } ActionsNoProject
|
|
37
35
|
* @typedef { Exclude<keyof import('./parameters').Actions, ActionsNoProject> } ActionsWithProject
|
|
38
36
|
* @typedef { import('./parameters').CommandlineOptions<ActionsNoProject> } ActionsNoProjectOptions
|
|
39
37
|
* @typedef { import('./parameters').CommandlineOptions<ActionsWithProject> } ActionsWithProjectOptions
|
|
40
38
|
*
|
|
39
|
+
* This is all the potential CLI combinations
|
|
41
40
|
* @typedef { {
|
|
42
|
-
* options:
|
|
41
|
+
* options: import('./parameters').CommandlineOptions
|
|
43
42
|
* } } CLIInfo
|
|
44
43
|
*
|
|
44
|
+
* Then readProjectInfo creates two possible set of outputs. One is for actions
|
|
45
|
+
* that don't need a project and one with project. This setup forces the actions
|
|
46
|
+
* to not use detailed information if they are listed in NO_PROJECT_ACTIONS
|
|
47
|
+
* @typedef { {
|
|
48
|
+
* options: ActionsNoProjectOptions
|
|
49
|
+
* } } NoProjectInfo
|
|
50
|
+
*
|
|
45
51
|
* @typedef { {
|
|
52
|
+
* options: ActionsWithProjectOptions
|
|
46
53
|
* root: string;
|
|
47
54
|
* userInfo: os.UserInfo<string>;
|
|
48
55
|
* haveProjectConfig: boolean;
|
|
49
56
|
* imageName: string;
|
|
50
57
|
* vendorDirectory: string;
|
|
51
58
|
* vendorStrategy: import('./parameters').VendorStrategy;
|
|
52
|
-
* containerId
|
|
53
|
-
* } }
|
|
54
|
-
*
|
|
55
|
-
* @typedef { CLIInfo & Partial<ExtendedInfo> } LibdragonInfo
|
|
59
|
+
* containerId?: string
|
|
60
|
+
* } } LibdragonInfo
|
|
56
61
|
*/
|
|
57
62
|
|
|
58
63
|
/**
|
|
59
64
|
* @param {LibdragonInfo} libdragonInfo
|
|
60
|
-
* @returns string
|
|
61
65
|
*/
|
|
62
66
|
async function findContainerId(libdragonInfo) {
|
|
63
67
|
const idFile = path.join(libdragonInfo.root, '.git', CACHED_CONTAINER_FILE);
|
|
@@ -109,7 +113,7 @@ async function findContainerId(libdragonInfo) {
|
|
|
109
113
|
/**
|
|
110
114
|
* @param {string} start
|
|
111
115
|
* @param {string} relativeFile
|
|
112
|
-
* @
|
|
116
|
+
* @returns {Promise<string | undefined>}
|
|
113
117
|
*/
|
|
114
118
|
async function findLibdragonRoot(
|
|
115
119
|
start = '.',
|
|
@@ -139,18 +143,15 @@ async function findGitRoot() {
|
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
/**
|
|
142
|
-
* @param {
|
|
146
|
+
* @param {CLIInfo} optionInfo
|
|
147
|
+
* @returns {Promise<LibdragonInfo | NoProjectInfo>}
|
|
143
148
|
*/
|
|
144
149
|
const readProjectInfo = async function (optionInfo) {
|
|
145
150
|
// No need to do anything here if the action does not depend on the project
|
|
146
151
|
// The only exception is the init and destroy actions, which do not need an
|
|
147
152
|
// existing project but readProjectInfo must always run to analyze the situation
|
|
148
|
-
if (
|
|
149
|
-
|
|
150
|
-
/** @type {ActionsNoProject} */ (optionInfo.options.CURRENT_ACTION.name)
|
|
151
|
-
)
|
|
152
|
-
) {
|
|
153
|
-
return /** @type {CLIInfo} */ (optionInfo);
|
|
153
|
+
if (!isProjectAction(optionInfo)) {
|
|
154
|
+
return /** @type {NoProjectInfo} */ (optionInfo);
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
const migratedRoot = await findLibdragonRoot();
|
|
@@ -173,10 +174,16 @@ const readProjectInfo = async function (optionInfo) {
|
|
|
173
174
|
);
|
|
174
175
|
}
|
|
175
176
|
|
|
177
|
+
const foundRoot =
|
|
178
|
+
projectRoot ?? (await findNPMRoot()) ?? (await findGitRoot());
|
|
179
|
+
if (!foundRoot) {
|
|
180
|
+
log('Could not find project root, set as cwd.', true);
|
|
181
|
+
}
|
|
182
|
+
|
|
176
183
|
/** @type {LibdragonInfo} */
|
|
177
184
|
let info = {
|
|
178
185
|
...optionInfo,
|
|
179
|
-
root:
|
|
186
|
+
root: foundRoot ?? process.cwd(),
|
|
180
187
|
userInfo: os.userInfo(),
|
|
181
188
|
|
|
182
189
|
// Use this to discriminate if there is a project when the command is run
|
|
@@ -190,11 +197,6 @@ const readProjectInfo = async function (optionInfo) {
|
|
|
190
197
|
vendorStrategy: DEFAULT_STRATEGY,
|
|
191
198
|
};
|
|
192
199
|
|
|
193
|
-
if (!info.root) {
|
|
194
|
-
log('Could not find project root, set as cwd.', true);
|
|
195
|
-
info.root = process.cwd();
|
|
196
|
-
}
|
|
197
|
-
|
|
198
200
|
log(`Project root: ${info.root}`, true);
|
|
199
201
|
|
|
200
202
|
if (migratedRoot) {
|
|
@@ -231,7 +233,11 @@ const readProjectInfo = async function (optionInfo) {
|
|
|
231
233
|
// As last option fallback to default value.
|
|
232
234
|
|
|
233
235
|
// If still have the container, read the image name from it
|
|
234
|
-
if (
|
|
236
|
+
if (
|
|
237
|
+
!info.imageName &&
|
|
238
|
+
info.containerId &&
|
|
239
|
+
(await checkContainerAndClean(info))
|
|
240
|
+
) {
|
|
235
241
|
info.imageName = (
|
|
236
242
|
await spawnProcess('docker', [
|
|
237
243
|
'container',
|