libdragon 11.1.3 → 11.3.0
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 +32 -18
- package/modules/actions/init.js +123 -19
- package/modules/actions/update.js +2 -2
- package/modules/constants.js +1 -1
- package/modules/helpers.js +24 -61
- package/modules/npm-utils.js +15 -2
- package/modules/parameters.js +23 -2
- package/modules/project-info.js +18 -4
- package/modules/utils.js +6 -22
- package/package.json +18 -21
- package/skeleton/index.d.ts +4 -0
- /package/skeleton/{Makefile → Makefile.mk} +0 -0
package/README.md
CHANGED
|
@@ -6,30 +6,34 @@ This is a wrapper for a docker container to make managing the libdragon toolchai
|
|
|
6
6
|
|
|
7
7
|
## Prerequisites
|
|
8
8
|
|
|
9
|
-
You should have [docker](https://www.docker.com/products/docker-desktop) (`>=
|
|
9
|
+
You should have [docker](https://www.docker.com/products/docker-desktop) (`>= 27.2.0`) installed on your system.
|
|
10
10
|
|
|
11
11
|
`git` is not strictly required to use the tool. Still, it is highly recommended to have git on your host machine as it will be used instead of the one in the container.
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
15
|
-
This is primarily a node.js script which is also packaged as an executable. You have
|
|
15
|
+
This is primarily a node.js script which is also packaged as an executable. You have a few options:
|
|
16
16
|
|
|
17
|
-
###
|
|
17
|
+
### installer
|
|
18
18
|
|
|
19
|
-
Download the [
|
|
19
|
+
Download the [windows installer](https://github.com/anacierdem/libdragon-docker/releases/latest) and run it. This option is currently only available on Windows.
|
|
20
20
|
|
|
21
21
|
<details>
|
|
22
|
-
<summary>
|
|
22
|
+
<summary>Detailed instructions</summary>
|
|
23
23
|
|
|
24
24
|
- Download the Windows installer and run it
|
|
25
|
-
- It can show a "Windows protected your PC" warning.
|
|
25
|
+
- It can show a "Windows protected your PC" warning. Click "More info" and "Run anyway".
|
|
26
26
|
- You should now be able to use the `libdragon` on a command line or PowerShell.
|
|
27
27
|
- To update it with a new version, download a newer installer and repeat the steps above.
|
|
28
28
|
|
|
29
29
|
</details>
|
|
30
30
|
|
|
31
|
+
### pre-built executable
|
|
32
|
+
|
|
33
|
+
Download the [pre-built executable](https://github.com/anacierdem/libdragon-docker/releases/latest) for your system and put it somewhere on your PATH. It is discouraged to put it in your project folder.
|
|
34
|
+
|
|
31
35
|
<details>
|
|
32
|
-
<summary>Windows instructions
|
|
36
|
+
<summary>Windows instructions</summary>
|
|
33
37
|
|
|
34
38
|
- Download the Windows executable and copy it to `C:\bin`
|
|
35
39
|
- Press `Windows + R` key combination and then enter `rundll32 sysdm.cpl,EditEnvironmentVariables`
|
|
@@ -101,15 +105,19 @@ Run `libdragon help [action]` for more details on individual actions.
|
|
|
101
105
|
|
|
102
106
|
## Recipes
|
|
103
107
|
|
|
104
|
-
### Using a different branch
|
|
108
|
+
### Using a different libdragon branch
|
|
105
109
|
|
|
106
|
-
|
|
110
|
+
Use the `--branch` flag to set up a custom libdragon branch when initializing your project:
|
|
107
111
|
|
|
108
112
|
```bash
|
|
109
|
-
libdragon init
|
|
113
|
+
libdragon init --branch unstable
|
|
110
114
|
```
|
|
111
115
|
|
|
112
|
-
|
|
116
|
+
This will use the `unstable` toolchain and code.
|
|
117
|
+
|
|
118
|
+
### Switching to a different branch of libdragon
|
|
119
|
+
|
|
120
|
+
On an already initialized project, switch the submodule to the desired branch:
|
|
113
121
|
|
|
114
122
|
```bash
|
|
115
123
|
git -C ./libdragon checkout opengl
|
|
@@ -157,7 +165,7 @@ To be able to share your project with the library change, you just commit your c
|
|
|
157
165
|
|
|
158
166
|
## Working on this repository
|
|
159
167
|
|
|
160
|
-
After cloning this repository on a system with node.js (`>= 18`) & docker (`>=
|
|
168
|
+
After cloning this repository on a system with node.js (`>= 18`) & docker (`>= 27.2.0`), in this repository's root do;
|
|
161
169
|
|
|
162
170
|
```bash
|
|
163
171
|
npm install
|
|
@@ -200,7 +208,9 @@ Similarly to run the `clean` recipe, run;
|
|
|
200
208
|
npm run libdragon -- make clean
|
|
201
209
|
```
|
|
202
210
|
|
|
203
|
-
|
|
211
|
+
> [!IMPORTANT]
|
|
212
|
+
> Keep in mind that `--` is necessary for actual arguments when using npm scripts.
|
|
213
|
+
|
|
204
214
|
|
|
205
215
|
To update the submodule and re-build everything;
|
|
206
216
|
|
|
@@ -214,7 +224,8 @@ The root `bench` recipe is for building the code in root `src` folder. This is a
|
|
|
214
224
|
|
|
215
225
|
There are also vscode launch configurations to quickly build the examples, tests and the bench. If you have the `N64_EMU` environment variable set pointing at your favourite emulator ([ares](https://ares-emu.net/) is highly recommended), the launch configurations ending in `(emu)` will kick your emulator with the selected example/code. You can also just hit F5 to launch the selected configuration.
|
|
216
226
|
|
|
217
|
-
|
|
227
|
+
> [!NOTE]
|
|
228
|
+
> This repository also uses [ed64](https://github.com/anacierdem/ed64), so you can use the launch configurations without `(emu)` to run the code if you have an everdrive plugged in and your active configuration will just boot up.
|
|
218
229
|
|
|
219
230
|
You can clean everything with the `clean` recipe/task (open the command palette and choose `Run Task -> clean`).
|
|
220
231
|
|
|
@@ -278,7 +289,12 @@ To create your own dev container backed project, you can use the contents of the
|
|
|
278
289
|
- It will prepare the container and open it in the editor.
|
|
279
290
|
</details>
|
|
280
291
|
|
|
281
|
-
##
|
|
292
|
+
## Dependency management
|
|
293
|
+
|
|
294
|
+
> [!WARNING]
|
|
295
|
+
> This is an experimental feature.
|
|
296
|
+
|
|
297
|
+
### As an NPM dependency
|
|
282
298
|
|
|
283
299
|
You can install libdragon as an NPM dependency by `npm install libdragon --save` in order to use docker in your N64 projects. A `libdragon` command similar to global intallation is provided that can be used in your NPM scripts as follows;
|
|
284
300
|
|
|
@@ -292,7 +308,7 @@ You can install libdragon as an NPM dependency by `npm install libdragon --save`
|
|
|
292
308
|
|
|
293
309
|
See [here](https://github.com/anacierdem/ed64-example) for a full example.
|
|
294
310
|
|
|
295
|
-
|
|
311
|
+
### Developing a dependency
|
|
296
312
|
|
|
297
313
|
You can make an NPM package that a `libdragon` project can depend on. Just include a `Makefile` on the repository root with a default recipe and an `install` recipe. On the depending project, after installing libdragon and the dependency with `npm install <dep name> --save`, one can install libdragon dependencies on the current docker container using `package.json` scripts.
|
|
298
314
|
|
|
@@ -317,8 +333,6 @@ For example this `package.json` in the dependent project;
|
|
|
317
333
|
|
|
318
334
|
will init the container for this project and run `make && make install` for `ed64` upon running `npm install`. To develop a dependency [this](https://github.com/anacierdem/libdragon-dependency) is a good starting point.
|
|
319
335
|
|
|
320
|
-
This is an experimental dependency management.
|
|
321
|
-
|
|
322
336
|
## Funding
|
|
323
337
|
|
|
324
338
|
If this tool helped you, consider supporting its development by sponsoring it!
|
package/modules/actions/init.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
/// <reference path="../../globals.d.ts" />
|
|
2
|
+
|
|
1
3
|
const fs = require('fs/promises');
|
|
4
|
+
const _fs = require('fs');
|
|
2
5
|
const path = require('path');
|
|
6
|
+
const sea = require('node:sea');
|
|
3
7
|
|
|
4
8
|
const chalk = require('chalk').stderr;
|
|
5
9
|
|
|
@@ -16,19 +20,33 @@ const {
|
|
|
16
20
|
const {
|
|
17
21
|
LIBDRAGON_PROJECT_MANIFEST,
|
|
18
22
|
LIBDRAGON_SUBMODULE,
|
|
19
|
-
LIBDRAGON_BRANCH,
|
|
20
23
|
LIBDRAGON_GIT,
|
|
21
24
|
} = require('../constants');
|
|
22
25
|
const {
|
|
23
26
|
log,
|
|
24
|
-
copyDirContents,
|
|
25
27
|
CommandError,
|
|
26
28
|
ParameterError,
|
|
27
29
|
ValidationError,
|
|
28
30
|
toPosixPath,
|
|
29
31
|
toNativePath,
|
|
32
|
+
getImageName,
|
|
30
33
|
} = require('../helpers');
|
|
31
34
|
|
|
35
|
+
// TODO: use https://nodejs.org/api/single-executable-applications.html#seagetassetkey-encoding &&
|
|
36
|
+
// https://nodejs.org/api/single-executable-applications.html#assets instead
|
|
37
|
+
const main_c = sea.isSea()
|
|
38
|
+
? // @ts-ignore-next-line
|
|
39
|
+
/** @type {string} */ (require('../../skeleton/src/main.c'))
|
|
40
|
+
: _fs.readFileSync(path.join(__dirname, '../../skeleton/src/main.c'), 'utf8');
|
|
41
|
+
|
|
42
|
+
const makefile = sea.isSea()
|
|
43
|
+
? // @ts-ignore-next-line
|
|
44
|
+
/** @type {string} */ require('../../skeleton/Makefile.mk')
|
|
45
|
+
: _fs.readFileSync(
|
|
46
|
+
path.join(__dirname, '../../skeleton/Makefile.mk'),
|
|
47
|
+
'utf8'
|
|
48
|
+
);
|
|
49
|
+
|
|
32
50
|
/**
|
|
33
51
|
* @param {import('../project-info').LibdragonInfo} info
|
|
34
52
|
* @returns {Promise<"submodule" | "subtree" | undefined>}
|
|
@@ -45,11 +63,15 @@ const autoDetect = async (info) => {
|
|
|
45
63
|
|
|
46
64
|
if (
|
|
47
65
|
vendorTargetExists &&
|
|
48
|
-
(await runGitMaybeHost(
|
|
49
|
-
|
|
50
|
-
'status',
|
|
51
|
-
|
|
52
|
-
|
|
66
|
+
(await runGitMaybeHost(
|
|
67
|
+
info,
|
|
68
|
+
['submodule', 'status', info.vendorDirectory],
|
|
69
|
+
{
|
|
70
|
+
inheritStdin: false,
|
|
71
|
+
inheritStdout: false,
|
|
72
|
+
inheritStderr: false,
|
|
73
|
+
}
|
|
74
|
+
).catch((e) => {
|
|
53
75
|
if (!(e instanceof CommandError)) {
|
|
54
76
|
throw e;
|
|
55
77
|
}
|
|
@@ -148,7 +170,7 @@ const autoVendor = async (info) => {
|
|
|
148
170
|
'--name',
|
|
149
171
|
LIBDRAGON_SUBMODULE,
|
|
150
172
|
'--branch',
|
|
151
|
-
|
|
173
|
+
info.activeBranchName,
|
|
152
174
|
LIBDRAGON_GIT,
|
|
153
175
|
info.vendorDirectory,
|
|
154
176
|
]);
|
|
@@ -156,6 +178,9 @@ const autoVendor = async (info) => {
|
|
|
156
178
|
if (!(e instanceof CommandError)) throw e;
|
|
157
179
|
// We speculate this is caused by the user, so replace it with a more useful error message.
|
|
158
180
|
e.message = `Unable to create submodule. If you have copied the executable in your project folder or you have a file named ${info.vendorDirectory}, removing it might solve this issue. Original error:\n${e.message}`;
|
|
181
|
+
if (e.out) {
|
|
182
|
+
e.message += `\n${e.out}`;
|
|
183
|
+
}
|
|
159
184
|
throw e;
|
|
160
185
|
}
|
|
161
186
|
|
|
@@ -189,7 +214,7 @@ const autoVendor = async (info) => {
|
|
|
189
214
|
'--prefix',
|
|
190
215
|
path.relative(info.root, info.vendorDirectory),
|
|
191
216
|
LIBDRAGON_GIT,
|
|
192
|
-
|
|
217
|
+
info.activeBranchName,
|
|
193
218
|
'--squash',
|
|
194
219
|
]);
|
|
195
220
|
return info;
|
|
@@ -198,6 +223,47 @@ const autoVendor = async (info) => {
|
|
|
198
223
|
return info;
|
|
199
224
|
};
|
|
200
225
|
|
|
226
|
+
/**
|
|
227
|
+
* @param {import('../project-info').LibdragonInfo} info
|
|
228
|
+
*/
|
|
229
|
+
async function copyFiles(info) {
|
|
230
|
+
// TODO: make use of VFS, this is far from ideal
|
|
231
|
+
const srcPath = path.join(info.root, 'src');
|
|
232
|
+
|
|
233
|
+
const srcStat = await fs.stat(srcPath).catch((e) => {
|
|
234
|
+
if (e.code !== 'ENOENT') throw e;
|
|
235
|
+
return null;
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (srcStat && !srcStat.isDirectory()) {
|
|
239
|
+
log(`${srcPath} is not a directory, skipping.`);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!srcStat) {
|
|
244
|
+
log(`Creating a directory at ${srcPath}.`, true);
|
|
245
|
+
await fs.mkdir(srcPath);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const makefilePath = path.join(info.root, 'Makefile');
|
|
249
|
+
await fs
|
|
250
|
+
.writeFile(makefilePath, makefile, {
|
|
251
|
+
flag: 'wx',
|
|
252
|
+
})
|
|
253
|
+
.catch(() => {
|
|
254
|
+
log(`${makefilePath} already exists, skipping.`);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const mainPath = path.join(info.root, 'src', 'main.c');
|
|
258
|
+
await fs
|
|
259
|
+
.writeFile(mainPath, main_c, {
|
|
260
|
+
flag: 'wx',
|
|
261
|
+
})
|
|
262
|
+
.catch(() => {
|
|
263
|
+
log(`${mainPath} already exists, skipping.`);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
201
267
|
/**
|
|
202
268
|
* Initialize a new libdragon project in current working directory
|
|
203
269
|
* Also downloads the image
|
|
@@ -252,11 +318,44 @@ async function init(info) {
|
|
|
252
318
|
}
|
|
253
319
|
|
|
254
320
|
if (!process.env.DOCKER_CONTAINER) {
|
|
255
|
-
|
|
256
|
-
|
|
321
|
+
let activeBranchName = info.options.BRANCH ?? info.activeBranchName;
|
|
322
|
+
let shouldOverrideBranch = !!info.options.BRANCH;
|
|
323
|
+
|
|
324
|
+
if ((await autoDetect(info)) === 'submodule') {
|
|
325
|
+
try {
|
|
326
|
+
const existingBranchName = await runGitMaybeHost(
|
|
327
|
+
info,
|
|
328
|
+
[
|
|
329
|
+
'-C',
|
|
330
|
+
path.relative(info.root, info.vendorDirectory),
|
|
331
|
+
'rev-parse',
|
|
332
|
+
'--abbrev-ref',
|
|
333
|
+
'HEAD',
|
|
334
|
+
],
|
|
335
|
+
{
|
|
336
|
+
inheritStdin: false,
|
|
337
|
+
inheritStdout: false,
|
|
338
|
+
inheritStderr: false,
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
activeBranchName = existingBranchName.trim();
|
|
342
|
+
shouldOverrideBranch = !!activeBranchName;
|
|
343
|
+
} catch {
|
|
344
|
+
// If we can't get the branch name, we will use the default
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
257
348
|
info = {
|
|
258
349
|
...info,
|
|
259
|
-
|
|
350
|
+
activeBranchName,
|
|
351
|
+
// We don't have a config yet, this is a fresh project, override with the
|
|
352
|
+
// image flag if provided. Also see https://github.com/anacierdem/libdragon-docker/issues/66
|
|
353
|
+
// Next, if `--branch` flag is provided use that or the one recovered from
|
|
354
|
+
// the submodule.
|
|
355
|
+
imageName:
|
|
356
|
+
(info.options.DOCKER_IMAGE ??
|
|
357
|
+
(shouldOverrideBranch && getImageName(activeBranchName))) ||
|
|
358
|
+
info.imageName,
|
|
260
359
|
};
|
|
261
360
|
// Download image and start it
|
|
262
361
|
await updateImage(info, info.imageName);
|
|
@@ -273,13 +372,8 @@ async function init(info) {
|
|
|
273
372
|
info = await autoVendor(info);
|
|
274
373
|
|
|
275
374
|
log(`Preparing project files...`);
|
|
276
|
-
const skeletonFolder = path.join(__dirname, '../../skeleton');
|
|
277
375
|
|
|
278
|
-
await Promise.all([
|
|
279
|
-
installDependencies(info),
|
|
280
|
-
// node copy functions does not work with pkg
|
|
281
|
-
copyDirContents(skeletonFolder, info.root),
|
|
282
|
-
]);
|
|
376
|
+
await Promise.all([installDependencies(info), copyFiles(info)]);
|
|
283
377
|
|
|
284
378
|
log(chalk.green(`libdragon ready at \`${info.root}\`.`));
|
|
285
379
|
return info;
|
|
@@ -299,6 +393,16 @@ module.exports = /** @type {const} */ ({
|
|
|
299
393
|
If this is the first time you are creating a libdragon project at that location, this action will also create skeleton project files to kickstart things with the given image, if provided. For subsequent runs without any parameter, it will act like \`start\` thus can be used to revive an existing project without modifying it.
|
|
300
394
|
|
|
301
395
|
If you have an existing project with an already vendored submodule or subtree libdragon copy, \`init\` will automatically detect it at the provided \`--directory\`.`,
|
|
302
|
-
group: ['docker', 'vendoring'],
|
|
396
|
+
group: ['docker', 'vendoring', '_none'],
|
|
397
|
+
|
|
398
|
+
optionList: [
|
|
399
|
+
{
|
|
400
|
+
name: 'branch',
|
|
401
|
+
description:
|
|
402
|
+
'Provide a different branch to initialize libdragon. It will also change the toolchain docker image to be based on the given branch but `--image` will have precedence. Defaults to `trunk` & `latest` image.',
|
|
403
|
+
alias: 'b',
|
|
404
|
+
typeLabel: '<branch>',
|
|
405
|
+
},
|
|
406
|
+
],
|
|
303
407
|
},
|
|
304
408
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { log, assert } = require('../helpers');
|
|
2
|
-
const { LIBDRAGON_GIT
|
|
2
|
+
const { LIBDRAGON_GIT } = require('../constants');
|
|
3
3
|
const {
|
|
4
4
|
runGitMaybeHost,
|
|
5
5
|
installDependencies,
|
|
@@ -69,7 +69,7 @@ const update = async (info) => {
|
|
|
69
69
|
'--prefix',
|
|
70
70
|
info.vendorDirectory,
|
|
71
71
|
LIBDRAGON_GIT,
|
|
72
|
-
|
|
72
|
+
info.activeBranchName,
|
|
73
73
|
'--squash',
|
|
74
74
|
]);
|
|
75
75
|
}
|
package/modules/constants.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module.exports = /** @type {const} */ ({
|
|
2
|
-
DOCKER_HUB_IMAGE: 'ghcr.io/dragonminded/libdragon
|
|
2
|
+
DOCKER_HUB_IMAGE: 'ghcr.io/dragonminded/libdragon',
|
|
3
3
|
LIBDRAGON_GIT: 'https://github.com/DragonMinded/libdragon',
|
|
4
4
|
LIBDRAGON_BRANCH: 'trunk',
|
|
5
5
|
LIBDRAGON_SUBMODULE: 'libdragon',
|
package/modules/helpers.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs/promises');
|
|
3
|
-
const fsClassic = require('fs');
|
|
4
3
|
const chalk = require('chalk').stderr;
|
|
5
4
|
const { spawn } = require('child_process');
|
|
6
5
|
|
|
7
6
|
const { globals } = require('./globals');
|
|
8
|
-
const {
|
|
7
|
+
const {
|
|
8
|
+
NO_PROJECT_ACTIONS,
|
|
9
|
+
LIBDRAGON_BRANCH,
|
|
10
|
+
DOCKER_HUB_IMAGE,
|
|
11
|
+
} = require('./constants');
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* A structure to keep additional error information
|
|
@@ -117,7 +120,7 @@ async function dirExists(path) {
|
|
|
117
120
|
* inheritStdout?: boolean,
|
|
118
121
|
* inheritStderr?: boolean,
|
|
119
122
|
* spawnOptions?: import('child_process').SpawnOptions,
|
|
120
|
-
* stdin?:
|
|
123
|
+
* stdin?: import('fs').ReadStream,
|
|
121
124
|
* }} SpawnOptions
|
|
122
125
|
*/
|
|
123
126
|
|
|
@@ -347,60 +350,6 @@ const dockerExec = /** @type {DockerExec} */ (
|
|
|
347
350
|
}
|
|
348
351
|
);
|
|
349
352
|
|
|
350
|
-
/**
|
|
351
|
-
* Recursively copies directories and files
|
|
352
|
-
* @param {string} src
|
|
353
|
-
* @param {string} dst
|
|
354
|
-
*/
|
|
355
|
-
async function copyDirContents(src, dst) {
|
|
356
|
-
log(`Copying from ${src} to ${dst}`, true);
|
|
357
|
-
|
|
358
|
-
const dstStat = await fs.stat(dst).catch((e) => {
|
|
359
|
-
if (e.code !== 'ENOENT') throw e;
|
|
360
|
-
return null;
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
if (dstStat && !dstStat.isDirectory()) {
|
|
364
|
-
log(`${dst} is not a directory, skipping.`);
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (!dstStat) {
|
|
369
|
-
log(`Creating a directory at ${dst}.`, true);
|
|
370
|
-
await fs.mkdir(dst);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const files = await fs.readdir(src);
|
|
374
|
-
return Promise.all(
|
|
375
|
-
files.map(async (name) => {
|
|
376
|
-
const source = path.join(src, name);
|
|
377
|
-
const dest = path.join(dst, name);
|
|
378
|
-
// promise version does not work on snapshot filesystem
|
|
379
|
-
// Also see https://github.com/vercel/pkg/issues/1561
|
|
380
|
-
const stats = await new Promise((res, rej) =>
|
|
381
|
-
fsClassic.stat(source, (err, stats) => {
|
|
382
|
-
if (err) return rej(err);
|
|
383
|
-
res(stats);
|
|
384
|
-
})
|
|
385
|
-
);
|
|
386
|
-
if (stats.isDirectory()) {
|
|
387
|
-
await copyDirContents(source, dest);
|
|
388
|
-
} else if (stats.isFile()) {
|
|
389
|
-
const content = await fs.readFile(source);
|
|
390
|
-
try {
|
|
391
|
-
log(`Writing to ${dest}`, true);
|
|
392
|
-
await fs.writeFile(dest, content, {
|
|
393
|
-
flag: 'wx',
|
|
394
|
-
});
|
|
395
|
-
} catch (e) {
|
|
396
|
-
log(`${dest} already exists, skipping.`);
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
})
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
353
|
/**
|
|
405
354
|
* @param {string} p
|
|
406
355
|
*/
|
|
@@ -442,8 +391,10 @@ function print(text) {
|
|
|
442
391
|
*/
|
|
443
392
|
function log(text, verboseOnly = false) {
|
|
444
393
|
if (!verboseOnly) {
|
|
394
|
+
// New default color for console.err is red
|
|
395
|
+
// Also see https://github.com/nodejs/node/issues/53661
|
|
445
396
|
// eslint-disable-next-line no-console
|
|
446
|
-
console.error(text);
|
|
397
|
+
console.error(chalk.white(text));
|
|
447
398
|
return;
|
|
448
399
|
}
|
|
449
400
|
if (globals.verbose) {
|
|
@@ -454,8 +405,8 @@ function log(text, verboseOnly = false) {
|
|
|
454
405
|
}
|
|
455
406
|
|
|
456
407
|
/**
|
|
457
|
-
* @param {import('./project-info').CLIInfo
|
|
458
|
-
* @returns {info is import('./project-info').
|
|
408
|
+
* @param {import('./project-info').CLIInfo} info
|
|
409
|
+
* @returns {info is import('./project-info').ProjectInfo}
|
|
459
410
|
*/
|
|
460
411
|
function isProjectAction(info) {
|
|
461
412
|
return !NO_PROJECT_ACTIONS.includes(
|
|
@@ -465,6 +416,18 @@ function isProjectAction(info) {
|
|
|
465
416
|
);
|
|
466
417
|
}
|
|
467
418
|
|
|
419
|
+
/**
|
|
420
|
+
* @param {string} branchName
|
|
421
|
+
* @returns {string}
|
|
422
|
+
*/
|
|
423
|
+
function getImageName(branchName) {
|
|
424
|
+
return (
|
|
425
|
+
DOCKER_HUB_IMAGE +
|
|
426
|
+
':' +
|
|
427
|
+
(branchName === LIBDRAGON_BRANCH ? 'latest' : branchName)
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
|
|
468
431
|
module.exports = {
|
|
469
432
|
spawnProcess,
|
|
470
433
|
toPosixPath,
|
|
@@ -475,9 +438,9 @@ module.exports = {
|
|
|
475
438
|
assert,
|
|
476
439
|
fileExists,
|
|
477
440
|
dirExists,
|
|
478
|
-
copyDirContents,
|
|
479
441
|
CommandError,
|
|
480
442
|
ParameterError,
|
|
481
443
|
ValidationError,
|
|
482
444
|
isProjectAction,
|
|
445
|
+
getImageName,
|
|
483
446
|
};
|
package/modules/npm-utils.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fsClassic = require('fs');
|
|
3
|
+
const fs = require('fs/promises');
|
|
3
4
|
|
|
5
|
+
// TODO: stop using lodash just for a simple uniq function
|
|
4
6
|
const _ = require('lodash');
|
|
5
7
|
|
|
6
8
|
const { dockerHostUserParams } = require('./docker-utils');
|
|
@@ -36,7 +38,9 @@ const installNPMDependencies = async (libdragonInfo) => {
|
|
|
36
38
|
if (npmRoot) {
|
|
37
39
|
const packageJsonPath = path.join(npmRoot, 'package.json');
|
|
38
40
|
|
|
39
|
-
const { dependencies, devDependencies } =
|
|
41
|
+
const { dependencies, devDependencies } = JSON.parse(
|
|
42
|
+
await fs.readFile(packageJsonPath, { encoding: 'utf8' })
|
|
43
|
+
);
|
|
40
44
|
|
|
41
45
|
const deps = await Promise.all(
|
|
42
46
|
Object.keys({
|
|
@@ -121,7 +125,16 @@ const installNPMDependencies = async (libdragonInfo) => {
|
|
|
121
125
|
function runNPM(params) {
|
|
122
126
|
return spawnProcess(
|
|
123
127
|
/^win/.test(process.platform) ? 'npm.cmd' : 'npm',
|
|
124
|
-
params
|
|
128
|
+
params,
|
|
129
|
+
{
|
|
130
|
+
userCommand: false,
|
|
131
|
+
inheritStdin: true,
|
|
132
|
+
inheritStdout: false,
|
|
133
|
+
inheritStderr: false,
|
|
134
|
+
spawnOptions: {
|
|
135
|
+
shell: true,
|
|
136
|
+
},
|
|
137
|
+
}
|
|
125
138
|
);
|
|
126
139
|
}
|
|
127
140
|
module.exports = {
|
package/modules/parameters.js
CHANGED
|
@@ -19,6 +19,7 @@ const { globals } = require('./globals');
|
|
|
19
19
|
* VENDOR_DIR?: string;
|
|
20
20
|
* VENDOR_STRAT?: VendorStrategy;
|
|
21
21
|
* FILE?: string;
|
|
22
|
+
* BRANCH?: string;
|
|
22
23
|
* }} CommandlineOptions
|
|
23
24
|
*/
|
|
24
25
|
|
|
@@ -81,6 +82,14 @@ const parseParameters = async (argv) => {
|
|
|
81
82
|
continue;
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
if (['--branch', '-b'].includes(val)) {
|
|
86
|
+
options.BRANCH = argv[++i];
|
|
87
|
+
continue;
|
|
88
|
+
} else if (val.indexOf('--branch=') === 0) {
|
|
89
|
+
options.BRANCH = val.split('=')[1];
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
|
|
84
93
|
if (val.indexOf('-') == 0) {
|
|
85
94
|
log(chalk.red(`Invalid flag \`${val}\``));
|
|
86
95
|
process.exit(STATUS_BAD_PARAM);
|
|
@@ -126,7 +135,7 @@ const parseParameters = async (argv) => {
|
|
|
126
135
|
) &&
|
|
127
136
|
options.DOCKER_IMAGE
|
|
128
137
|
) {
|
|
129
|
-
log(chalk.red('Invalid flag: image'));
|
|
138
|
+
log(chalk.red('Invalid flag: --image'));
|
|
130
139
|
process.exit(STATUS_BAD_PARAM);
|
|
131
140
|
}
|
|
132
141
|
|
|
@@ -138,7 +147,19 @@ const parseParameters = async (argv) => {
|
|
|
138
147
|
) &&
|
|
139
148
|
options.FILE
|
|
140
149
|
) {
|
|
141
|
-
log(chalk.red('Invalid flag: file'));
|
|
150
|
+
log(chalk.red('Invalid flag: --file'));
|
|
151
|
+
process.exit(STATUS_BAD_PARAM);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (
|
|
155
|
+
!(
|
|
156
|
+
/** @type {typeof actions[keyof actions][]} */ ([actions.init]).includes(
|
|
157
|
+
options.CURRENT_ACTION
|
|
158
|
+
)
|
|
159
|
+
) &&
|
|
160
|
+
options.BRANCH
|
|
161
|
+
) {
|
|
162
|
+
log(chalk.red('Invalid flag: --branch'));
|
|
142
163
|
process.exit(STATUS_BAD_PARAM);
|
|
143
164
|
}
|
|
144
165
|
|
package/modules/project-info.js
CHANGED
|
@@ -12,11 +12,11 @@ const { findNPMRoot } = require('./npm-utils');
|
|
|
12
12
|
const {
|
|
13
13
|
LIBDRAGON_PROJECT_MANIFEST,
|
|
14
14
|
CONFIG_FILE,
|
|
15
|
-
DOCKER_HUB_IMAGE,
|
|
16
15
|
DEFAULT_STRATEGY,
|
|
17
16
|
LIBDRAGON_SUBMODULE,
|
|
18
17
|
CACHED_CONTAINER_FILE,
|
|
19
18
|
CONTAINER_TARGET_PATH,
|
|
19
|
+
LIBDRAGON_BRANCH,
|
|
20
20
|
} = require('./constants');
|
|
21
21
|
|
|
22
22
|
const {
|
|
@@ -27,6 +27,7 @@ const {
|
|
|
27
27
|
assert,
|
|
28
28
|
ParameterError,
|
|
29
29
|
isProjectAction,
|
|
30
|
+
getImageName,
|
|
30
31
|
} = require('./helpers');
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -49,13 +50,18 @@ const {
|
|
|
49
50
|
*
|
|
50
51
|
* @typedef { {
|
|
51
52
|
* options: ActionsWithProjectOptions
|
|
53
|
+
* } } ProjectInfo
|
|
54
|
+
*
|
|
55
|
+
* @typedef { {
|
|
56
|
+
* options: ActionsWithProjectOptions
|
|
52
57
|
* root: string;
|
|
53
58
|
* userInfo: os.UserInfo<string>;
|
|
54
59
|
* haveProjectConfig: boolean;
|
|
55
60
|
* imageName: string;
|
|
56
61
|
* vendorDirectory: string;
|
|
57
62
|
* vendorStrategy: import('./parameters').VendorStrategy;
|
|
58
|
-
* containerId?: string
|
|
63
|
+
* containerId?: string;
|
|
64
|
+
* activeBranchName: string;
|
|
59
65
|
* } } LibdragonInfo
|
|
60
66
|
*/
|
|
61
67
|
|
|
@@ -189,6 +195,13 @@ const readProjectInfo = async function (optionInfo) {
|
|
|
189
195
|
|
|
190
196
|
vendorDirectory: toPosixPath(path.join('.', LIBDRAGON_SUBMODULE)),
|
|
191
197
|
vendorStrategy: DEFAULT_STRATEGY,
|
|
198
|
+
|
|
199
|
+
activeBranchName: LIBDRAGON_BRANCH,
|
|
200
|
+
|
|
201
|
+
// This is invalid at this point, but is overridden below from config file
|
|
202
|
+
// or container if it doesn't exist in the file. If a flag is provided, it
|
|
203
|
+
// will be overridden later.
|
|
204
|
+
imageName: '',
|
|
192
205
|
};
|
|
193
206
|
|
|
194
207
|
log(`Project root: ${info.root}`, true);
|
|
@@ -231,15 +244,16 @@ const readProjectInfo = async function (optionInfo) {
|
|
|
231
244
|
|
|
232
245
|
// For imageName, flag has the highest priority followed by the one read from
|
|
233
246
|
// the file and then if there is any matching container, name is read from it.
|
|
234
|
-
//
|
|
247
|
+
// Next if `--branch` flag and as last option fallback to default value.
|
|
235
248
|
// This represents the current value, so the flag should be processed later.
|
|
236
249
|
// If we were to update it here, it would be impossible to know the change
|
|
237
250
|
// with how we structure the code right now.
|
|
238
|
-
info.imageName = info.imageName
|
|
251
|
+
info.imageName = info.imageName || getImageName(info.activeBranchName);
|
|
239
252
|
|
|
240
253
|
log(`Active image name: ${info.imageName}`, true);
|
|
241
254
|
log(`Active vendor directory: ${info.vendorDirectory}`, true);
|
|
242
255
|
log(`Active vendor strategy: ${info.vendorStrategy}`, true);
|
|
256
|
+
log(`Active branch: ${info.activeBranchName}`, true);
|
|
243
257
|
|
|
244
258
|
return info;
|
|
245
259
|
};
|
package/modules/utils.js
CHANGED
|
@@ -137,29 +137,9 @@ async function runGitMaybeHost(libdragonInfo, params, options = {}) {
|
|
|
137
137
|
try {
|
|
138
138
|
const isWin = /^win/.test(process.platform);
|
|
139
139
|
|
|
140
|
-
// When using host's git the actual top level can be higher in the tree
|
|
141
|
-
// rather that at the root of the project. We need to find it to run some
|
|
142
|
-
// git operations like subtree.
|
|
143
|
-
const gitRoot = (
|
|
144
|
-
await spawnProcess(
|
|
145
|
-
'git',
|
|
146
|
-
['-C', libdragonInfo.root, 'rev-parse', '--show-toplevel'],
|
|
147
|
-
{ inheritStdin: false }
|
|
148
|
-
).catch(() => {
|
|
149
|
-
// Either this is not a git repo or the host does not have git
|
|
150
|
-
// We might also screw something up but we can live with it for now
|
|
151
|
-
return '';
|
|
152
|
-
})
|
|
153
|
-
).trim();
|
|
154
|
-
|
|
155
140
|
return await spawnProcess(
|
|
156
141
|
'git',
|
|
157
|
-
[
|
|
158
|
-
'-C',
|
|
159
|
-
libdragonInfo.root,
|
|
160
|
-
...(gitRoot ? ['--git-dir', `${gitRoot}/.git`] : []),
|
|
161
|
-
...params,
|
|
162
|
-
],
|
|
142
|
+
['-C', libdragonInfo.root, ...params],
|
|
163
143
|
// Windows git is breaking the TTY somehow - disable TTY for now
|
|
164
144
|
// We are not able to display progress for the initial clone b/c of this
|
|
165
145
|
// Enable progress otherwise.
|
|
@@ -287,7 +267,11 @@ async function initGitAndCacheContainerId(libdragonInfo) {
|
|
|
287
267
|
*/
|
|
288
268
|
async function ensureGit(info) {
|
|
289
269
|
const gitRoot = (
|
|
290
|
-
await runGitMaybeHost(info, ['rev-parse', '--show-toplevel']
|
|
270
|
+
await runGitMaybeHost(info, ['rev-parse', '--show-toplevel'], {
|
|
271
|
+
inheritStdin: false,
|
|
272
|
+
inheritStdout: false,
|
|
273
|
+
inheritStderr: false,
|
|
274
|
+
}).catch(() => {
|
|
291
275
|
// Probably host does not have git, can ignore
|
|
292
276
|
return '';
|
|
293
277
|
})
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libdragon",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.3.0",
|
|
4
4
|
"description": "This is a docker wrapper for libdragon",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
8
|
-
"npm": ">=
|
|
7
|
+
"node": ">=22",
|
|
8
|
+
"npm": ">=10"
|
|
9
9
|
},
|
|
10
10
|
"bin": {
|
|
11
11
|
"libdragon": "./index.js"
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
"libdragon": "node index.js",
|
|
17
17
|
"start": "node index.js start",
|
|
18
18
|
"stop": "node index.js stop",
|
|
19
|
-
"pack": "
|
|
20
|
-
"format": "prettier **/*.js --write",
|
|
21
|
-
"format-check": "prettier **/*.js --check",
|
|
22
|
-
"lint": "eslint --fix
|
|
23
|
-
"lint-check": "eslint
|
|
19
|
+
"pack": "node pack.mjs",
|
|
20
|
+
"format": "prettier **/*.js **/*.mjs **/*.cjs --write",
|
|
21
|
+
"format-check": "prettier **/*.js **/*.mjs **/*.cjs --check",
|
|
22
|
+
"lint": "eslint --fix modules/**/*.js *.js *.mjs *.cjs",
|
|
23
|
+
"lint-check": "eslint modules/**/*.js *.js *.mjs *.cjs",
|
|
24
24
|
"tsc": "tsc"
|
|
25
25
|
},
|
|
26
26
|
"repository": {
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"chalk": "^4.1.0",
|
|
42
42
|
"command-line-usage": "^6.1.1",
|
|
43
|
-
"lodash": "^4.17.20"
|
|
43
|
+
"lodash": "^4.17.20",
|
|
44
|
+
"zx": "^8.1.8"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@semantic-release/changelog": "^6.0.1",
|
|
@@ -50,23 +51,19 @@
|
|
|
50
51
|
"@types/lodash": "^4.14.182",
|
|
51
52
|
"commitizen": "^4.2.4",
|
|
52
53
|
"cz-conventional-changelog": "^3.3.0",
|
|
54
|
+
"esbuild": "^0.20.0",
|
|
53
55
|
"ed64": "^2.0.4",
|
|
54
|
-
"eslint": "^
|
|
56
|
+
"eslint": "^9.11.0",
|
|
55
57
|
"jest": "^29.5.0",
|
|
56
|
-
"
|
|
58
|
+
"postject": "^1.0.0-alpha.6",
|
|
57
59
|
"prettier": "^2.4.0",
|
|
60
|
+
"ref-napi": "^3.0.3",
|
|
58
61
|
"semantic-release": "^24.0.0",
|
|
59
|
-
"typescript": "^4.7.4"
|
|
62
|
+
"typescript": "^4.7.4",
|
|
63
|
+
"zx": "^7.2.2"
|
|
60
64
|
},
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"node14-win-x64",
|
|
64
|
-
"node14-linux-x64",
|
|
65
|
-
"node14-macos-x64"
|
|
66
|
-
],
|
|
67
|
-
"assets": [
|
|
68
|
-
"skeleton/**"
|
|
69
|
-
]
|
|
65
|
+
"overrides": {
|
|
66
|
+
"minimist": "1.2.6"
|
|
70
67
|
},
|
|
71
68
|
"release": {
|
|
72
69
|
"plugins": [
|
|
File without changes
|