@enspirit/emb 0.0.4 → 0.0.6
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 +2 -19
- package/dist/src/cli/commands/config/print.js +1 -1
- package/dist/src/cli/commands/tasks/run.d.ts +0 -2
- package/dist/src/cli/commands/tasks/run.js +10 -59
- package/dist/src/config/convert.d.ts +2 -3
- package/dist/src/config/convert.js +2 -7
- package/dist/src/config/schema.d.ts +28 -28
- package/dist/src/config/schema.json +45 -50
- package/dist/src/config/types.d.ts +2 -2
- package/dist/src/config/validation.d.ts +2 -0
- package/dist/src/config/validation.js +26 -1
- package/dist/src/docker/operations/containers/ExecContainerOperation.d.ts +22 -0
- package/dist/src/docker/operations/containers/ExecContainerOperation.js +78 -0
- package/dist/src/docker/operations/containers/index.d.ts +1 -0
- package/dist/src/docker/operations/containers/index.js +1 -0
- package/dist/src/monorepo/config.d.ts +2 -1
- package/dist/src/monorepo/config.js +8 -2
- package/dist/src/monorepo/operations/components/GetComponentContainerOperation.d.ts +6 -0
- package/dist/src/monorepo/operations/components/GetComponentContainerOperation.js +21 -0
- package/dist/src/monorepo/operations/components/index.d.ts +1 -0
- package/dist/src/monorepo/operations/components/index.js +1 -0
- package/dist/src/monorepo/operations/index.d.ts +2 -0
- package/dist/src/monorepo/operations/index.js +2 -0
- package/dist/src/monorepo/operations/shell/ExecuteLocalCommandOperation.d.ts +18 -0
- package/dist/src/monorepo/operations/shell/ExecuteLocalCommandOperation.js +35 -0
- package/dist/src/monorepo/operations/shell/index.d.ts +1 -0
- package/dist/src/monorepo/operations/shell/index.js +1 -0
- package/dist/src/monorepo/operations/tasks/RunTaskOperation.d.ts +18 -0
- package/dist/src/monorepo/operations/tasks/RunTaskOperation.js +50 -0
- package/dist/src/monorepo/operations/tasks/index.d.ts +1 -0
- package/dist/src/monorepo/operations/tasks/index.js +1 -0
- package/dist/src/monorepo/plugins/ComponentDiscoverPlugin.d.ts +15 -0
- package/dist/src/monorepo/plugins/{ComponentsDiscover.js → ComponentDiscoverPlugin.js} +13 -1
- package/dist/src/monorepo/plugins/EmbfileLoaderPlugin.d.ts +14 -0
- package/dist/src/monorepo/plugins/EmbfileLoaderPlugin.js +33 -0
- package/dist/src/monorepo/plugins/index.d.ts +3 -2
- package/dist/src/monorepo/plugins/index.js +5 -2
- package/dist/src/operations/abstract/AbstractOperation.d.ts +1 -1
- package/dist/src/utils/deepMergeArray.d.ts +3 -0
- package/dist/src/utils/deepMergeArray.js +1 -0
- package/oclif.manifest.json +33 -67
- package/package.json +2 -2
- package/dist/src/cli/commands/run/index.d.ts +0 -10
- package/dist/src/cli/commands/run/index.js +0 -49
- package/dist/src/executors/docker.d.ts +0 -6
- package/dist/src/executors/docker.js +0 -14
- package/dist/src/executors/index.d.ts +0 -6
- package/dist/src/executors/index.js +0 -7
- package/dist/src/executors/shell.d.ts +0 -2
- package/dist/src/executors/shell.js +0 -14
- package/dist/src/executors/types.d.ts +0 -8
- package/dist/src/executors/types.js +0 -1
- package/dist/src/monorepo/plugins/ComponentsDiscover.d.ts +0 -6
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
emb (Enspirit's Monorepo Builder)
|
|
2
2
|
=================
|
|
3
3
|
|
|
4
|
-
A CLI to help on Enspirit monorepos. This aims at replacing our aging [Makefile for monorepos](https://github.com/enspirit/makefile-for-monorepos
|
|
4
|
+
A CLI to help on Enspirit monorepos. This aims at replacing our aging [Makefile for monorepos](https://github.com/enspirit/makefile-for-monorepos)
|
|
5
5
|
|
|
6
6
|
<!-- toc -->
|
|
7
7
|
* [Usage](#usage)
|
|
@@ -14,7 +14,7 @@ $ npm install -g @enspirit/emb
|
|
|
14
14
|
$ emb COMMAND
|
|
15
15
|
running command...
|
|
16
16
|
$ emb (--version)
|
|
17
|
-
@enspirit/emb/0.0.
|
|
17
|
+
@enspirit/emb/0.0.6 darwin-x64 node-v22.12.0
|
|
18
18
|
$ emb --help [COMMAND]
|
|
19
19
|
USAGE
|
|
20
20
|
$ emb COMMAND
|
|
@@ -36,7 +36,6 @@ USAGE
|
|
|
36
36
|
* [`emb images delete`](#emb-images-delete)
|
|
37
37
|
* [`emb images prune`](#emb-images-prune)
|
|
38
38
|
* [`emb ps`](#emb-ps)
|
|
39
|
-
* [`emb run COMPONENT SCRIPT`](#emb-run-component-script)
|
|
40
39
|
* [`emb tasks`](#emb-tasks)
|
|
41
40
|
* [`emb tasks run [TASK]`](#emb-tasks-run-task)
|
|
42
41
|
* [`emb up`](#emb-up)
|
|
@@ -323,22 +322,6 @@ EXAMPLES
|
|
|
323
322
|
$ emb ps
|
|
324
323
|
```
|
|
325
324
|
|
|
326
|
-
## `emb run COMPONENT SCRIPT`
|
|
327
|
-
|
|
328
|
-
Run an npm script from a component's package.json
|
|
329
|
-
|
|
330
|
-
```
|
|
331
|
-
USAGE
|
|
332
|
-
$ emb run COMPONENT SCRIPT
|
|
333
|
-
|
|
334
|
-
ARGUMENTS
|
|
335
|
-
COMPONENT Component name
|
|
336
|
-
SCRIPT NPM script to run
|
|
337
|
-
|
|
338
|
-
DESCRIPTION
|
|
339
|
-
Run an npm script from a component's package.json
|
|
340
|
-
```
|
|
341
|
-
|
|
342
325
|
## `emb tasks`
|
|
343
326
|
|
|
344
327
|
List tasks.
|
|
@@ -9,7 +9,7 @@ export default class ConfigPrint extends FlavoredCommand {
|
|
|
9
9
|
const context = await getContext();
|
|
10
10
|
const { monorepo } = context;
|
|
11
11
|
if (!flags.json) {
|
|
12
|
-
|
|
12
|
+
this.log(YAML.stringify(monorepo.config));
|
|
13
13
|
}
|
|
14
14
|
return monorepo.config;
|
|
15
15
|
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { getContext } from '../../../index.js';
|
|
2
2
|
import { Args, Command, Flags } from '@oclif/core';
|
|
3
3
|
import { Listr } from 'listr2';
|
|
4
|
-
import {
|
|
5
|
-
import { getContainer, ListContainersOperation } from '../../../docker/index.js';
|
|
6
|
-
import { dockerExecutor } from '../../../executors/docker.js';
|
|
7
|
-
import { ExecutorType } from '../../../executors/index.js';
|
|
8
|
-
import { shellExecutor } from '../../../executors/shell.js';
|
|
4
|
+
import { ExecutorType, RunTaskOperation, } from '../../../monorepo/operations/tasks/RunTaskOperation.js';
|
|
9
5
|
export default class RunTask extends Command {
|
|
10
6
|
static args = {
|
|
11
7
|
task: Args.string({
|
|
@@ -20,7 +16,7 @@ export default class RunTask extends Command {
|
|
|
20
16
|
executor: Flags.string({
|
|
21
17
|
char: 'x',
|
|
22
18
|
name: 'executor',
|
|
23
|
-
options: Object.
|
|
19
|
+
options: Object.values(ExecutorType),
|
|
24
20
|
}),
|
|
25
21
|
};
|
|
26
22
|
static strict = false;
|
|
@@ -38,30 +34,17 @@ export default class RunTask extends Command {
|
|
|
38
34
|
}, [])
|
|
39
35
|
: monorepo.tasks;
|
|
40
36
|
const runTasks = toRun.map((task) => {
|
|
37
|
+
const executor = flags.executor ||
|
|
38
|
+
(task.component ? ExecutorType.container : ExecutorType.local);
|
|
41
39
|
return {
|
|
42
40
|
rendererOptions: { persistentOutput: true },
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// Gonna log on both the logStream and stdout
|
|
49
|
-
const tee = new PassThrough();
|
|
50
|
-
tee.pipe(listrTask.stdout());
|
|
51
|
-
tee.pipe(logStream);
|
|
52
|
-
switch (type) {
|
|
53
|
-
case ExecutorType.container: {
|
|
54
|
-
return this.dockerExec(task, tee);
|
|
55
|
-
}
|
|
56
|
-
case ExecutorType.local: {
|
|
57
|
-
return this.shellExec(task, tee);
|
|
58
|
-
}
|
|
59
|
-
default: {
|
|
60
|
-
throw new Error(`Unsupported executor: ${type}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
41
|
+
async task(_ctx, listrTask) {
|
|
42
|
+
await monorepo.run(new RunTaskOperation(listrTask.stdout()), {
|
|
43
|
+
executor,
|
|
44
|
+
task,
|
|
45
|
+
});
|
|
63
46
|
},
|
|
64
|
-
title: `Running ${task.id}`,
|
|
47
|
+
title: `Running ${task.id} (${executor})`,
|
|
65
48
|
};
|
|
66
49
|
});
|
|
67
50
|
const runner = new Listr([
|
|
@@ -78,36 +61,4 @@ export default class RunTask extends Command {
|
|
|
78
61
|
]);
|
|
79
62
|
await runner.run();
|
|
80
63
|
}
|
|
81
|
-
async dockerExec(task, out) {
|
|
82
|
-
const { monorepo } = getContext();
|
|
83
|
-
const matching = await monorepo.run(new ListContainersOperation(), {
|
|
84
|
-
filters: {
|
|
85
|
-
label: [
|
|
86
|
-
`emb/project=${monorepo.name}`,
|
|
87
|
-
`emb/component=${task.component}`,
|
|
88
|
-
],
|
|
89
|
-
},
|
|
90
|
-
});
|
|
91
|
-
if (matching.length === 0) {
|
|
92
|
-
throw new Error(`Could not find a running container for '${task.component}'`);
|
|
93
|
-
}
|
|
94
|
-
if (matching.length > 1) {
|
|
95
|
-
throw new Error(`More than one running container found for '${task.component}'`);
|
|
96
|
-
}
|
|
97
|
-
const container = await getContainer(matching[0].Id);
|
|
98
|
-
return dockerExecutor.run(task.script, {
|
|
99
|
-
container,
|
|
100
|
-
out,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
async shellExec(task, out) {
|
|
104
|
-
const { monorepo } = getContext();
|
|
105
|
-
const cwd = task.component
|
|
106
|
-
? monorepo.component(task.component).rootdir
|
|
107
|
-
: monorepo.rootDir;
|
|
108
|
-
return shellExecutor.run(task.script, {
|
|
109
|
-
cwd,
|
|
110
|
-
out,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
64
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Flavor } from './schema.js';
|
|
2
|
+
import { FlavorConfig, IMonorepoConfig, UserConfig } from './types.js';
|
|
3
3
|
export declare const toFlavor: (flavor: Flavor) => FlavorConfig;
|
|
4
|
-
export declare const toComponent: (cmp: Component) => ComponentConfig;
|
|
5
4
|
export declare const toProjectConfig: (config: UserConfig, rootDir?: string) => IMonorepoConfig;
|
|
@@ -3,14 +3,9 @@ import { cwd } from 'node:process';
|
|
|
3
3
|
export const toFlavor = (flavor) => {
|
|
4
4
|
return {
|
|
5
5
|
...flavor,
|
|
6
|
-
components: flavor.components
|
|
6
|
+
components: flavor.components,
|
|
7
7
|
};
|
|
8
8
|
};
|
|
9
|
-
export const toComponent = (cmp) => {
|
|
10
|
-
return typeof cmp === 'string'
|
|
11
|
-
? { context: cmp, name: cmp }
|
|
12
|
-
: cmp;
|
|
13
|
-
};
|
|
14
9
|
export const toProjectConfig = (config, rootDir) => {
|
|
15
10
|
const project = typeof config.project === 'string'
|
|
16
11
|
? { name: config.project }
|
|
@@ -23,7 +18,7 @@ export const toProjectConfig = (config, rootDir) => {
|
|
|
23
18
|
else {
|
|
24
19
|
project.rootDir = rootDir || cwd();
|
|
25
20
|
}
|
|
26
|
-
const components =
|
|
21
|
+
const components = config.components || [];
|
|
27
22
|
const { defaults, env, flavors, plugins, vars } = config;
|
|
28
23
|
return {
|
|
29
24
|
components,
|
|
@@ -3,33 +3,6 @@
|
|
|
3
3
|
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
|
|
4
4
|
* and run json-schema-to-typescript to regenerate this file.
|
|
5
5
|
*/
|
|
6
|
-
export type Component = string | {
|
|
7
|
-
/**
|
|
8
|
-
* The name of the component.
|
|
9
|
-
*/
|
|
10
|
-
name: string;
|
|
11
|
-
/**
|
|
12
|
-
* The directory of the component.
|
|
13
|
-
*/
|
|
14
|
-
context?: string;
|
|
15
|
-
/**
|
|
16
|
-
* A description of the component.
|
|
17
|
-
*/
|
|
18
|
-
description?: string;
|
|
19
|
-
buildArgs?: {
|
|
20
|
-
[k: string]: unknown;
|
|
21
|
-
};
|
|
22
|
-
dependencies?: string[];
|
|
23
|
-
/**
|
|
24
|
-
* The stage to target for the build
|
|
25
|
-
*/
|
|
26
|
-
target?: string;
|
|
27
|
-
/**
|
|
28
|
-
* The Dockerfile to use
|
|
29
|
-
*/
|
|
30
|
-
dockerfile?: string;
|
|
31
|
-
tasks?: Task[];
|
|
32
|
-
};
|
|
33
6
|
export interface EMBConfigSchema {
|
|
34
7
|
project: string | {
|
|
35
8
|
/**
|
|
@@ -72,7 +45,7 @@ export interface Defaults {
|
|
|
72
45
|
tag?: string;
|
|
73
46
|
target?: string;
|
|
74
47
|
buildArgs?: {
|
|
75
|
-
[k: string]:
|
|
48
|
+
[k: string]: string;
|
|
76
49
|
};
|
|
77
50
|
labels?: {
|
|
78
51
|
[k: string]: string;
|
|
@@ -80,6 +53,33 @@ export interface Defaults {
|
|
|
80
53
|
[k: string]: unknown;
|
|
81
54
|
};
|
|
82
55
|
}
|
|
56
|
+
export interface Component {
|
|
57
|
+
/**
|
|
58
|
+
* The name of the component.
|
|
59
|
+
*/
|
|
60
|
+
name: string;
|
|
61
|
+
/**
|
|
62
|
+
* The directory of the component.
|
|
63
|
+
*/
|
|
64
|
+
context?: string;
|
|
65
|
+
/**
|
|
66
|
+
* A description of the component.
|
|
67
|
+
*/
|
|
68
|
+
description?: string;
|
|
69
|
+
buildArgs?: {
|
|
70
|
+
[k: string]: string;
|
|
71
|
+
};
|
|
72
|
+
dependencies?: string[];
|
|
73
|
+
/**
|
|
74
|
+
* The stage to target for the build
|
|
75
|
+
*/
|
|
76
|
+
target?: string;
|
|
77
|
+
/**
|
|
78
|
+
* The Dockerfile to use
|
|
79
|
+
*/
|
|
80
|
+
dockerfile?: string;
|
|
81
|
+
tasks?: Task[];
|
|
82
|
+
}
|
|
83
83
|
export interface Task {
|
|
84
84
|
name: string;
|
|
85
85
|
description?: string;
|
|
@@ -88,7 +88,9 @@
|
|
|
88
88
|
"target": { "type": "string" },
|
|
89
89
|
"buildArgs": {
|
|
90
90
|
"type": "object",
|
|
91
|
-
"additionalProperties":
|
|
91
|
+
"additionalProperties": {
|
|
92
|
+
"type": "string"
|
|
93
|
+
}
|
|
92
94
|
},
|
|
93
95
|
"labels": {
|
|
94
96
|
"type": "object",
|
|
@@ -124,60 +126,53 @@
|
|
|
124
126
|
"additionalProperties": false
|
|
125
127
|
},
|
|
126
128
|
"component": {
|
|
127
|
-
"
|
|
128
|
-
|
|
129
|
+
"type": "object",
|
|
130
|
+
"required": ["name"],
|
|
131
|
+
"properties": {
|
|
132
|
+
"name": {
|
|
129
133
|
"type": "string",
|
|
130
134
|
"minLength": 3,
|
|
131
|
-
"description": "
|
|
135
|
+
"description": "The name of the component."
|
|
132
136
|
},
|
|
133
|
-
{
|
|
137
|
+
"context": {
|
|
138
|
+
"type": "string",
|
|
139
|
+
"minLength": 1,
|
|
140
|
+
"description": "The directory of the component."
|
|
141
|
+
},
|
|
142
|
+
"description": {
|
|
143
|
+
"type": "string",
|
|
144
|
+
"minLength": 1,
|
|
145
|
+
"description": "A description of the component."
|
|
146
|
+
},
|
|
147
|
+
"buildArgs": {
|
|
134
148
|
"type": "object",
|
|
135
|
-
"
|
|
136
|
-
"
|
|
137
|
-
"
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"
|
|
158
|
-
|
|
159
|
-
"items": {
|
|
160
|
-
"type": "string"
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
"target": {
|
|
164
|
-
"type": "string",
|
|
165
|
-
"description": "The stage to target for the build"
|
|
166
|
-
},
|
|
167
|
-
"dockerfile": {
|
|
168
|
-
"type": "string",
|
|
169
|
-
"description": "The Dockerfile to use"
|
|
170
|
-
},
|
|
171
|
-
"tasks": {
|
|
172
|
-
"type": "array",
|
|
173
|
-
"items": {
|
|
174
|
-
"$ref": "#/$defs/task"
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
"additionalProperties": false
|
|
149
|
+
"properties": {},
|
|
150
|
+
"additionalProperties": {
|
|
151
|
+
"type": "string"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"dependencies": {
|
|
155
|
+
"type": "array",
|
|
156
|
+
"items": {
|
|
157
|
+
"type": "string"
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"target": {
|
|
161
|
+
"type": "string",
|
|
162
|
+
"description": "The stage to target for the build"
|
|
163
|
+
},
|
|
164
|
+
"dockerfile": {
|
|
165
|
+
"type": "string",
|
|
166
|
+
"description": "The Dockerfile to use"
|
|
167
|
+
},
|
|
168
|
+
"tasks": {
|
|
169
|
+
"type": "array",
|
|
170
|
+
"items": {
|
|
171
|
+
"$ref": "#/$defs/task"
|
|
172
|
+
}
|
|
179
173
|
}
|
|
180
|
-
|
|
174
|
+
},
|
|
175
|
+
"additionalProperties": false
|
|
181
176
|
},
|
|
182
177
|
"flavor": {
|
|
183
178
|
"type": "object",
|
|
@@ -5,7 +5,7 @@ export type IProjectConfig = {
|
|
|
5
5
|
rootDir: string;
|
|
6
6
|
};
|
|
7
7
|
export type ComponentConfig = {
|
|
8
|
-
buildArgs?: Record<
|
|
8
|
+
buildArgs?: Record<string, string>;
|
|
9
9
|
context?: string;
|
|
10
10
|
dependencies?: Array<string>;
|
|
11
11
|
dockerfile?: string;
|
|
@@ -16,7 +16,7 @@ export type ComponentConfig = {
|
|
|
16
16
|
};
|
|
17
17
|
export type DefaultSettings = {
|
|
18
18
|
docker?: {
|
|
19
|
-
buildArgs?: Record<string,
|
|
19
|
+
buildArgs?: Record<string, string>;
|
|
20
20
|
labels?: Record<string, string>;
|
|
21
21
|
tag?: string;
|
|
22
22
|
target?: string;
|
|
@@ -3,8 +3,9 @@ import { readFile, stat } from 'node:fs/promises';
|
|
|
3
3
|
import yaml from 'yaml';
|
|
4
4
|
import { toProjectConfig } from './convert.js';
|
|
5
5
|
import configSchema from './schema.json' with { type: 'json' };
|
|
6
|
+
const ajv = new Ajv();
|
|
7
|
+
ajv.addSchema(configSchema);
|
|
6
8
|
export const validateUserConfig = async (pathOrObject) => {
|
|
7
|
-
const ajv = new Ajv();
|
|
8
9
|
let userConfig;
|
|
9
10
|
if (typeof pathOrObject === 'string') {
|
|
10
11
|
if (await stat(pathOrObject)) {
|
|
@@ -24,3 +25,27 @@ export const validateUserConfig = async (pathOrObject) => {
|
|
|
24
25
|
}
|
|
25
26
|
return toProjectConfig(userConfig);
|
|
26
27
|
};
|
|
28
|
+
export const validateEmbfile = async (pathOrObject) => {
|
|
29
|
+
let component;
|
|
30
|
+
if (typeof pathOrObject === 'string') {
|
|
31
|
+
if (await stat(pathOrObject)) {
|
|
32
|
+
const cfgYaml = (await readFile(pathOrObject)).toString();
|
|
33
|
+
component = yaml.parse(cfgYaml.toString());
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
throw new Error(`Could not find file: ${pathOrObject}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
component = pathOrObject;
|
|
41
|
+
}
|
|
42
|
+
const validate = ajv.getSchema('/schemas/config#/$defs/component');
|
|
43
|
+
if (!validate) {
|
|
44
|
+
throw new Error('Could not find the JSON schema validator for Embfile');
|
|
45
|
+
}
|
|
46
|
+
if (!validate(component)) {
|
|
47
|
+
ajv.errors.forEach((err) => console.error(err));
|
|
48
|
+
throw new Error(`Your .emb.yml is incorrect`);
|
|
49
|
+
}
|
|
50
|
+
return component;
|
|
51
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Writable } from 'node:stream';
|
|
2
|
+
import * as z from 'zod';
|
|
3
|
+
import { AbstractOperation } from '../../../operations/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* https://docs.docker.com/reference/api/engine/version/v1.37/#tag/Exec/operation/ContainerExec
|
|
6
|
+
*/
|
|
7
|
+
declare const schema: z.ZodObject<{
|
|
8
|
+
attachStderr: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
9
|
+
attachStdin: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
10
|
+
attachStdout: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
11
|
+
container: z.ZodString;
|
|
12
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
13
|
+
script: z.ZodString;
|
|
14
|
+
tty: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
15
|
+
workingDir: z.ZodOptional<z.ZodString>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
export declare class ContainerExecOperation extends AbstractOperation<typeof schema, void> {
|
|
18
|
+
protected out?: Writable | undefined;
|
|
19
|
+
constructor(out?: Writable | undefined);
|
|
20
|
+
protected _run(input: z.input<typeof schema>): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
import { AbstractOperation } from '../../../operations/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* https://docs.docker.com/reference/api/engine/version/v1.37/#tag/Exec/operation/ContainerExec
|
|
5
|
+
*/
|
|
6
|
+
const schema = z.object({
|
|
7
|
+
attachStderr: z
|
|
8
|
+
.boolean()
|
|
9
|
+
.default(false)
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('Attach to `stderr` of the exec command.'),
|
|
12
|
+
attachStdin: z
|
|
13
|
+
.boolean()
|
|
14
|
+
.default(false)
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('Attach to `stdin` of the exec command.'),
|
|
17
|
+
attachStdout: z
|
|
18
|
+
.boolean()
|
|
19
|
+
.default(false)
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('Attach to `stdout` of the exec command.'),
|
|
22
|
+
container: z.string().describe('ID or name of the container'),
|
|
23
|
+
env: z
|
|
24
|
+
.record(z.string(), z.string())
|
|
25
|
+
.optional()
|
|
26
|
+
.describe('A list of environment variables in the form'),
|
|
27
|
+
script: z.string().describe('Command to run, as a string'),
|
|
28
|
+
tty: z.boolean().default(false).optional().describe('Allocate a pseudo-TTY'),
|
|
29
|
+
workingDir: z
|
|
30
|
+
.string()
|
|
31
|
+
.optional()
|
|
32
|
+
.describe('The working directory for the exec process inside the container'),
|
|
33
|
+
});
|
|
34
|
+
export class ContainerExecOperation extends AbstractOperation {
|
|
35
|
+
out;
|
|
36
|
+
constructor(out) {
|
|
37
|
+
super(schema);
|
|
38
|
+
this.out = out;
|
|
39
|
+
}
|
|
40
|
+
async _run(input) {
|
|
41
|
+
const container = await this.context.docker.getContainer(input.container);
|
|
42
|
+
const envVars = Object.entries(input.env || {}).reduce((arr, [key, value]) => {
|
|
43
|
+
return [...arr, `${key}=${value}`];
|
|
44
|
+
}, []);
|
|
45
|
+
const options = {
|
|
46
|
+
AttachStderr: input.attachStderr,
|
|
47
|
+
AttachStdin: input.attachStdin,
|
|
48
|
+
AttachStdout: input.attachStdout,
|
|
49
|
+
Cmd: ['bash', '-eu', '-o', 'pipefail', '-c', input.script],
|
|
50
|
+
Env: envVars,
|
|
51
|
+
Tty: input.tty,
|
|
52
|
+
WorkingDir: input.workingDir,
|
|
53
|
+
};
|
|
54
|
+
const exec = await container.exec(options);
|
|
55
|
+
const stream = await exec.start({});
|
|
56
|
+
container.modem.demuxStream(stream, this.out || process.stdout, this.out || process.stderr);
|
|
57
|
+
await new Promise((resolve, reject) => {
|
|
58
|
+
const onError = (err) => reject(err);
|
|
59
|
+
const onEnd = async () => {
|
|
60
|
+
exec.inspect((error, res) => {
|
|
61
|
+
if (error)
|
|
62
|
+
return reject(error);
|
|
63
|
+
const code = res?.ExitCode ?? 0;
|
|
64
|
+
if (code !== 0) {
|
|
65
|
+
const msg = res?.ProcessConfig?.entrypoint
|
|
66
|
+
? `container exec failed (exit ${code})`
|
|
67
|
+
: `command failed (exit ${code})`;
|
|
68
|
+
return reject(new Error(msg));
|
|
69
|
+
}
|
|
70
|
+
resolve();
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
stream.on('error', onError);
|
|
74
|
+
stream.on('end', onEnd);
|
|
75
|
+
stream.on('close', onEnd); // some engines emit 'close' not 'end'
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { ComponentConfig, DefaultSettings, FlavorConfig, IMonorepoConfig, IProjectConfig, PluginConfig } from '../config/index.js';
|
|
2
2
|
export declare class MonorepoConfig implements IMonorepoConfig {
|
|
3
|
-
components: ComponentConfig[];
|
|
4
3
|
defaults: DefaultSettings;
|
|
5
4
|
env: Record<string, string>;
|
|
6
5
|
flavors: Array<FlavorConfig>;
|
|
7
6
|
plugins: Array<PluginConfig>;
|
|
8
7
|
project: IProjectConfig;
|
|
9
8
|
vars: Record<string, unknown>;
|
|
9
|
+
private _components;
|
|
10
10
|
constructor(config: IMonorepoConfig);
|
|
11
|
+
get components(): ComponentConfig[];
|
|
11
12
|
component(name: string): ComponentConfig;
|
|
12
13
|
flavor(name: string): FlavorConfig;
|
|
13
14
|
toJSON(): Required<IMonorepoConfig>;
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import deepMerge from '@fastify/deepmerge';
|
|
2
2
|
import { deepMergeArray } from '../utils/index.js';
|
|
3
3
|
export class MonorepoConfig {
|
|
4
|
-
components;
|
|
5
4
|
defaults;
|
|
6
5
|
env;
|
|
7
6
|
flavors;
|
|
8
7
|
plugins;
|
|
9
8
|
project;
|
|
10
9
|
vars;
|
|
10
|
+
_components;
|
|
11
11
|
constructor(config) {
|
|
12
|
-
this.
|
|
12
|
+
this._components = config.components.reduce((map, cmp) => {
|
|
13
|
+
map.set(cmp.name, cmp);
|
|
14
|
+
return map;
|
|
15
|
+
}, new Map());
|
|
13
16
|
this.defaults = config.defaults || {};
|
|
14
17
|
this.project = config.project;
|
|
15
18
|
this.vars = config.vars || {};
|
|
@@ -17,6 +20,9 @@ export class MonorepoConfig {
|
|
|
17
20
|
this.env = config.env || {};
|
|
18
21
|
this.plugins = config.plugins || [];
|
|
19
22
|
}
|
|
23
|
+
get components() {
|
|
24
|
+
return [...this._components.values()];
|
|
25
|
+
}
|
|
20
26
|
component(name) {
|
|
21
27
|
const config = this.components.find((c) => c.name === name);
|
|
22
28
|
if (!config) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ContainerInfo } from 'dockerode';
|
|
2
|
+
import { Component } from '../../component.js';
|
|
3
|
+
import { IOperation } from '../../../operations/index.js';
|
|
4
|
+
export declare class GetComponentContainerOperation implements IOperation<Component, ContainerInfo> {
|
|
5
|
+
run(component: Component | string): Promise<ContainerInfo>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { getContext } from '../../../index.js';
|
|
2
|
+
import { ListContainersOperation } from '../../../docker/index.js';
|
|
3
|
+
import { Component } from '../../component.js';
|
|
4
|
+
export class GetComponentContainerOperation {
|
|
5
|
+
async run(component) {
|
|
6
|
+
const { monorepo } = getContext();
|
|
7
|
+
const cmpName = component instanceof Component ? component.name : component;
|
|
8
|
+
const matching = await monorepo.run(new ListContainersOperation(), {
|
|
9
|
+
filters: {
|
|
10
|
+
label: [`emb/project=${monorepo.name}`, `emb/component=${cmpName}`],
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
if (matching.length === 0) {
|
|
14
|
+
throw new Error(`Could not find a running container for '${cmpName}'`);
|
|
15
|
+
}
|
|
16
|
+
if (matching.length > 1) {
|
|
17
|
+
throw new Error(`More than one running container found for '${cmpName}'`);
|
|
18
|
+
}
|
|
19
|
+
return matching[0];
|
|
20
|
+
}
|
|
21
|
+
}
|