@enspirit/emb 0.5.2 → 0.6.0
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 +87 -3
- package/dist/src/cli/abstract/BaseCommand.js +3 -1
- package/dist/src/cli/commands/components/logs.js +7 -19
- package/dist/src/cli/commands/images/push.d.ts +11 -0
- package/dist/src/cli/commands/images/push.js +29 -0
- package/dist/src/cli/commands/resources/index.js +9 -1
- package/dist/src/cli/commands/restart.d.ts +14 -0
- package/dist/src/cli/commands/restart.js +38 -0
- package/dist/src/cli/commands/stop.js +2 -2
- package/dist/src/cli/commands/tasks/index.js +1 -1
- package/dist/src/cli/commands/tasks/run.d.ts +1 -0
- package/dist/src/cli/commands/tasks/run.js +1 -0
- package/dist/src/config/schema.d.ts +6 -0
- package/dist/src/config/schema.json +7 -0
- package/dist/src/docker/compose/client.d.ts +24 -0
- package/dist/src/docker/compose/client.js +55 -0
- package/dist/src/docker/compose/index.d.ts +1 -0
- package/dist/src/docker/compose/index.js +1 -0
- package/dist/src/docker/compose/operations/ComposeExecOperation.d.ts +1 -0
- package/dist/src/docker/compose/operations/ComposeExecOperation.js +12 -1
- package/dist/src/docker/compose/operations/ComposeRestartOperation.d.ts +14 -0
- package/dist/src/docker/compose/operations/ComposeRestartOperation.js +46 -0
- package/dist/src/docker/compose/operations/index.d.ts +1 -0
- package/dist/src/docker/compose/operations/index.js +1 -0
- package/dist/src/docker/index.d.ts +0 -1
- package/dist/src/docker/index.js +0 -1
- package/dist/src/docker/operations/containers/ContainerExecOperation.d.ts +0 -3
- package/dist/src/docker/operations/containers/ContainerExecOperation.js +3 -19
- package/dist/src/docker/operations/images/PushImagesOperation.d.ts +9 -3
- package/dist/src/docker/operations/images/PushImagesOperation.js +104 -6
- package/dist/src/docker/resources/DockerImageResource.js +9 -6
- package/dist/src/monorepo/component.d.ts +1 -0
- package/dist/src/monorepo/component.js +3 -0
- package/dist/src/monorepo/config.js +1 -1
- package/dist/src/monorepo/monorepo.js +1 -8
- package/dist/src/monorepo/operations/resources/BuildResourcesOperation.js +0 -1
- package/dist/src/monorepo/operations/shell/ExecuteLocalCommandOperation.js +1 -0
- package/dist/src/monorepo/operations/tasks/RunTasksOperation.d.ts +5 -1
- package/dist/src/monorepo/operations/tasks/RunTasksOperation.js +8 -5
- package/dist/src/monorepo/resources/FileResourceBuilder.d.ts +1 -0
- package/dist/src/monorepo/resources/FileResourceBuilder.js +4 -1
- package/dist/src/monorepo/resources/ResourceFactory.js +1 -1
- package/dist/src/monorepo/resources/abstract/AbstractResourceBuilder.d.ts +1 -0
- package/dist/src/monorepo/resources/types.d.ts +6 -0
- package/dist/src/monorepo/types.d.ts +6 -1
- package/dist/src/monorepo/utils/EMBCollection.d.ts +0 -3
- package/dist/src/monorepo/utils/EMBCollection.js +0 -14
- package/dist/src/monorepo/utils/index.d.ts +7 -4
- package/dist/src/monorepo/utils/index.js +14 -8
- package/dist/src/types.d.ts +2 -0
- package/oclif.manifest.json +139 -36
- package/package.json +1 -1
- package/dist/src/docker/containers/getContainer.d.ts +0 -2
- package/dist/src/docker/containers/getContainer.js +0 -5
- package/dist/src/docker/containers/index.d.ts +0 -1
- package/dist/src/docker/containers/index.js +0 -1
|
@@ -4,21 +4,6 @@ import { AbstractOperation } from '../../../operations/index.js';
|
|
|
4
4
|
* https://docs.docker.com/reference/api/engine/version/v1.37/#tag/Exec/operation/ContainerExec
|
|
5
5
|
*/
|
|
6
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
7
|
container: z.string().describe('ID or name of the container'),
|
|
23
8
|
env: z
|
|
24
9
|
.record(z.string(), z.string())
|
|
@@ -43,9 +28,8 @@ export class ContainerExecOperation extends AbstractOperation {
|
|
|
43
28
|
return [...arr, `${key}=${value}`];
|
|
44
29
|
}, []);
|
|
45
30
|
const options = {
|
|
46
|
-
AttachStderr:
|
|
47
|
-
|
|
48
|
-
AttachStdout: input.attachStdout,
|
|
31
|
+
AttachStderr: true,
|
|
32
|
+
AttachStdout: true,
|
|
49
33
|
Cmd: ['bash', '-eu', '-o', 'pipefail', '-c', input.script],
|
|
50
34
|
Env: envVars,
|
|
51
35
|
Tty: input.tty,
|
|
@@ -53,7 +37,7 @@ export class ContainerExecOperation extends AbstractOperation {
|
|
|
53
37
|
};
|
|
54
38
|
const exec = await container.exec(options);
|
|
55
39
|
const stream = await exec.start({});
|
|
56
|
-
|
|
40
|
+
exec.modem.demuxStream(stream, this.out || process.stdout, this.out || process.stderr);
|
|
57
41
|
await new Promise((resolve, reject) => {
|
|
58
42
|
const onError = (err) => reject(err);
|
|
59
43
|
const onEnd = async () => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Writable } from 'node:stream';
|
|
1
2
|
import * as z from 'zod';
|
|
2
3
|
import { AbstractOperation } from '../../../operations/index.js';
|
|
3
4
|
/**
|
|
@@ -5,10 +6,15 @@ import { AbstractOperation } from '../../../operations/index.js';
|
|
|
5
6
|
*/
|
|
6
7
|
declare const schema: z.ZodObject<{
|
|
7
8
|
images: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
8
|
-
tag: z.
|
|
9
|
+
tag: z.ZodOptional<z.ZodString>;
|
|
10
|
+
registry: z.ZodOptional<z.ZodString>;
|
|
11
|
+
retag: z.ZodOptional<z.ZodString>;
|
|
9
12
|
}, z.core.$strip>;
|
|
10
13
|
export declare class PushImagesOperation extends AbstractOperation<typeof schema, void> {
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
protected out?: Writable | undefined;
|
|
15
|
+
constructor(out?: Writable | undefined);
|
|
16
|
+
protected _run(input: z.input<typeof schema>): Promise<void>;
|
|
17
|
+
private retagIfNecessary;
|
|
18
|
+
private pushImage;
|
|
13
19
|
}
|
|
14
20
|
export {};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { taskManagerFactory } from '../../../index.js';
|
|
2
|
+
import { join } from 'node:path/posix';
|
|
3
|
+
import { Transform } from 'node:stream';
|
|
1
4
|
import * as z from 'zod';
|
|
5
|
+
import { ResourceFactory } from '../../../monorepo/resources/ResourceFactory.js';
|
|
2
6
|
import { AbstractOperation } from '../../../operations/index.js';
|
|
3
7
|
/**
|
|
4
8
|
* https://docs.docker.com/reference/api/engine/version/v1.37/#tag/Image/operation/ImagePush
|
|
@@ -7,16 +11,110 @@ const schema = z.object({
|
|
|
7
11
|
images: z
|
|
8
12
|
.array(z.string())
|
|
9
13
|
.optional()
|
|
10
|
-
.describe('The names of images to push (The name should be provided without tag. Use the `tag` parameter to specify
|
|
11
|
-
tag: z
|
|
14
|
+
.describe('The names of images to push (The name should be provided without tag. Use the `tag` parameter to specify which tag to push)'),
|
|
15
|
+
tag: z.string().optional().describe('Tag of the images to push'),
|
|
16
|
+
registry: z.string().optional().describe('Override the registry to push to'),
|
|
17
|
+
retag: z
|
|
12
18
|
.string()
|
|
13
19
|
.optional()
|
|
14
|
-
.
|
|
15
|
-
.describe('Tag of the images to push'),
|
|
20
|
+
.describe('Override the original tag to push as a new tag'),
|
|
16
21
|
});
|
|
17
22
|
export class PushImagesOperation extends AbstractOperation {
|
|
18
|
-
|
|
23
|
+
out;
|
|
24
|
+
constructor(out) {
|
|
19
25
|
super(schema);
|
|
26
|
+
this.out = out;
|
|
27
|
+
}
|
|
28
|
+
async _run(input) {
|
|
29
|
+
const { monorepo } = this.context;
|
|
30
|
+
const references = await Promise.all(monorepo.resources
|
|
31
|
+
.filter((r) => r.type === 'docker/image')
|
|
32
|
+
.map(async (config) => {
|
|
33
|
+
const component = monorepo.component(config.component);
|
|
34
|
+
const builder = ResourceFactory.factor(config.type, {
|
|
35
|
+
config,
|
|
36
|
+
monorepo,
|
|
37
|
+
component,
|
|
38
|
+
});
|
|
39
|
+
return builder.getReference();
|
|
40
|
+
}));
|
|
41
|
+
const manager = taskManagerFactory();
|
|
42
|
+
const tasks = references.map((fullName) => {
|
|
43
|
+
return {
|
|
44
|
+
title: `Push ${fullName}`,
|
|
45
|
+
task: async (ctx, task) => {
|
|
46
|
+
const { imgName, tag } = await this.retagIfNecessary(fullName, input.retag, input.registry);
|
|
47
|
+
task.title = `Pushing ${imgName}:${tag}`;
|
|
48
|
+
return this.pushImage(imgName, tag, task.stdout());
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
return manager.run([
|
|
53
|
+
{
|
|
54
|
+
title: 'Push imags',
|
|
55
|
+
async task(ctx, task) {
|
|
56
|
+
return task.newListr(tasks, {
|
|
57
|
+
rendererOptions: {
|
|
58
|
+
collapseSubtasks: false,
|
|
59
|
+
collapseSkips: true,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
async retagIfNecessary(fullName, retag, registry) {
|
|
67
|
+
let [imgName, tag] = fullName.split(':');
|
|
68
|
+
// Retag if necessary
|
|
69
|
+
if (retag || registry) {
|
|
70
|
+
const dockerImage = await this.context.docker.getImage(fullName);
|
|
71
|
+
tag = retag || tag;
|
|
72
|
+
imgName = registry ? join(registry, imgName) : imgName;
|
|
73
|
+
await dockerImage.tag({
|
|
74
|
+
tag,
|
|
75
|
+
repo: imgName,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return { imgName, tag };
|
|
79
|
+
}
|
|
80
|
+
async pushImage(repo, tag, out) {
|
|
81
|
+
const dockerImage = await this.context.docker.getImage(`${repo}:${tag}`);
|
|
82
|
+
const stream = await dockerImage.push({
|
|
83
|
+
authconfig: {
|
|
84
|
+
username: process.env.DOCKER_USERNAME,
|
|
85
|
+
password: process.env.DOCKER_PASSWORD,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
const transform = new Transform({
|
|
89
|
+
transform(chunk, encoding, callback) {
|
|
90
|
+
const lines = chunk.toString().split('\n');
|
|
91
|
+
lines.forEach((line) => {
|
|
92
|
+
if (!line.trim()) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const { status } = JSON.parse(line.trim());
|
|
97
|
+
out?.write(status + '\n');
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
out?.write(error + '\n');
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
callback();
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
stream.pipe(transform).pipe(process.stdout);
|
|
107
|
+
await new Promise((resolve, reject) => {
|
|
108
|
+
this.context.docker.modem.followProgress(stream, (err, data) => {
|
|
109
|
+
if (err) {
|
|
110
|
+
return reject(err);
|
|
111
|
+
}
|
|
112
|
+
const hasError = data.find((d) => Boolean(d.error));
|
|
113
|
+
if (hasError) {
|
|
114
|
+
return reject(new Error(hasError.error));
|
|
115
|
+
}
|
|
116
|
+
resolve(null);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
20
119
|
}
|
|
21
|
-
async _run(_input) { }
|
|
22
120
|
}
|
|
@@ -18,6 +18,14 @@ class DockerImageResourceBuilder extends SentinelFileBasedBuilder {
|
|
|
18
18
|
: buildContext.component.join(this.config.context)
|
|
19
19
|
: buildContext.monorepo.join(buildContext.component.rootDir);
|
|
20
20
|
}
|
|
21
|
+
async getReference() {
|
|
22
|
+
const imageName = [
|
|
23
|
+
this.monorepo.name,
|
|
24
|
+
this.config?.tag || this.component.name,
|
|
25
|
+
].join('/');
|
|
26
|
+
const tagName = this.config?.tag || this.monorepo.defaults.docker?.tag || 'latest';
|
|
27
|
+
return this.monorepo.expand(`${imageName}:${tagName}`);
|
|
28
|
+
}
|
|
21
29
|
get monorepo() {
|
|
22
30
|
return this.buildContext.monorepo;
|
|
23
31
|
}
|
|
@@ -30,11 +38,6 @@ class DockerImageResourceBuilder extends SentinelFileBasedBuilder {
|
|
|
30
38
|
async _build(_resource, out) {
|
|
31
39
|
// Ensure the folder exists
|
|
32
40
|
await statfs(this.dockerContext);
|
|
33
|
-
const imageName = [
|
|
34
|
-
this.monorepo.name,
|
|
35
|
-
this.config?.tag || this.component.name,
|
|
36
|
-
].join('/');
|
|
37
|
-
const tagName = this.config?.tag || this.monorepo.defaults.docker?.tag || 'latest';
|
|
38
41
|
const crawler = new Fdir();
|
|
39
42
|
const sources = await crawler
|
|
40
43
|
.withRelativePaths()
|
|
@@ -48,7 +51,7 @@ class DockerImageResourceBuilder extends SentinelFileBasedBuilder {
|
|
|
48
51
|
...this.monorepo.defaults.docker?.buildArgs,
|
|
49
52
|
...this.config?.buildArgs,
|
|
50
53
|
}),
|
|
51
|
-
tag: await this.
|
|
54
|
+
tag: await this.getReference(),
|
|
52
55
|
labels: await this.monorepo.expand({
|
|
53
56
|
...this.config?.labels,
|
|
54
57
|
'emb/project': this.monorepo.name,
|
|
@@ -16,7 +16,7 @@ export class MonorepoConfig {
|
|
|
16
16
|
this.flavors = config.flavors || {};
|
|
17
17
|
this.env = config.env || {};
|
|
18
18
|
this.plugins = config.plugins || [];
|
|
19
|
-
this.tasks = toIdentifedHash(config.tasks || {}
|
|
19
|
+
this.tasks = toIdentifedHash(config.tasks || {});
|
|
20
20
|
this.components = config.components || {};
|
|
21
21
|
}
|
|
22
22
|
component(id) {
|
|
@@ -75,14 +75,7 @@ export class Monorepo {
|
|
|
75
75
|
}
|
|
76
76
|
get resources() {
|
|
77
77
|
return this.components.reduce((resources, cmp) => {
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
...task,
|
|
81
|
-
name,
|
|
82
|
-
id: `${cmp.name}:${name}`,
|
|
83
|
-
};
|
|
84
|
-
});
|
|
85
|
-
return [...resources, ...cmpResources];
|
|
78
|
+
return [...resources, ...Object.values(cmp.resources)];
|
|
86
79
|
}, []);
|
|
87
80
|
}
|
|
88
81
|
resource(nameOrId) {
|
|
@@ -34,7 +34,6 @@ export class BuildResourcesOperation extends AbstractOperation {
|
|
|
34
34
|
const collection = new EMBCollection(monorepo.resources, {
|
|
35
35
|
idField: 'id',
|
|
36
36
|
depField: 'dependencies',
|
|
37
|
-
forbidIdNameCollision: true,
|
|
38
37
|
});
|
|
39
38
|
const ordered = findRunOrder(input.resources || [], collection);
|
|
40
39
|
const tasks = ordered.map((resource) => {
|
|
@@ -13,8 +13,12 @@ export type RunTasksOperationParams = {
|
|
|
13
13
|
export type TaskWithScript = TaskInfo & {
|
|
14
14
|
script: string;
|
|
15
15
|
};
|
|
16
|
+
export type TaskWithScriptAndComponent = TaskInfo & {
|
|
17
|
+
script: string;
|
|
18
|
+
component: string;
|
|
19
|
+
};
|
|
16
20
|
export declare class RunTasksOperation implements IOperation<RunTasksOperationParams, Array<TaskInfo>> {
|
|
17
21
|
run(params: RunTasksOperationParams): Promise<Array<TaskInfo>>;
|
|
18
|
-
protected runDocker(task:
|
|
22
|
+
protected runDocker(task: TaskWithScriptAndComponent, out?: Writable): Promise<void>;
|
|
19
23
|
protected runLocal(task: TaskWithScript, out: Writable): Promise<import("stream").Readable>;
|
|
20
24
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getContext } from '../../../index.js';
|
|
2
2
|
import { PassThrough } from 'node:stream';
|
|
3
|
-
import {
|
|
3
|
+
import { ContainerExecOperation } from '../../../docker/index.js';
|
|
4
4
|
import { EMBCollection, findRunOrder, taskManagerFactory, } from '../../index.js';
|
|
5
5
|
import { ExecuteLocalCommandOperation } from '../index.js';
|
|
6
6
|
export var ExecutorType;
|
|
@@ -57,10 +57,12 @@ export class RunTasksOperation {
|
|
|
57
57
|
return ordered;
|
|
58
58
|
}
|
|
59
59
|
async runDocker(task, out) {
|
|
60
|
-
const { monorepo } = getContext();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
const { monorepo, compose } = getContext();
|
|
61
|
+
const containerID = await compose.getContainer(task.component);
|
|
62
|
+
return monorepo.run(new ContainerExecOperation(out), {
|
|
63
|
+
container: containerID,
|
|
64
|
+
script: task.script,
|
|
65
|
+
env: await monorepo.expand(task.vars || {}),
|
|
64
66
|
});
|
|
65
67
|
}
|
|
66
68
|
async runLocal(task, out) {
|
|
@@ -71,6 +73,7 @@ export class RunTasksOperation {
|
|
|
71
73
|
return monorepo.run(new ExecuteLocalCommandOperation(out), {
|
|
72
74
|
script: task.script,
|
|
73
75
|
workingDir: cwd,
|
|
76
|
+
env: await monorepo.expand(task.vars || {}),
|
|
74
77
|
});
|
|
75
78
|
}
|
|
76
79
|
}
|
|
@@ -5,6 +5,7 @@ import { ResourceBuildContext } from './ResourceFactory.js';
|
|
|
5
5
|
export declare class FileResourceBuilder implements IResourceBuilder<OpInput<CreateFileOperation>, OpOutput<CreateFileOperation>, void> {
|
|
6
6
|
protected context: ResourceBuildContext<OpInput<CreateFileOperation>>;
|
|
7
7
|
constructor(context: ResourceBuildContext<OpInput<CreateFileOperation>>);
|
|
8
|
+
getReference(): Promise<string>;
|
|
8
9
|
build(resource: ResourceInfo<OpInput<CreateFileOperation>>, out?: Writable): Promise<{
|
|
9
10
|
input: {
|
|
10
11
|
path: string;
|
|
@@ -5,9 +5,12 @@ export class FileResourceBuilder {
|
|
|
5
5
|
constructor(context) {
|
|
6
6
|
this.context = context;
|
|
7
7
|
}
|
|
8
|
+
async getReference() {
|
|
9
|
+
return this.context.component.relative(this.context.config.params?.path || this.context.config.name);
|
|
10
|
+
}
|
|
8
11
|
async build(resource, out) {
|
|
9
12
|
const input = {
|
|
10
|
-
path: this.context.component.join(
|
|
13
|
+
path: await this.context.component.join(this.context.config.params?.path || this.context.config.name),
|
|
11
14
|
};
|
|
12
15
|
return {
|
|
13
16
|
input,
|
|
@@ -9,7 +9,7 @@ export class ResourceFactory {
|
|
|
9
9
|
static factor(type, context) {
|
|
10
10
|
const BuilderClass = this.types[type];
|
|
11
11
|
if (!BuilderClass) {
|
|
12
|
-
throw new Error(`Unknown resource type \`${type}
|
|
12
|
+
throw new Error(`Unknown resource type \`${type}\` (${context.config.id})`);
|
|
13
13
|
}
|
|
14
14
|
return new BuilderClass(context);
|
|
15
15
|
}
|
|
@@ -20,4 +20,5 @@ export declare abstract class AbstractResourceBuilder<I, O, R> implements IResou
|
|
|
20
20
|
publish?(resource: ResourceInfo<I>, out?: Writable): Promise<void>;
|
|
21
21
|
abstract _commit(resource: ResourceInfo<I>, output: O, reason: R): Promise<void>;
|
|
22
22
|
commit(resource: ResourceInfo<I>, output: O, reason: R): Promise<void>;
|
|
23
|
+
abstract getReference(): Promise<string>;
|
|
23
24
|
}
|
|
@@ -2,6 +2,12 @@ import { ResourceInfo } from '../../index.js';
|
|
|
2
2
|
import { Writable } from 'node:stream';
|
|
3
3
|
import { IOperation } from '../../operations/types.js';
|
|
4
4
|
export type IResourceBuilder<Input, Output, Reason> = {
|
|
5
|
+
/**
|
|
6
|
+
* Returns a string representing the resource to build
|
|
7
|
+
* Eg. the full name of a docker image (repo/imgname:tag)
|
|
8
|
+
* Eg. a file path
|
|
9
|
+
*/
|
|
10
|
+
getReference(): Promise<string>;
|
|
5
11
|
/**
|
|
6
12
|
* Returns input and operation required to actually
|
|
7
13
|
* build the resources.
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { ComponentFlavorConfig, IResourceConfig, ProjectFlavorConfig, TaskConfig } from '../config/types.js';
|
|
2
|
+
export type MaybeComponentIdentifiable<T> = T & {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
component?: string;
|
|
6
|
+
};
|
|
2
7
|
export type ComponentIdentifiable<T> = T & {
|
|
3
8
|
id: string;
|
|
4
9
|
name: string;
|
|
@@ -17,7 +22,7 @@ export type ComponentFlavorInfo = ComponentIdentifiable<ComponentFlavorConfig>;
|
|
|
17
22
|
export type ComponentFlavors = {
|
|
18
23
|
[k: string]: ComponentFlavorInfo;
|
|
19
24
|
};
|
|
20
|
-
export type TaskInfo =
|
|
25
|
+
export type TaskInfo = MaybeComponentIdentifiable<TaskConfig>;
|
|
21
26
|
export type Tasks = {
|
|
22
27
|
[k: string]: TaskInfo;
|
|
23
28
|
};
|
|
@@ -2,8 +2,6 @@ import { DepList } from './types.js';
|
|
|
2
2
|
export type CollectionConfig<IDK extends PropertyKey, DPK extends PropertyKey> = {
|
|
3
3
|
idField: IDK;
|
|
4
4
|
depField: DPK;
|
|
5
|
-
/** If true, throw when an item's id equals some other item's name (or vice versa). */
|
|
6
|
-
forbidIdNameCollision?: boolean;
|
|
7
5
|
};
|
|
8
6
|
export declare class EMBCollection<T extends Partial<Record<DPK, DepList>> & Record<IDK, string> & {
|
|
9
7
|
name: string;
|
|
@@ -11,7 +9,6 @@ export declare class EMBCollection<T extends Partial<Record<DPK, DepList>> & Rec
|
|
|
11
9
|
private items;
|
|
12
10
|
readonly idField: IDK;
|
|
13
11
|
readonly depField: DPK;
|
|
14
|
-
readonly forbidIdNameCollision: boolean;
|
|
15
12
|
private byId;
|
|
16
13
|
private byName;
|
|
17
14
|
constructor(items: Iterable<T>, cfg: CollectionConfig<IDK, DPK>);
|
|
@@ -3,7 +3,6 @@ export class EMBCollection {
|
|
|
3
3
|
items;
|
|
4
4
|
idField;
|
|
5
5
|
depField;
|
|
6
|
-
forbidIdNameCollision;
|
|
7
6
|
//
|
|
8
7
|
byId;
|
|
9
8
|
byName;
|
|
@@ -11,7 +10,6 @@ export class EMBCollection {
|
|
|
11
10
|
this.items = [];
|
|
12
11
|
this.idField = cfg.idField;
|
|
13
12
|
this.depField = cfg.depField;
|
|
14
|
-
this.forbidIdNameCollision = cfg.forbidIdNameCollision ?? true;
|
|
15
13
|
this.byId = new Map();
|
|
16
14
|
this.byName = new Map();
|
|
17
15
|
// single-pass validation state
|
|
@@ -32,18 +30,6 @@ export class EMBCollection {
|
|
|
32
30
|
this.byId.set(id, t);
|
|
33
31
|
seenIds.add(id);
|
|
34
32
|
}
|
|
35
|
-
// --- Optional validation: forbid id <-> name collisions ---
|
|
36
|
-
if (this.forbidIdNameCollision) {
|
|
37
|
-
if (seenNames.has(id)) {
|
|
38
|
-
const nameOwners = this.byName.get(id) ?? [];
|
|
39
|
-
const ownerIds = nameOwners.map((o) => o[this.idField]).join(', ');
|
|
40
|
-
collisions.push(`value \`${id}\` is an id of \`${name}\` and also a name of item(s) with id(s): [${ownerIds}]`);
|
|
41
|
-
}
|
|
42
|
-
if (seenIds.has(name)) {
|
|
43
|
-
const idOwner = this.byId.get(name);
|
|
44
|
-
collisions.push(`value \`${name}\` is a name of \`${t.name}\` and also an id of \`${idOwner.name}\``);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
33
|
// byName index
|
|
48
34
|
const list = this.byName.get(name);
|
|
49
35
|
if (list) {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { ComponentIdentifiable } from '../../index.js';
|
|
1
|
+
import { ComponentIdentifiable, MaybeComponentIdentifiable } from '../../index.js';
|
|
2
2
|
export * from './EMBCollection.js';
|
|
3
3
|
export * from './graph.js';
|
|
4
4
|
export * from './types.js';
|
|
5
|
-
export declare
|
|
6
|
-
[
|
|
7
|
-
}
|
|
5
|
+
export declare function toIdentifedHash<V, T extends Record<string, V>>(hash: T, parentName: string): {
|
|
6
|
+
[K in keyof T]: ComponentIdentifiable<T[K]>;
|
|
7
|
+
};
|
|
8
|
+
export declare function toIdentifedHash<V, T extends Record<string, V>>(hash: T, parentName?: undefined): {
|
|
9
|
+
[K in keyof T]: MaybeComponentIdentifiable<T[K]>;
|
|
10
|
+
};
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
export * from './EMBCollection.js';
|
|
2
2
|
export * from './graph.js';
|
|
3
3
|
export * from './types.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
// implementation
|
|
5
|
+
export function toIdentifedHash(hash, parentName) {
|
|
6
|
+
const out = {};
|
|
7
|
+
for (const key in hash) {
|
|
8
|
+
if (!Object.hasOwn(hash, key)) {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
const value = hash[key];
|
|
12
|
+
out[key] = {
|
|
7
13
|
...value,
|
|
8
|
-
id: `${parentName}:${key}
|
|
14
|
+
id: parentName ? `${parentName}:${key}` : key,
|
|
9
15
|
name: key,
|
|
10
|
-
component: parentName,
|
|
16
|
+
...(parentName ? { component: parentName } : {}),
|
|
11
17
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type Docker from 'dockerode';
|
|
2
2
|
import { Monorepo } from './monorepo/index.js';
|
|
3
|
+
import { DockerComposeClient } from './docker/index.js';
|
|
3
4
|
/**
|
|
4
5
|
* The context is meant to be what all plugins can decorate
|
|
5
6
|
* to install their own things
|
|
@@ -8,6 +9,7 @@ import { Monorepo } from './monorepo/index.js';
|
|
|
8
9
|
* and install here things that to be accessible by operations during a CLI run
|
|
9
10
|
*/
|
|
10
11
|
export interface EmbContext {
|
|
12
|
+
compose: DockerComposeClient;
|
|
11
13
|
docker: Docker;
|
|
12
14
|
monorepo: Monorepo;
|
|
13
15
|
}
|