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.
@@ -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
+ };