libdragon 10.4.1 → 10.4.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/CHANGELOG.md +30 -0
- package/index.js +12 -1
- package/modules/actions/exec.js +8 -2
- package/modules/actions/help.js +18 -12
- package/modules/actions/index.js +1 -19
- package/modules/actions/init.js +32 -21
- package/modules/actions/install.js +27 -31
- package/modules/actions/start.js +5 -5
- package/modules/actions/stop.js +21 -0
- package/modules/actions/update-and-start.js +33 -0
- package/modules/actions/update.js +7 -9
- package/modules/actions/utils.js +18 -5
- package/modules/constants.js +1 -0
- package/modules/helpers.js +11 -0
- package/modules/project-info.js +37 -55
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## [10.4.2] - 2022-04-03
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Make sure actions depending on an `init` fail in a non-project directory to
|
|
8
|
+
keep the project state consistent. This fixes #51.
|
|
9
|
+
- `update` action now tries to update the toolchain image as well. Previously
|
|
10
|
+
this was not the case contrary to what someone would expect. Considering it won't
|
|
11
|
+
change the behaviour for non-latest images and the toolchain did not have any
|
|
12
|
+
breaking changes for a long time, this is not considered a breaking change either.
|
|
13
|
+
- `start` action was printing stuff other than the container id. It doesn't
|
|
14
|
+
anymore.
|
|
15
|
+
- Stop unnecessarily printing container id and a few messages related to updates.
|
|
16
|
+
- Fix a potential race condition that might cause unexpected failures.
|
|
17
|
+
- Correct some errors' exit codes.
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- A new exit code (`4`) to represent unexpected conditions.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Deprecated providing the image flag for `install` action by displaying a
|
|
25
|
+
warning and removing it from documentation, without changing behaviour even
|
|
26
|
+
though it is higly unlikely this feature was ever used. It mainly exists for
|
|
27
|
+
historical reasons and it wil be removed in next major release.
|
|
28
|
+
- Update documentation to warn against changing strategy is a one way operation.
|
|
29
|
+
- Update documentation to reflect `update` action changes.
|
|
30
|
+
- Minor refactors.
|
|
31
|
+
- Update submodule for local environment.
|
|
32
|
+
|
|
3
33
|
## [10.4.1] - 2022-03-23
|
|
4
34
|
|
|
5
35
|
### Fixed
|
package/index.js
CHANGED
|
@@ -8,9 +8,14 @@ const {
|
|
|
8
8
|
STATUS_OK,
|
|
9
9
|
STATUS_BAD_PARAM,
|
|
10
10
|
STATUS_ERROR,
|
|
11
|
+
STATUS_VALIDATION_ERROR,
|
|
11
12
|
} = require('./modules/constants');
|
|
12
13
|
const { globals } = require('./modules/globals');
|
|
13
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
CommandError,
|
|
16
|
+
ParameterError,
|
|
17
|
+
ValidationError,
|
|
18
|
+
} = require('./modules/helpers');
|
|
14
19
|
const { readProjectInfo, writeProjectInfo } = require('./modules/project-info');
|
|
15
20
|
|
|
16
21
|
let options = {},
|
|
@@ -127,6 +132,12 @@ readProjectInfo()
|
|
|
127
132
|
printUsage(undefined, [currentAction.name]);
|
|
128
133
|
process.exit(STATUS_BAD_PARAM);
|
|
129
134
|
}
|
|
135
|
+
|
|
136
|
+
if (e instanceof ValidationError) {
|
|
137
|
+
console.error(chalk.red(e.message));
|
|
138
|
+
process.exit(STATUS_VALIDATION_ERROR);
|
|
139
|
+
}
|
|
140
|
+
|
|
130
141
|
const userTargetedError = e instanceof CommandError && e.userCommand;
|
|
131
142
|
|
|
132
143
|
// Show additional information to user if verbose or we did a mistake
|
package/modules/actions/exec.js
CHANGED
|
@@ -3,8 +3,12 @@ const path = require('path');
|
|
|
3
3
|
const { CONTAINER_TARGET_PATH } = require('../constants');
|
|
4
4
|
const { log, dockerExec, toPosixPath } = require('../helpers');
|
|
5
5
|
|
|
6
|
-
const {
|
|
7
|
-
const {
|
|
6
|
+
const { start } = require('./start');
|
|
7
|
+
const {
|
|
8
|
+
dockerHostUserParams,
|
|
9
|
+
installDependencies,
|
|
10
|
+
mustHaveProject,
|
|
11
|
+
} = require('./utils');
|
|
8
12
|
|
|
9
13
|
function dockerRelativeWorkdir(libdragonInfo) {
|
|
10
14
|
return (
|
|
@@ -19,6 +23,8 @@ function dockerRelativeWorkdirParams(libdragonInfo) {
|
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
const exec = async (libdragonInfo, commandAndParams) => {
|
|
26
|
+
await mustHaveProject(libdragonInfo);
|
|
27
|
+
|
|
22
28
|
log(
|
|
23
29
|
`Running ${commandAndParams[0]} at ${dockerRelativeWorkdir(
|
|
24
30
|
libdragonInfo
|
package/modules/actions/help.js
CHANGED
|
@@ -39,15 +39,17 @@ const printUsage = (_, actionArr) => {
|
|
|
39
39
|
|
|
40
40
|
To disable auto-vendoring, init with \`manual\`. With \`manual\`, libdragon files are expected at the location provided by \`--directory\` flag and the user is responsible for vendoring and updating them. This will allow using any other manual vendoring method.
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
You can always switch to manual by re-running \`init\` with \`--strategy manual\`, though you will be responsible for managing the existing submodule/subtree. Also it is not possible to automatically switch back.
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
With the \`manual\` strategy, it is still recommended to have a git repository at project root such that container actions can execute faster by caching the container id inside the \`.git\` folder.\n`,
|
|
45
45
|
alias: 'v',
|
|
46
46
|
typeLabel: '<strategy>',
|
|
47
47
|
group: 'vendoring',
|
|
48
48
|
},
|
|
49
49
|
];
|
|
50
50
|
|
|
51
|
+
// TODO: move these to respective actions
|
|
52
|
+
|
|
51
53
|
const actions = {
|
|
52
54
|
help: {
|
|
53
55
|
name: 'help [action]',
|
|
@@ -68,7 +70,7 @@ const printUsage = (_, actionArr) => {
|
|
|
68
70
|
summary: 'Run the libdragon build system in the current directory.',
|
|
69
71
|
description: `Runs the libdragon build system in the current directory. It will mirror your current working directory to the container.
|
|
70
72
|
|
|
71
|
-
This action is a shortcut to the \`exec\` action under the hood.`,
|
|
73
|
+
Must be run in an initialized libdragon project. This action is a shortcut to the \`exec\` action under the hood.`,
|
|
72
74
|
},
|
|
73
75
|
exec: {
|
|
74
76
|
name: 'exec <command>',
|
|
@@ -77,33 +79,37 @@ const printUsage = (_, actionArr) => {
|
|
|
77
79
|
|
|
78
80
|
This action will first try to execute the command in the container and if the container is not accessible, it will attempt a complete \`start\` cycle.
|
|
79
81
|
|
|
80
|
-
This will properly passthrough your TTY if you have one. So by running \`libdragon exec bash\` you can start an interactive bash session with full TTY support
|
|
82
|
+
This will properly passthrough your TTY if you have one. So by running \`libdragon exec bash\` you can start an interactive bash session with full TTY support.
|
|
83
|
+
|
|
84
|
+
Must be run in an initialized libdragon project.`,
|
|
81
85
|
},
|
|
82
86
|
start: {
|
|
83
87
|
name: 'start',
|
|
84
88
|
summary: 'Start the container for current project.',
|
|
85
|
-
description:
|
|
86
|
-
|
|
89
|
+
description: `Start the container assigned to the current libdragon project. Will first attempt to start an existing container if found, followed by a new container run and installation similar to the \`install\` action. Will always print out the container id on success.
|
|
90
|
+
|
|
91
|
+
Must be run in an initialized libdragon project.`,
|
|
87
92
|
},
|
|
88
93
|
name: {
|
|
89
94
|
name: 'stop',
|
|
90
95
|
summary: 'Stop the container for current project.',
|
|
91
|
-
description:
|
|
92
|
-
|
|
96
|
+
description: `Stop the container assigned to the current libdragon project.
|
|
97
|
+
|
|
98
|
+
Must be run in an initialized libdragon project.`,
|
|
93
99
|
},
|
|
94
100
|
install: {
|
|
95
101
|
name: 'install',
|
|
96
102
|
summary: 'Vendor libdragon as is.',
|
|
97
|
-
group: ['docker'],
|
|
98
103
|
description: `Attempts to build and install everything libdragon related into the container. This includes all the tools and third parties used by libdragon except for the toolchain. If you have made changes to libdragon, you can execute this action to build everything based on your changes. Requires you to have an intact vendoring target (also see the \`--directory\` flag). If you are not working on libdragon itself, you can just use the \`update\` action instead.
|
|
99
104
|
|
|
100
|
-
|
|
105
|
+
Must be run in an initialized libdragon project. This can be useful to recover from a half-baked container.`,
|
|
101
106
|
},
|
|
102
107
|
update: {
|
|
103
108
|
name: 'update',
|
|
104
109
|
summary: 'Update libdragon and do an install.',
|
|
105
|
-
description:
|
|
106
|
-
|
|
110
|
+
description: `Will update the docker image and if you are using auto-vendoring (see \`--strategy\`), will also update the submodule/subtree from the remote branch (\`trunk\`) with a merge/squash strategy and then perform a \`libdragon install\`. You can use the \`install\` action to only update all libdragon related artifacts in the container.
|
|
111
|
+
|
|
112
|
+
Must be run in an initialized libdragon project.`,
|
|
107
113
|
group: ['docker'],
|
|
108
114
|
},
|
|
109
115
|
};
|
package/modules/actions/index.js
CHANGED
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
const { spawnProcess } = require('../helpers');
|
|
2
|
-
const { checkContainerRunning } = require('./utils');
|
|
3
|
-
|
|
4
1
|
const { fn: exec } = require('./exec');
|
|
5
2
|
|
|
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
3
|
const make = async (libdragonInfo, params) => {
|
|
18
4
|
await exec(libdragonInfo, ['make', ...params]);
|
|
19
5
|
};
|
|
@@ -21,11 +7,7 @@ const make = async (libdragonInfo, params) => {
|
|
|
21
7
|
// TODO: separate into files
|
|
22
8
|
module.exports = {
|
|
23
9
|
start: require('./start'),
|
|
24
|
-
stop:
|
|
25
|
-
name: 'stop',
|
|
26
|
-
fn: stop,
|
|
27
|
-
showStatus: false, // This will only print out the id
|
|
28
|
-
},
|
|
10
|
+
stop: require('./stop'),
|
|
29
11
|
init: require('./init'),
|
|
30
12
|
|
|
31
13
|
exec: require('./exec'),
|
package/modules/actions/init.js
CHANGED
|
@@ -4,7 +4,7 @@ const path = require('path');
|
|
|
4
4
|
const chalk = require('chalk');
|
|
5
5
|
|
|
6
6
|
const { fn: install } = require('./install');
|
|
7
|
-
const {
|
|
7
|
+
const { start } = require('./start');
|
|
8
8
|
const {
|
|
9
9
|
installDependencies,
|
|
10
10
|
tryCacheContainerId,
|
|
@@ -23,6 +23,7 @@ const {
|
|
|
23
23
|
copyDirContents,
|
|
24
24
|
CommandError,
|
|
25
25
|
ParameterError,
|
|
26
|
+
ValidationError,
|
|
26
27
|
} = require('../helpers');
|
|
27
28
|
const { setProjectInfoToSave } = require('../project-info');
|
|
28
29
|
|
|
@@ -81,23 +82,29 @@ const autoVendor = async (libdragonInfo) => {
|
|
|
81
82
|
async function init(libdragonInfo) {
|
|
82
83
|
log(`Initializing a libdragon project at ${libdragonInfo.root}`);
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
const files = await fs.readdir(libdragonInfo.root);
|
|
85
|
+
let newInfo = libdragonInfo;
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
)
|
|
87
|
+
// Validate manifest
|
|
88
|
+
const manifestPath = path.join(newInfo.root, LIBDRAGON_PROJECT_MANIFEST);
|
|
89
|
+
const manifestStats = await fs.stat(manifestPath).catch((e) => {
|
|
90
|
+
if (e.code !== 'ENOENT') throw e;
|
|
91
|
+
return false;
|
|
92
|
+
});
|
|
90
93
|
|
|
91
|
-
|
|
94
|
+
if (manifestStats && !manifestStats.isDirectory()) {
|
|
95
|
+
throw new ValidationError(
|
|
96
|
+
'There is already a `.libdragon` file and it is not a directory.'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
92
99
|
|
|
93
100
|
// Validate vendoring strategy. Do not allow a switch after successful initialization
|
|
94
101
|
if (
|
|
95
|
-
|
|
102
|
+
newInfo.haveProjectConfig &&
|
|
96
103
|
newInfo.options.VENDOR_STRAT &&
|
|
97
104
|
newInfo.options.VENDOR_STRAT !== 'manual' &&
|
|
98
105
|
newInfo.vendorStrategy !== newInfo.options.VENDOR_STRAT
|
|
99
106
|
) {
|
|
100
|
-
throw new
|
|
107
|
+
throw new ParameterError(
|
|
101
108
|
`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
109
|
);
|
|
103
110
|
}
|
|
@@ -125,11 +132,11 @@ async function init(libdragonInfo) {
|
|
|
125
132
|
});
|
|
126
133
|
}
|
|
127
134
|
|
|
128
|
-
if (
|
|
135
|
+
if (newInfo.haveProjectConfig) {
|
|
129
136
|
log(
|
|
130
137
|
`${path.join(
|
|
131
138
|
newInfo.root,
|
|
132
|
-
|
|
139
|
+
LIBDRAGON_PROJECT_MANIFEST
|
|
133
140
|
)} exists. This is already a libdragon project, starting it...`
|
|
134
141
|
);
|
|
135
142
|
if (newInfo.options.DOCKER_IMAGE) {
|
|
@@ -137,6 +144,7 @@ async function init(libdragonInfo) {
|
|
|
137
144
|
`Not changing docker image. Use the install action if you want to override the image.`
|
|
138
145
|
);
|
|
139
146
|
}
|
|
147
|
+
// TODO: we may make sure git and submodule is initialized here
|
|
140
148
|
await install(newInfo);
|
|
141
149
|
return;
|
|
142
150
|
}
|
|
@@ -144,24 +152,27 @@ async function init(libdragonInfo) {
|
|
|
144
152
|
newInfo.imageName =
|
|
145
153
|
(await updateImage(newInfo, newInfo.imageName)) || newInfo.imageName;
|
|
146
154
|
// Download image and start it
|
|
147
|
-
const containerReadyPromise = start(newInfo).then((newId) => ({
|
|
155
|
+
const containerReadyPromise = start(newInfo, true).then((newId) => ({
|
|
148
156
|
...newInfo,
|
|
149
157
|
containerId: newId,
|
|
150
158
|
}));
|
|
151
159
|
|
|
152
160
|
let vendorAndGitReadyPromise = containerReadyPromise;
|
|
153
161
|
if (newInfo.vendorStrategy !== 'manual') {
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
162
|
+
const vendorTarget = path.relative(newInfo.root, newInfo.vendorDirectory);
|
|
163
|
+
const [vendorTargetExists] = await Promise.all([
|
|
164
|
+
fs.stat(vendorTarget).catch((e) => {
|
|
165
|
+
if (e.code !== 'ENOENT') throw e;
|
|
166
|
+
return false;
|
|
167
|
+
}),
|
|
168
|
+
containerReadyPromise,
|
|
169
|
+
]);
|
|
159
170
|
|
|
160
|
-
if (
|
|
161
|
-
throw new
|
|
162
|
-
`${path.
|
|
171
|
+
if (vendorTargetExists) {
|
|
172
|
+
throw new ValidationError(
|
|
173
|
+
`${path.resolve(
|
|
163
174
|
newInfo.root,
|
|
164
|
-
|
|
175
|
+
newInfo.vendorDirectory
|
|
165
176
|
)} already exists. That is the libdragon vendoring target, please remove and retry.`
|
|
166
177
|
);
|
|
167
178
|
}
|
|
@@ -1,48 +1,44 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
|
|
3
|
+
const { installDependencies, mustHaveProject } = require('./utils');
|
|
4
|
+
const { start } = require('./start');
|
|
5
|
+
const { updateAndStart } = require('./update-and-start');
|
|
1
6
|
const { log } = require('../helpers');
|
|
2
|
-
const {
|
|
3
|
-
destroyContainer,
|
|
4
|
-
installDependencies,
|
|
5
|
-
updateImage,
|
|
6
|
-
} = require('./utils');
|
|
7
|
-
const { fn: start } = require('./start');
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Updates the image if flag is provided and install vendors onto the container.
|
|
11
10
|
* We should probably remove the image installation responsibility from this
|
|
12
|
-
* action but it might be a breaking change.
|
|
13
|
-
* compatible with additional flags.
|
|
11
|
+
* action but it might be a breaking change.
|
|
14
12
|
* @param libdragonInfo
|
|
13
|
+
* @param skipUpdate This is added to skip the update when calling this from
|
|
14
|
+
* the update action as it already does an update itself. install doing an image
|
|
15
|
+
* update is pretty much a useless operation, but let's keep it in case someone
|
|
16
|
+
* depends on it. It used to only update the image if the flag is provided and
|
|
17
|
+
* we still keep that logic but with a deprecation warning.
|
|
15
18
|
*/
|
|
16
|
-
const install = async (libdragonInfo) => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
const install = async (libdragonInfo, skipUpdate) => {
|
|
20
|
+
await mustHaveProject(libdragonInfo);
|
|
21
|
+
let updatedInfo = libdragonInfo;
|
|
19
22
|
const imageName = libdragonInfo.options.DOCKER_IMAGE;
|
|
20
|
-
// If an image is provided,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
containerId = await start({
|
|
31
|
-
...libdragonInfo,
|
|
32
|
-
imageName,
|
|
33
|
-
});
|
|
23
|
+
// If an image is provided, attempt to install
|
|
24
|
+
if (imageName && skipUpdate !== true) {
|
|
25
|
+
log(
|
|
26
|
+
chalk.yellow(
|
|
27
|
+
'Using `install` action to update the docker image is deprecated. Use the `update` action instead.'
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
updatedInfo = await updateAndStart(libdragonInfo);
|
|
34
31
|
} else {
|
|
35
32
|
// Make sure existing one is running
|
|
36
|
-
|
|
33
|
+
updatedInfo = {
|
|
34
|
+
...updatedInfo,
|
|
35
|
+
containerId: await start(libdragonInfo),
|
|
36
|
+
};
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// Re-install vendors on new image
|
|
40
40
|
// TODO: skip this if unnecessary
|
|
41
|
-
await installDependencies(
|
|
42
|
-
...libdragonInfo,
|
|
43
|
-
imageName,
|
|
44
|
-
containerId,
|
|
45
|
-
});
|
|
41
|
+
await installDependencies(updatedInfo);
|
|
46
42
|
};
|
|
47
43
|
|
|
48
44
|
module.exports = {
|
package/modules/actions/start.js
CHANGED
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
checkContainerAndClean,
|
|
9
9
|
checkContainerRunning,
|
|
10
10
|
destroyContainer,
|
|
11
|
+
mustHaveProject,
|
|
11
12
|
} = require('./utils');
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -80,14 +81,14 @@ const initContainer = async (libdragonInfo) => {
|
|
|
80
81
|
return newId;
|
|
81
82
|
};
|
|
82
83
|
|
|
83
|
-
const start = async (libdragonInfo) => {
|
|
84
|
+
const start = async (libdragonInfo, skipProjectCheck) => {
|
|
85
|
+
!skipProjectCheck && (await mustHaveProject(libdragonInfo));
|
|
84
86
|
const running =
|
|
85
87
|
libdragonInfo.containerId &&
|
|
86
88
|
(await checkContainerRunning(libdragonInfo.containerId));
|
|
87
89
|
|
|
88
90
|
if (running) {
|
|
89
91
|
log(`Container ${running} already running.`, true);
|
|
90
|
-
log(libdragonInfo.containerId);
|
|
91
92
|
return running;
|
|
92
93
|
}
|
|
93
94
|
|
|
@@ -96,19 +97,18 @@ const start = async (libdragonInfo) => {
|
|
|
96
97
|
if (!id) {
|
|
97
98
|
log(`Container does not exist, re-initializing...`, true);
|
|
98
99
|
id = await initContainer(libdragonInfo);
|
|
99
|
-
log(id);
|
|
100
100
|
return id;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
log(`Starting container: ${id}`, true);
|
|
104
104
|
await spawnProcess('docker', ['container', 'start', id]);
|
|
105
105
|
|
|
106
|
-
log(id);
|
|
107
106
|
return id;
|
|
108
107
|
};
|
|
109
108
|
|
|
110
109
|
module.exports = {
|
|
111
110
|
name: 'start',
|
|
112
|
-
fn: start,
|
|
111
|
+
fn: async (libdragonInfo) => log(await start(libdragonInfo)),
|
|
112
|
+
start,
|
|
113
113
|
showStatus: false, // This will only print out the id
|
|
114
114
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { spawnProcess } = require('../helpers');
|
|
2
|
+
|
|
3
|
+
const { checkContainerRunning, mustHaveProject } = require('./utils');
|
|
4
|
+
|
|
5
|
+
const stop = async (libdragonInfo) => {
|
|
6
|
+
await mustHaveProject(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
|
+
module.exports = {
|
|
18
|
+
name: 'stop',
|
|
19
|
+
fn: stop,
|
|
20
|
+
showStatus: false, // This will only print out the id
|
|
21
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const { log } = require('../helpers');
|
|
2
|
+
const { updateImage, destroyContainer } = require('./utils');
|
|
3
|
+
const { start } = require('./start');
|
|
4
|
+
|
|
5
|
+
async function updateAndStart(libdragonInfo) {
|
|
6
|
+
const oldImageName = libdragonInfo.imageName;
|
|
7
|
+
const imageName = libdragonInfo.options.DOCKER_IMAGE ?? oldImageName;
|
|
8
|
+
// If an image is provided, always attempt to install it
|
|
9
|
+
// See https://github.com/anacierdem/libdragon-docker/issues/47
|
|
10
|
+
if (oldImageName !== imageName) {
|
|
11
|
+
log(`Updating image from \`${oldImageName}\` to \`${imageName}\``);
|
|
12
|
+
} else {
|
|
13
|
+
log(`Updating image \`${oldImageName}\``);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Download the new image and if it is different, re-create the container
|
|
17
|
+
if (await updateImage(libdragonInfo, imageName)) {
|
|
18
|
+
await destroyContainer(libdragonInfo);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
...libdragonInfo,
|
|
23
|
+
imageName,
|
|
24
|
+
containerId: await start({
|
|
25
|
+
...libdragonInfo,
|
|
26
|
+
imageName,
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
updateAndStart,
|
|
33
|
+
};
|
|
@@ -2,17 +2,13 @@ const path = require('path');
|
|
|
2
2
|
|
|
3
3
|
const { log } = require('../helpers');
|
|
4
4
|
const { LIBDRAGON_GIT, LIBDRAGON_BRANCH } = require('../constants');
|
|
5
|
-
const { runGitMaybeHost } = require('./utils');
|
|
6
|
-
const { fn: start } = require('./start');
|
|
5
|
+
const { runGitMaybeHost, mustHaveProject } = require('./utils');
|
|
7
6
|
const { fn: install } = require('./install');
|
|
7
|
+
const { updateAndStart } = require('./update-and-start');
|
|
8
8
|
|
|
9
9
|
const update = async (libdragonInfo) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const newInfo = {
|
|
13
|
-
...libdragonInfo,
|
|
14
|
-
containerId,
|
|
15
|
-
};
|
|
10
|
+
await mustHaveProject(libdragonInfo);
|
|
11
|
+
const newInfo = await updateAndStart(libdragonInfo);
|
|
16
12
|
|
|
17
13
|
if (newInfo.vendorStrategy !== 'manual') {
|
|
18
14
|
log(`Updating ${newInfo.vendorStrategy}...`);
|
|
@@ -38,7 +34,9 @@ const update = async (libdragonInfo) => {
|
|
|
38
34
|
]);
|
|
39
35
|
}
|
|
40
36
|
|
|
41
|
-
|
|
37
|
+
// The second parameter forces it to skip the image update step as we already
|
|
38
|
+
// do that above.
|
|
39
|
+
await install(newInfo, true);
|
|
42
40
|
};
|
|
43
41
|
|
|
44
42
|
module.exports = {
|
package/modules/actions/utils.js
CHANGED
|
@@ -18,6 +18,8 @@ const {
|
|
|
18
18
|
dirExists,
|
|
19
19
|
assert,
|
|
20
20
|
CommandError,
|
|
21
|
+
ValidationError,
|
|
22
|
+
ParameterError,
|
|
21
23
|
} = require('../helpers');
|
|
22
24
|
|
|
23
25
|
function dockerHostUserParams(libdragonInfo) {
|
|
@@ -45,7 +47,7 @@ const installDependencies = async (libdragonInfo) => {
|
|
|
45
47
|
'build.sh'
|
|
46
48
|
);
|
|
47
49
|
if (!(await fileExists(buildScriptPath))) {
|
|
48
|
-
throw new
|
|
50
|
+
throw new ValidationError(
|
|
49
51
|
`build.sh not found. Make sure you have a vendored libdragon copy at ${libdragonInfo.vendorDirectory}`
|
|
50
52
|
);
|
|
51
53
|
}
|
|
@@ -103,7 +105,7 @@ const installDependencies = async (libdragonInfo) => {
|
|
|
103
105
|
|
|
104
106
|
if (paths.length > 1) {
|
|
105
107
|
reject(
|
|
106
|
-
new
|
|
108
|
+
new ValidationError(
|
|
107
109
|
`Using same dependency with different versions is not supported! ${name}`
|
|
108
110
|
)
|
|
109
111
|
);
|
|
@@ -173,7 +175,7 @@ const updateImage = async (libdragonInfo, newImageName) => {
|
|
|
173
175
|
'docker',
|
|
174
176
|
['images', '-q', '--no-trunc', newImageName],
|
|
175
177
|
false,
|
|
176
|
-
|
|
178
|
+
false
|
|
177
179
|
);
|
|
178
180
|
|
|
179
181
|
// Attempt to compare digests if the new image name is the same
|
|
@@ -186,14 +188,15 @@ const updateImage = async (libdragonInfo, newImageName) => {
|
|
|
186
188
|
const newDigest = await getDigest();
|
|
187
189
|
|
|
188
190
|
if (existingDigest === newDigest) {
|
|
189
|
-
libdragonInfo.showStatus &&
|
|
191
|
+
libdragonInfo.showStatus &&
|
|
192
|
+
log(`Image is the same: ${newImageName}`, true);
|
|
190
193
|
return false;
|
|
191
194
|
}
|
|
192
195
|
} else {
|
|
193
196
|
await download();
|
|
194
197
|
}
|
|
195
198
|
|
|
196
|
-
libdragonInfo.showStatus && log(`Image is different: ${newImageName}
|
|
199
|
+
libdragonInfo.showStatus && log(`Image is different: ${newImageName}`, true);
|
|
197
200
|
return newImageName;
|
|
198
201
|
};
|
|
199
202
|
|
|
@@ -301,6 +304,15 @@ async function tryCacheContainerId(libdragonInfo) {
|
|
|
301
304
|
}
|
|
302
305
|
}
|
|
303
306
|
|
|
307
|
+
// Throws if the project was not initialized for the current libdragonInfo
|
|
308
|
+
async function mustHaveProject(libdragonInfo) {
|
|
309
|
+
if (!libdragonInfo.haveProjectConfig) {
|
|
310
|
+
throw new ParameterError(
|
|
311
|
+
'This is not a libdragon project. Initialize with `libdragon init` first.'
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
304
316
|
module.exports = {
|
|
305
317
|
installDependencies,
|
|
306
318
|
updateImage,
|
|
@@ -311,4 +323,5 @@ module.exports = {
|
|
|
311
323
|
tryCacheContainerId,
|
|
312
324
|
runGitMaybeHost,
|
|
313
325
|
findNPMRoot,
|
|
326
|
+
mustHaveProject,
|
|
314
327
|
};
|
package/modules/constants.js
CHANGED
package/modules/helpers.js
CHANGED
|
@@ -6,6 +6,7 @@ const { spawn } = require('child_process');
|
|
|
6
6
|
|
|
7
7
|
const { globals } = require('./globals');
|
|
8
8
|
|
|
9
|
+
// An error caused by a command explicitly run by the user
|
|
9
10
|
class CommandError extends Error {
|
|
10
11
|
constructor(message, { code, out, userCommand }) {
|
|
11
12
|
super(message);
|
|
@@ -15,12 +16,20 @@ class CommandError extends Error {
|
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
// The user provided an unexpected input
|
|
18
20
|
class ParameterError extends Error {
|
|
19
21
|
constructor(message) {
|
|
20
22
|
super(message);
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
// Something was not as expected to continue the operation
|
|
27
|
+
class ValidationError extends Error {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
24
33
|
async function fileExists(path) {
|
|
25
34
|
return fs
|
|
26
35
|
.stat(path)
|
|
@@ -210,6 +219,7 @@ function assert(condition, error) {
|
|
|
210
219
|
}
|
|
211
220
|
}
|
|
212
221
|
|
|
222
|
+
// TODO: we can handle showStatus here
|
|
213
223
|
function log(text, verboseOnly = false) {
|
|
214
224
|
if (!verboseOnly) {
|
|
215
225
|
// eslint-disable-next-line no-console
|
|
@@ -234,4 +244,5 @@ module.exports = {
|
|
|
234
244
|
copyDirContents,
|
|
235
245
|
CommandError,
|
|
236
246
|
ParameterError,
|
|
247
|
+
ValidationError,
|
|
237
248
|
};
|
package/modules/project-info.js
CHANGED
|
@@ -20,13 +20,7 @@ const {
|
|
|
20
20
|
CONTAINER_TARGET_PATH,
|
|
21
21
|
} = require('./constants');
|
|
22
22
|
|
|
23
|
-
const {
|
|
24
|
-
fileExists,
|
|
25
|
-
log,
|
|
26
|
-
spawnProcess,
|
|
27
|
-
dirExists,
|
|
28
|
-
toPosixPath,
|
|
29
|
-
} = require('./helpers');
|
|
23
|
+
const { fileExists, log, spawnProcess, toPosixPath } = require('./helpers');
|
|
30
24
|
|
|
31
25
|
async function findContainerId(libdragonInfo) {
|
|
32
26
|
const idFile = path.join(libdragonInfo.root, '.git', CACHED_CONTAINER_FILE);
|
|
@@ -67,8 +61,8 @@ async function findContainerId(libdragonInfo) {
|
|
|
67
61
|
}
|
|
68
62
|
|
|
69
63
|
async function findLibdragonRoot(start = '.') {
|
|
70
|
-
const manifest = path.join(start, LIBDRAGON_PROJECT_MANIFEST);
|
|
71
|
-
if (await
|
|
64
|
+
const manifest = path.join(start, LIBDRAGON_PROJECT_MANIFEST, CONFIG_FILE);
|
|
65
|
+
if (await fileExists(manifest)) {
|
|
72
66
|
return path.resolve(start);
|
|
73
67
|
} else {
|
|
74
68
|
const parent = path.resolve(start, '..');
|
|
@@ -89,42 +83,16 @@ async function findGitRoot() {
|
|
|
89
83
|
}
|
|
90
84
|
}
|
|
91
85
|
|
|
92
|
-
/**
|
|
93
|
-
* Creates the manifest folder if it does not exist. Will return true if
|
|
94
|
-
* created, false otherwise.
|
|
95
|
-
*/
|
|
96
|
-
async function createManifestIfNotExist(libdragonInfo) {
|
|
97
|
-
const manifestPath = path.join(
|
|
98
|
-
libdragonInfo.root,
|
|
99
|
-
LIBDRAGON_PROJECT_MANIFEST
|
|
100
|
-
);
|
|
101
|
-
const manifestExists = await fs.stat(manifestPath).catch((e) => {
|
|
102
|
-
if (e.code !== 'ENOENT') throw e;
|
|
103
|
-
return false;
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
if (manifestExists && !manifestExists.isDirectory()) {
|
|
107
|
-
throw new Error(
|
|
108
|
-
'There is already a `.libdragon` file and it is not a directory.'
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (!manifestExists) {
|
|
113
|
-
log(
|
|
114
|
-
`Creating libdragon project configuration at \`${libdragonInfo.root}\`.`
|
|
115
|
-
);
|
|
116
|
-
await fs.mkdir(manifestPath);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
86
|
async function readProjectInfo() {
|
|
87
|
+
const projectRoot = await findLibdragonRoot();
|
|
88
|
+
|
|
121
89
|
let info = {
|
|
122
|
-
root:
|
|
123
|
-
(await findLibdragonRoot()) ??
|
|
124
|
-
(await findNPMRoot()) ??
|
|
125
|
-
(await findGitRoot()),
|
|
90
|
+
root: projectRoot ?? (await findNPMRoot()) ?? (await findGitRoot()),
|
|
126
91
|
userInfo: os.userInfo(),
|
|
127
92
|
|
|
93
|
+
// Use this to discriminate if there is a project when the command is run
|
|
94
|
+
haveProjectConfig: !!projectRoot,
|
|
95
|
+
|
|
128
96
|
// Set the defaults immediately, these should be present at all times even
|
|
129
97
|
// if we are migrating from the old config because they did not exist before
|
|
130
98
|
imageName: DOCKER_HUB_IMAGE,
|
|
@@ -200,27 +168,41 @@ let projectInfoToWrite = {};
|
|
|
200
168
|
* Updates project info to be written. The provided keys are overwritten without
|
|
201
169
|
* changing the existing values. When the process exists successfully these will
|
|
202
170
|
* get written to the configuration file. Echoes back the given info.
|
|
203
|
-
* @param
|
|
171
|
+
* @param info This is only the base info without action properties like showStatus
|
|
172
|
+
* fn and command line options
|
|
204
173
|
*/
|
|
205
|
-
function setProjectInfoToSave(
|
|
206
|
-
projectInfoToWrite = { ...projectInfoToWrite, ...
|
|
207
|
-
return
|
|
174
|
+
function setProjectInfoToSave(info) {
|
|
175
|
+
projectInfoToWrite = { ...projectInfoToWrite, ...info };
|
|
176
|
+
return info;
|
|
208
177
|
}
|
|
209
178
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
179
|
+
/**
|
|
180
|
+
* @param info This is only the base info without action properties like showStatus
|
|
181
|
+
* fn and command line options
|
|
182
|
+
*/
|
|
183
|
+
async function writeProjectInfo(info = projectInfoToWrite) {
|
|
184
|
+
// Do not log anything here as it may litter the output being always run on exit
|
|
185
|
+
|
|
186
|
+
const projectPath = path.join(info.root, LIBDRAGON_PROJECT_MANIFEST);
|
|
187
|
+
|
|
188
|
+
const pathExists = await fs.stat(projectPath).catch((e) => {
|
|
189
|
+
if (e.code !== 'ENOENT') throw e;
|
|
190
|
+
return false;
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (!pathExists) {
|
|
194
|
+
log(`Creating libdragon project configuration at \`${info.root}\`.`, true);
|
|
195
|
+
await fs.mkdir(projectPath);
|
|
196
|
+
}
|
|
197
|
+
|
|
216
198
|
await fs.writeFile(
|
|
217
|
-
path.join(
|
|
199
|
+
path.join(projectPath, CONFIG_FILE),
|
|
218
200
|
JSON.stringify(
|
|
219
201
|
{
|
|
220
|
-
imageName:
|
|
202
|
+
imageName: info.imageName,
|
|
221
203
|
// Always save this in posix format
|
|
222
|
-
vendorDirectory: toPosixPath(
|
|
223
|
-
vendorStrategy:
|
|
204
|
+
vendorDirectory: toPosixPath(info.vendorDirectory),
|
|
205
|
+
vendorStrategy: info.vendorStrategy,
|
|
224
206
|
},
|
|
225
207
|
null,
|
|
226
208
|
' '
|