libdragon 10.2.1 → 10.4.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/CHANGELOG.md +91 -13
- package/README.md +3 -51
- package/index.js +78 -33
- package/modules/actions/exec.js +91 -0
- package/modules/actions/help.js +157 -0
- package/modules/actions/index.js +43 -0
- package/modules/actions/init.js +190 -0
- package/modules/actions/install.js +52 -0
- package/modules/actions/start.js +114 -0
- package/modules/actions/update.js +48 -0
- package/modules/actions/utils.js +314 -0
- package/modules/constants.js +9 -4
- package/modules/globals.js +5 -0
- package/modules/helpers.js +82 -276
- package/modules/project-info.js +232 -0
- package/package.json +5 -4
- package/modules/actions.js +0 -567
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const { spawnProcess } = require('../helpers');
|
|
2
|
+
const { checkContainerRunning } = require('./utils');
|
|
3
|
+
|
|
4
|
+
const { fn: exec } = require('./exec');
|
|
5
|
+
|
|
6
|
+
const stop = async (libdragonInfo) => {
|
|
7
|
+
const running =
|
|
8
|
+
libdragonInfo.containerId &&
|
|
9
|
+
(await checkContainerRunning(libdragonInfo.containerId));
|
|
10
|
+
if (!running) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
await spawnProcess('docker', ['container', 'stop', running]);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const make = async (libdragonInfo, params) => {
|
|
18
|
+
await exec(libdragonInfo, ['make', ...params]);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// TODO: separate into files
|
|
22
|
+
module.exports = {
|
|
23
|
+
start: require('./start'),
|
|
24
|
+
stop: {
|
|
25
|
+
name: 'stop',
|
|
26
|
+
fn: stop,
|
|
27
|
+
showStatus: false, // This will only print out the id
|
|
28
|
+
},
|
|
29
|
+
init: require('./init'),
|
|
30
|
+
|
|
31
|
+
exec: require('./exec'),
|
|
32
|
+
make: {
|
|
33
|
+
name: 'make',
|
|
34
|
+
fn: make,
|
|
35
|
+
forwardsRestParams: true,
|
|
36
|
+
showStatus: true,
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
install: require('./install'),
|
|
40
|
+
update: require('./update'),
|
|
41
|
+
|
|
42
|
+
help: require('./help'),
|
|
43
|
+
};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
const fs = require('fs/promises');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
|
|
6
|
+
const { fn: install } = require('./install');
|
|
7
|
+
const { fn: start } = require('./start');
|
|
8
|
+
const {
|
|
9
|
+
installDependencies,
|
|
10
|
+
tryCacheContainerId,
|
|
11
|
+
updateImage,
|
|
12
|
+
runGitMaybeHost,
|
|
13
|
+
} = require('./utils');
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
LIBDRAGON_PROJECT_MANIFEST,
|
|
17
|
+
LIBDRAGON_SUBMODULE,
|
|
18
|
+
LIBDRAGON_BRANCH,
|
|
19
|
+
LIBDRAGON_GIT,
|
|
20
|
+
} = require('../constants');
|
|
21
|
+
const {
|
|
22
|
+
log,
|
|
23
|
+
copyDirContents,
|
|
24
|
+
CommandError,
|
|
25
|
+
ParameterError,
|
|
26
|
+
} = require('../helpers');
|
|
27
|
+
const { setProjectInfoToSave } = require('../project-info');
|
|
28
|
+
|
|
29
|
+
const autoVendor = async (libdragonInfo) => {
|
|
30
|
+
await runGitMaybeHost(libdragonInfo, ['init']);
|
|
31
|
+
|
|
32
|
+
if (libdragonInfo.vendorStrategy === 'submodule') {
|
|
33
|
+
await runGitMaybeHost(libdragonInfo, [
|
|
34
|
+
'submodule',
|
|
35
|
+
'add',
|
|
36
|
+
'--force',
|
|
37
|
+
'--name',
|
|
38
|
+
LIBDRAGON_SUBMODULE,
|
|
39
|
+
'--branch',
|
|
40
|
+
LIBDRAGON_BRANCH,
|
|
41
|
+
LIBDRAGON_GIT,
|
|
42
|
+
libdragonInfo.vendorDirectory,
|
|
43
|
+
]);
|
|
44
|
+
} else if (libdragonInfo.vendorStrategy === 'subtree') {
|
|
45
|
+
// Create a commit if it does not exist. This is required for subtree.
|
|
46
|
+
try {
|
|
47
|
+
await runGitMaybeHost(libdragonInfo, ['rev-parse', 'HEAD']);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
if (!(e instanceof CommandError)) throw e;
|
|
50
|
+
|
|
51
|
+
// This will throw if git user name/email is not set up. Let's not assume
|
|
52
|
+
// anything for now. This means subtree is not supported for someone without
|
|
53
|
+
// git on the host machine.
|
|
54
|
+
await runGitMaybeHost(libdragonInfo, [
|
|
55
|
+
'commit',
|
|
56
|
+
'--allow-empty',
|
|
57
|
+
'-n',
|
|
58
|
+
'-m',
|
|
59
|
+
'Initial commit.',
|
|
60
|
+
]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
await runGitMaybeHost(libdragonInfo, [
|
|
64
|
+
'subtree',
|
|
65
|
+
'add',
|
|
66
|
+
'--prefix',
|
|
67
|
+
path.relative(libdragonInfo.root, libdragonInfo.vendorDirectory),
|
|
68
|
+
LIBDRAGON_GIT,
|
|
69
|
+
LIBDRAGON_BRANCH,
|
|
70
|
+
'--squash',
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return libdragonInfo;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Initialize a new libdragon project in current working directory
|
|
79
|
+
* Also downloads the image
|
|
80
|
+
*/
|
|
81
|
+
async function init(libdragonInfo) {
|
|
82
|
+
log(`Initializing a libdragon project at ${libdragonInfo.root}`);
|
|
83
|
+
|
|
84
|
+
// TODO: use exists instead & check if it is a directory
|
|
85
|
+
const files = await fs.readdir(libdragonInfo.root);
|
|
86
|
+
|
|
87
|
+
const manifestFile = files.find(
|
|
88
|
+
(name) => name === LIBDRAGON_PROJECT_MANIFEST
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
let newInfo = libdragonInfo;
|
|
92
|
+
|
|
93
|
+
// Validate vendoring strategy. Do not allow a switch after successful initialization
|
|
94
|
+
if (
|
|
95
|
+
manifestFile &&
|
|
96
|
+
newInfo.options.VENDOR_STRAT &&
|
|
97
|
+
newInfo.options.VENDOR_STRAT !== 'manual' &&
|
|
98
|
+
newInfo.vendorStrategy !== newInfo.options.VENDOR_STRAT
|
|
99
|
+
) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Requested strategy switch: ${newInfo.vendorStrategy} -> ${newInfo.options.VENDOR_STRAT} It is not possible to switch vendoring strategy after initializing a project. You can always switch to manual and handle libdragon yourself.`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Update the strategy information for the project if the flag is provided
|
|
106
|
+
if (newInfo.options.VENDOR_STRAT) {
|
|
107
|
+
newInfo = setProjectInfoToSave({
|
|
108
|
+
...newInfo,
|
|
109
|
+
vendorStrategy: newInfo.options.VENDOR_STRAT,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Update the directory information for the project if the flag is provided
|
|
114
|
+
if (newInfo.options.VENDOR_DIR) {
|
|
115
|
+
// Validate vendoring path
|
|
116
|
+
if (path.isAbsolute(newInfo.options.VENDOR_DIR)) {
|
|
117
|
+
throw new ParameterError(
|
|
118
|
+
'`--directory` must be project-relative as it will be mounted to the docker container.'
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
newInfo = setProjectInfoToSave({
|
|
123
|
+
...newInfo,
|
|
124
|
+
vendorDirectory: newInfo.options.VENDOR_DIR,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (manifestFile) {
|
|
129
|
+
log(
|
|
130
|
+
`${path.join(
|
|
131
|
+
newInfo.root,
|
|
132
|
+
manifestFile
|
|
133
|
+
)} exists. This is already a libdragon project, starting it...`
|
|
134
|
+
);
|
|
135
|
+
if (newInfo.options.DOCKER_IMAGE) {
|
|
136
|
+
log(
|
|
137
|
+
`Not changing docker image. Use the install action if you want to override the image.`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
await install(newInfo);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
newInfo.imageName =
|
|
145
|
+
(await updateImage(newInfo, newInfo.imageName)) || newInfo.imageName;
|
|
146
|
+
// Download image and start it
|
|
147
|
+
const containerReadyPromise = start(newInfo).then((newId) => ({
|
|
148
|
+
...newInfo,
|
|
149
|
+
containerId: newId,
|
|
150
|
+
}));
|
|
151
|
+
|
|
152
|
+
let vendorAndGitReadyPromise = containerReadyPromise;
|
|
153
|
+
if (newInfo.vendorStrategy !== 'manual') {
|
|
154
|
+
const libdragonFile = files.find((name) =>
|
|
155
|
+
name.match(
|
|
156
|
+
new RegExp(`^${path.relative(newInfo.root, newInfo.vendorDirectory)}$`)
|
|
157
|
+
)
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
if (libdragonFile) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`${path.join(
|
|
163
|
+
newInfo.root,
|
|
164
|
+
libdragonFile
|
|
165
|
+
)} already exists. That is the libdragon vendoring target, please remove and retry.`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
vendorAndGitReadyPromise = containerReadyPromise.then(autoVendor);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
log(`Preparing project files...`);
|
|
173
|
+
const skeletonFolder = path.join(__dirname, '../../skeleton');
|
|
174
|
+
|
|
175
|
+
await Promise.all([
|
|
176
|
+
// We have created a new container, save the new info
|
|
177
|
+
vendorAndGitReadyPromise.then(tryCacheContainerId),
|
|
178
|
+
vendorAndGitReadyPromise.then(installDependencies),
|
|
179
|
+
// node copy functions does not work with pkg
|
|
180
|
+
copyDirContents(skeletonFolder, newInfo.root),
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
log(chalk.green(`libdragon ready at \`${newInfo.root}\`.`));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = {
|
|
187
|
+
name: 'init',
|
|
188
|
+
fn: init,
|
|
189
|
+
showStatus: true,
|
|
190
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const { log } = require('../helpers');
|
|
2
|
+
const {
|
|
3
|
+
destroyContainer,
|
|
4
|
+
installDependencies,
|
|
5
|
+
updateImage,
|
|
6
|
+
} = require('./utils');
|
|
7
|
+
const { fn: start } = require('./start');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Updates the image if flag is provided and install vendors onto the container.
|
|
11
|
+
* We should probably remove the image installation responsibility from this
|
|
12
|
+
* action but it might be a breaking change. Maybe we can keep it backward
|
|
13
|
+
* compatible with additional flags.
|
|
14
|
+
* @param libdragonInfo
|
|
15
|
+
*/
|
|
16
|
+
const install = async (libdragonInfo) => {
|
|
17
|
+
let containerId;
|
|
18
|
+
const oldImageName = libdragonInfo.imageName;
|
|
19
|
+
const imageName = libdragonInfo.options.DOCKER_IMAGE;
|
|
20
|
+
// If an image is provided, always attempt to install it
|
|
21
|
+
// See https://github.com/anacierdem/libdragon-docker/issues/47
|
|
22
|
+
if (imageName) {
|
|
23
|
+
log(`Changing image from \`${oldImageName}\` to \`${imageName}\``);
|
|
24
|
+
|
|
25
|
+
// Download the new image and if it is different, re-create the container
|
|
26
|
+
if (await updateImage(libdragonInfo, imageName)) {
|
|
27
|
+
await destroyContainer(libdragonInfo);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
containerId = await start({
|
|
31
|
+
...libdragonInfo,
|
|
32
|
+
imageName,
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
// Make sure existing one is running
|
|
36
|
+
containerId = await start(libdragonInfo);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Re-install vendors on new image
|
|
40
|
+
// TODO: skip this if unnecessary
|
|
41
|
+
await installDependencies({
|
|
42
|
+
...libdragonInfo,
|
|
43
|
+
imageName,
|
|
44
|
+
containerId,
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
name: 'install',
|
|
50
|
+
fn: install,
|
|
51
|
+
showStatus: true,
|
|
52
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
|
|
3
|
+
const { CONTAINER_TARGET_PATH } = require('../constants');
|
|
4
|
+
const { spawnProcess, log, dockerExec } = require('../helpers');
|
|
5
|
+
const { setProjectInfoToSave } = require('../project-info');
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
checkContainerAndClean,
|
|
9
|
+
checkContainerRunning,
|
|
10
|
+
destroyContainer,
|
|
11
|
+
} = require('./utils');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create a new container
|
|
15
|
+
*/
|
|
16
|
+
const initContainer = async (libdragonInfo) => {
|
|
17
|
+
let newId;
|
|
18
|
+
try {
|
|
19
|
+
// Create a new container
|
|
20
|
+
libdragonInfo.showStatus && log('Creating new container...');
|
|
21
|
+
newId = (
|
|
22
|
+
await spawnProcess('docker', [
|
|
23
|
+
'run',
|
|
24
|
+
'-d', // Detached
|
|
25
|
+
'--mount',
|
|
26
|
+
'type=bind,source=' +
|
|
27
|
+
libdragonInfo.root +
|
|
28
|
+
',target=' +
|
|
29
|
+
CONTAINER_TARGET_PATH, // Mount files
|
|
30
|
+
'-w=' + CONTAINER_TARGET_PATH, // Set working directory
|
|
31
|
+
libdragonInfo.imageName,
|
|
32
|
+
'tail',
|
|
33
|
+
'-f',
|
|
34
|
+
'/dev/null',
|
|
35
|
+
])
|
|
36
|
+
).trim();
|
|
37
|
+
|
|
38
|
+
const newInfo = {
|
|
39
|
+
...libdragonInfo,
|
|
40
|
+
containerId: newId,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// chown the installation folder once on init
|
|
44
|
+
const { uid, gid } = libdragonInfo.userInfo;
|
|
45
|
+
await dockerExec(newInfo, [
|
|
46
|
+
'chown',
|
|
47
|
+
'-R',
|
|
48
|
+
`${uid >= 0 ? uid : ''}:${gid >= 0 ? gid : ''}`,
|
|
49
|
+
'/n64_toolchain',
|
|
50
|
+
]);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// Dispose the invalid container, clean and exit
|
|
53
|
+
await destroyContainer({
|
|
54
|
+
...libdragonInfo,
|
|
55
|
+
containerId: newId,
|
|
56
|
+
});
|
|
57
|
+
log(
|
|
58
|
+
chalk.red(
|
|
59
|
+
'We were unable to initialize libdragon. Done cleanup. Check following logs for the actual error.'
|
|
60
|
+
)
|
|
61
|
+
);
|
|
62
|
+
throw e;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const name = await spawnProcess('docker', [
|
|
66
|
+
'container',
|
|
67
|
+
'inspect',
|
|
68
|
+
newId,
|
|
69
|
+
'--format',
|
|
70
|
+
'{{.Name}}',
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
libdragonInfo.showStatus &&
|
|
74
|
+
log(
|
|
75
|
+
chalk.green(`Successfully initialized docker container: ${name.trim()}`)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Schedule an update to write image name
|
|
79
|
+
setProjectInfoToSave(libdragonInfo);
|
|
80
|
+
return newId;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const start = async (libdragonInfo) => {
|
|
84
|
+
const running =
|
|
85
|
+
libdragonInfo.containerId &&
|
|
86
|
+
(await checkContainerRunning(libdragonInfo.containerId));
|
|
87
|
+
|
|
88
|
+
if (running) {
|
|
89
|
+
log(`Container ${running} already running.`, true);
|
|
90
|
+
log(libdragonInfo.containerId);
|
|
91
|
+
return running;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let id = await checkContainerAndClean(libdragonInfo);
|
|
95
|
+
|
|
96
|
+
if (!id) {
|
|
97
|
+
log(`Container does not exist, re-initializing...`, true);
|
|
98
|
+
id = await initContainer(libdragonInfo);
|
|
99
|
+
log(id);
|
|
100
|
+
return id;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
log(`Starting container: ${id}`, true);
|
|
104
|
+
await spawnProcess('docker', ['container', 'start', id]);
|
|
105
|
+
|
|
106
|
+
log(id);
|
|
107
|
+
return id;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
module.exports = {
|
|
111
|
+
name: 'start',
|
|
112
|
+
fn: start,
|
|
113
|
+
showStatus: false, // This will only print out the id
|
|
114
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
const { log } = require('../helpers');
|
|
4
|
+
const { LIBDRAGON_GIT, LIBDRAGON_BRANCH } = require('../constants');
|
|
5
|
+
const { runGitMaybeHost } = require('./utils');
|
|
6
|
+
const { fn: start } = require('./start');
|
|
7
|
+
const { fn: install } = require('./install');
|
|
8
|
+
|
|
9
|
+
const update = async (libdragonInfo) => {
|
|
10
|
+
const containerId = await start(libdragonInfo);
|
|
11
|
+
|
|
12
|
+
const newInfo = {
|
|
13
|
+
...libdragonInfo,
|
|
14
|
+
containerId,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
if (newInfo.vendorStrategy !== 'manual') {
|
|
18
|
+
log(`Updating ${newInfo.vendorStrategy}...`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (newInfo.vendorStrategy === 'submodule') {
|
|
22
|
+
await runGitMaybeHost(newInfo, [
|
|
23
|
+
'submodule',
|
|
24
|
+
'update',
|
|
25
|
+
'--remote',
|
|
26
|
+
'--merge',
|
|
27
|
+
newInfo.vendorDirectory,
|
|
28
|
+
]);
|
|
29
|
+
} else if (newInfo.vendorStrategy === 'subtree') {
|
|
30
|
+
await runGitMaybeHost(newInfo, [
|
|
31
|
+
'subtree',
|
|
32
|
+
'pull',
|
|
33
|
+
'--prefix',
|
|
34
|
+
path.relative(libdragonInfo.root, libdragonInfo.vendorDirectory),
|
|
35
|
+
LIBDRAGON_GIT,
|
|
36
|
+
LIBDRAGON_BRANCH,
|
|
37
|
+
'--squash',
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await install(newInfo);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
name: 'update',
|
|
46
|
+
fn: update,
|
|
47
|
+
showStatus: true,
|
|
48
|
+
};
|