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,314 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fsClassic = require('fs');
|
|
3
|
+
const fs = require('fs/promises');
|
|
4
|
+
|
|
5
|
+
const _ = require('lodash');
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
CONTAINER_TARGET_PATH,
|
|
9
|
+
CACHED_CONTAINER_FILE,
|
|
10
|
+
} = require('../constants');
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
fileExists,
|
|
14
|
+
log,
|
|
15
|
+
toPosixPath,
|
|
16
|
+
spawnProcess,
|
|
17
|
+
dockerExec,
|
|
18
|
+
dirExists,
|
|
19
|
+
assert,
|
|
20
|
+
CommandError,
|
|
21
|
+
} = require('../helpers');
|
|
22
|
+
|
|
23
|
+
function dockerHostUserParams(libdragonInfo) {
|
|
24
|
+
const { uid, gid } = libdragonInfo.userInfo;
|
|
25
|
+
return ['-u', `${uid >= 0 ? uid : ''}:${gid >= 0 ? gid : ''}`];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function findNPMRoot() {
|
|
29
|
+
try {
|
|
30
|
+
const root = path.resolve((await runNPM(['root'])).trim(), '..');
|
|
31
|
+
// Only report if package.json really exists. npm fallbacks to cwd
|
|
32
|
+
if (await fileExists(path.join(root, 'package.json'))) {
|
|
33
|
+
return root;
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
// User does not have and does not care about NPM if it didn't work
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const installDependencies = async (libdragonInfo) => {
|
|
42
|
+
const buildScriptPath = path.join(
|
|
43
|
+
libdragonInfo.root,
|
|
44
|
+
libdragonInfo.vendorDirectory,
|
|
45
|
+
'build.sh'
|
|
46
|
+
);
|
|
47
|
+
if (!(await fileExists(buildScriptPath))) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`build.sh not found. Make sure you have a vendored libdragon copy at ${libdragonInfo.vendorDirectory}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
libdragonInfo.showStatus && log('Installing libdragon to the container...');
|
|
54
|
+
|
|
55
|
+
await dockerExec(
|
|
56
|
+
libdragonInfo,
|
|
57
|
+
[
|
|
58
|
+
'--workdir',
|
|
59
|
+
CONTAINER_TARGET_PATH +
|
|
60
|
+
'/' +
|
|
61
|
+
toPosixPath(
|
|
62
|
+
path.relative(libdragonInfo.root, libdragonInfo.vendorDirectory)
|
|
63
|
+
),
|
|
64
|
+
...dockerHostUserParams(libdragonInfo),
|
|
65
|
+
],
|
|
66
|
+
['/bin/bash', './build.sh']
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// Install other NPM dependencies if this is an NPM project
|
|
70
|
+
const npmRoot = await findNPMRoot();
|
|
71
|
+
if (npmRoot) {
|
|
72
|
+
const packageJsonPath = path.join(npmRoot, 'package.json');
|
|
73
|
+
|
|
74
|
+
const { dependencies, devDependencies } = require(packageJsonPath);
|
|
75
|
+
|
|
76
|
+
const deps = await Promise.all(
|
|
77
|
+
Object.keys({
|
|
78
|
+
...dependencies,
|
|
79
|
+
...devDependencies,
|
|
80
|
+
})
|
|
81
|
+
.filter((dep) => dep !== 'libdragon')
|
|
82
|
+
.map(async (dep) => {
|
|
83
|
+
const npmPath = await runNPM(['ls', dep, '--parseable=true']);
|
|
84
|
+
return {
|
|
85
|
+
name: dep,
|
|
86
|
+
paths: _.uniq(npmPath.split('\n').filter((f) => f)),
|
|
87
|
+
};
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
await Promise.all(
|
|
92
|
+
deps.map(({ name, paths }) => {
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
fsClassic.access(
|
|
95
|
+
path.join(paths[0], 'Makefile'),
|
|
96
|
+
fsClassic.F_OK,
|
|
97
|
+
async (e) => {
|
|
98
|
+
if (e) {
|
|
99
|
+
// File does not exist - skip
|
|
100
|
+
resolve();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (paths.length > 1) {
|
|
105
|
+
reject(
|
|
106
|
+
new Error(
|
|
107
|
+
`Using same dependency with different versions is not supported! ${name}`
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const relativePath = toPosixPath(
|
|
115
|
+
path.relative(libdragonInfo.root, paths[0])
|
|
116
|
+
);
|
|
117
|
+
const containerPath = path.posix.join(
|
|
118
|
+
CONTAINER_TARGET_PATH,
|
|
119
|
+
relativePath
|
|
120
|
+
);
|
|
121
|
+
const makePath = path.posix.join(containerPath, 'Makefile');
|
|
122
|
+
|
|
123
|
+
await dockerExec(
|
|
124
|
+
libdragonInfo,
|
|
125
|
+
[...dockerHostUserParams(libdragonInfo)],
|
|
126
|
+
[
|
|
127
|
+
'/bin/bash',
|
|
128
|
+
'-c',
|
|
129
|
+
'[ -f ' +
|
|
130
|
+
makePath +
|
|
131
|
+
' ] && make -C ' +
|
|
132
|
+
containerPath +
|
|
133
|
+
' && make -C ' +
|
|
134
|
+
containerPath +
|
|
135
|
+
' install',
|
|
136
|
+
]
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
resolve();
|
|
140
|
+
} catch (e) {
|
|
141
|
+
reject(e);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Downloads the given docker image. Returns false if the local image is the
|
|
153
|
+
* same, new image name otherwise.
|
|
154
|
+
* @param libdragonInfo
|
|
155
|
+
* @param newImageName
|
|
156
|
+
* @returns false | string
|
|
157
|
+
*/
|
|
158
|
+
const updateImage = async (libdragonInfo, newImageName) => {
|
|
159
|
+
// Will not take too much time if already have the same
|
|
160
|
+
const download = async () => {
|
|
161
|
+
libdragonInfo.showStatus &&
|
|
162
|
+
log(`Downloading docker image: ${newImageName}`);
|
|
163
|
+
await spawnProcess(
|
|
164
|
+
'docker',
|
|
165
|
+
['pull', newImageName],
|
|
166
|
+
false,
|
|
167
|
+
libdragonInfo.showStatus
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const getDigest = async () =>
|
|
172
|
+
await spawnProcess(
|
|
173
|
+
'docker',
|
|
174
|
+
['images', '-q', '--no-trunc', newImageName],
|
|
175
|
+
false,
|
|
176
|
+
libdragonInfo.showStatus
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Attempt to compare digests if the new image name is the same
|
|
180
|
+
// Even if they are not the same tag, it is possible to have a different
|
|
181
|
+
// image but we already attempt a download in any case. It would just take
|
|
182
|
+
// less time as we already have the layers.
|
|
183
|
+
if (libdragonInfo.imageName === newImageName) {
|
|
184
|
+
const existingDigest = await getDigest();
|
|
185
|
+
await download();
|
|
186
|
+
const newDigest = await getDigest();
|
|
187
|
+
|
|
188
|
+
if (existingDigest === newDigest) {
|
|
189
|
+
libdragonInfo.showStatus && log(`Image is the same: ${newImageName}`);
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
await download();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
libdragonInfo.showStatus && log(`Image is different: ${newImageName}`);
|
|
197
|
+
return newImageName;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const destroyContainer = async (libdragonInfo) => {
|
|
201
|
+
if (libdragonInfo.containerId) {
|
|
202
|
+
await spawnProcess('docker', [
|
|
203
|
+
'container',
|
|
204
|
+
'rm',
|
|
205
|
+
libdragonInfo.containerId,
|
|
206
|
+
'--force',
|
|
207
|
+
]);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
await checkContainerAndClean({
|
|
211
|
+
...libdragonInfo,
|
|
212
|
+
containerId: undefined, // We just destroyed it
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Invokes host git with provided params. If host does not have git, falls back
|
|
218
|
+
* to the docker git, with the nix user set to the user running libdragon.
|
|
219
|
+
*/
|
|
220
|
+
async function runGitMaybeHost(libdragonInfo, params, interactive = 'full') {
|
|
221
|
+
assert(
|
|
222
|
+
libdragonInfo.vendorStrategy !== 'manual',
|
|
223
|
+
new Error('Should never run git if vendoring strategy is manual.')
|
|
224
|
+
);
|
|
225
|
+
try {
|
|
226
|
+
return await spawnProcess(
|
|
227
|
+
'git',
|
|
228
|
+
['-C', libdragonInfo.root, ...params],
|
|
229
|
+
false,
|
|
230
|
+
// Windows git is breaking the TTY somehow - disable interactive for now
|
|
231
|
+
// We are not able to display progress for the initial clone b/c of this
|
|
232
|
+
/^win/.test(process.platform) ? false : interactive
|
|
233
|
+
);
|
|
234
|
+
} catch (e) {
|
|
235
|
+
if (!(e instanceof CommandError)) {
|
|
236
|
+
return await dockerExec(
|
|
237
|
+
libdragonInfo,
|
|
238
|
+
// Use the host user when initializing git as we will need access
|
|
239
|
+
[...dockerHostUserParams(libdragonInfo)],
|
|
240
|
+
['git', ...params],
|
|
241
|
+
false,
|
|
242
|
+
interactive
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
throw e;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function runNPM(params) {
|
|
250
|
+
return spawnProcess(
|
|
251
|
+
/^win/.test(process.platform) ? 'npm.cmd' : 'npm',
|
|
252
|
+
params
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function checkContainerAndClean(libdragonInfo) {
|
|
257
|
+
const id =
|
|
258
|
+
libdragonInfo.containerId &&
|
|
259
|
+
(
|
|
260
|
+
await spawnProcess('docker', [
|
|
261
|
+
'container',
|
|
262
|
+
'ls',
|
|
263
|
+
'-qa',
|
|
264
|
+
'-f id=' + libdragonInfo.containerId,
|
|
265
|
+
])
|
|
266
|
+
).trim();
|
|
267
|
+
|
|
268
|
+
// Container does not exist, clean the id up
|
|
269
|
+
if (!id) {
|
|
270
|
+
const containerIdFile = path.join(
|
|
271
|
+
libdragonInfo.root,
|
|
272
|
+
'.git',
|
|
273
|
+
CACHED_CONTAINER_FILE
|
|
274
|
+
);
|
|
275
|
+
if (await fileExists(containerIdFile)) {
|
|
276
|
+
await fs.rm(containerIdFile);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return id ? libdragonInfo.containerId : undefined;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function checkContainerRunning(containerId) {
|
|
283
|
+
const running = (
|
|
284
|
+
await spawnProcess('docker', [
|
|
285
|
+
'container',
|
|
286
|
+
'ls',
|
|
287
|
+
'-q',
|
|
288
|
+
'-f id=' + containerId,
|
|
289
|
+
])
|
|
290
|
+
).trim();
|
|
291
|
+
return running ? containerId : undefined;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async function tryCacheContainerId(libdragonInfo) {
|
|
295
|
+
const gitFolder = path.join(libdragonInfo.root, '.git');
|
|
296
|
+
if (await dirExists(gitFolder)) {
|
|
297
|
+
await fs.writeFile(
|
|
298
|
+
path.join(gitFolder, CACHED_CONTAINER_FILE),
|
|
299
|
+
libdragonInfo.containerId
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
module.exports = {
|
|
305
|
+
installDependencies,
|
|
306
|
+
updateImage,
|
|
307
|
+
destroyContainer,
|
|
308
|
+
checkContainerRunning,
|
|
309
|
+
checkContainerAndClean,
|
|
310
|
+
dockerHostUserParams,
|
|
311
|
+
tryCacheContainerId,
|
|
312
|
+
runGitMaybeHost,
|
|
313
|
+
findNPMRoot,
|
|
314
|
+
};
|
package/modules/constants.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
DOCKER_HUB_IMAGE: 'ghcr.io/dragonminded/libdragon:latest',
|
|
3
|
-
PROJECT_NAME: process.env.npm_package_name,
|
|
4
|
-
|
|
5
3
|
LIBDRAGON_GIT: 'https://github.com/DragonMinded/libdragon',
|
|
6
4
|
LIBDRAGON_BRANCH: 'trunk',
|
|
7
5
|
LIBDRAGON_SUBMODULE: 'libdragon',
|
|
8
6
|
CONTAINER_TARGET_PATH: '/libdragon',
|
|
9
|
-
|
|
10
7
|
LIBDRAGON_PROJECT_MANIFEST: '.libdragon',
|
|
11
8
|
CACHED_CONTAINER_FILE: 'libdragon-docker-container',
|
|
12
|
-
|
|
9
|
+
CONFIG_FILE: 'config.json',
|
|
10
|
+
DEFAULT_STRATEGY: 'submodule',
|
|
11
|
+
|
|
12
|
+
// cli exit codes
|
|
13
|
+
STATUS_OK: 0,
|
|
14
|
+
STATUS_ERROR: 1,
|
|
15
|
+
STATUS_BAD_PARAM: 2,
|
|
16
|
+
|
|
17
|
+
IMAGE_FILE: 'docker-image', // deprecated
|
|
13
18
|
};
|