@enspirit/emb 0.3.1 → 0.4.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 +1 -1
- package/dist/src/cli/abstract/BaseCommand.js +2 -2
- package/dist/src/cli/commands/down.js +2 -2
- package/dist/src/config/index.d.ts +4 -4
- package/dist/src/config/index.js +4 -15
- package/dist/src/config/types.d.ts +1 -4
- package/dist/src/config/validation.d.ts +2 -2
- package/dist/src/config/validation.js +1 -2
- package/dist/src/docker/compose/operations/ComposeDownOperation.d.ts +3 -2
- package/dist/src/docker/compose/operations/ComposeDownOperation.js +4 -2
- package/dist/src/docker/compose/operations/ComposeUpOperation.js +2 -2
- package/dist/src/docker/operations/images/BuildImageOperation.js +12 -7
- package/dist/src/monorepo/component.d.ts +2 -1
- package/dist/src/monorepo/component.js +5 -2
- package/dist/src/monorepo/config.d.ts +5 -5
- package/dist/src/monorepo/monorepo.d.ts +7 -6
- package/dist/src/monorepo/monorepo.js +28 -13
- package/dist/src/monorepo/operations/shell/ExecuteLocalCommandOperation.d.ts +3 -2
- package/dist/src/monorepo/operations/shell/ExecuteLocalCommandOperation.js +4 -1
- package/dist/src/monorepo/operations/tasks/RunTasksOperation.d.ts +1 -1
- package/dist/src/monorepo/operations/tasks/RunTasksOperation.js +9 -4
- package/dist/src/monorepo/plugins/AutoDockerPlugin.js +1 -1
- package/dist/src/monorepo/plugins/EmbfileLoaderPlugin.js +2 -2
- package/dist/src/utils/TemplateExpander.d.ts +9 -6
- package/dist/src/utils/TemplateExpander.js +17 -23
- package/oclif.manifest.json +117 -117
- package/package.json +30 -9
- package/dist/src/config/convert.d.ts +0 -2
- package/dist/src/config/convert.js +0 -18
|
@@ -6,11 +6,13 @@ import { MonorepoConfig } from './config.js';
|
|
|
6
6
|
import { getPlugin } from './plugins/index.js';
|
|
7
7
|
import { EMBStore } from './store/index.js';
|
|
8
8
|
export class Monorepo {
|
|
9
|
+
_rootDir;
|
|
9
10
|
defaultFlavor;
|
|
10
11
|
_config;
|
|
11
12
|
_store;
|
|
12
13
|
initialized = false;
|
|
13
|
-
constructor(config, defaultFlavor = 'default') {
|
|
14
|
+
constructor(config, _rootDir, defaultFlavor = 'default') {
|
|
15
|
+
this._rootDir = _rootDir;
|
|
14
16
|
this.defaultFlavor = defaultFlavor;
|
|
15
17
|
this._config = new MonorepoConfig(config);
|
|
16
18
|
}
|
|
@@ -27,7 +29,9 @@ export class Monorepo {
|
|
|
27
29
|
return this._config.project.name;
|
|
28
30
|
}
|
|
29
31
|
get rootDir() {
|
|
30
|
-
return this._config.project.rootDir
|
|
32
|
+
return this._config.project.rootDir
|
|
33
|
+
? join(this._rootDir, this._config.project.rootDir)
|
|
34
|
+
: this._rootDir;
|
|
31
35
|
}
|
|
32
36
|
get currentFlavor() {
|
|
33
37
|
return this.defaultFlavor;
|
|
@@ -104,7 +108,8 @@ export class Monorepo {
|
|
|
104
108
|
get vars() {
|
|
105
109
|
return this._config.vars;
|
|
106
110
|
}
|
|
107
|
-
|
|
111
|
+
// Helper to expand a record of strings
|
|
112
|
+
async expand(toExpand, expander = new TemplateExpander()) {
|
|
108
113
|
const options = {
|
|
109
114
|
default: 'vars',
|
|
110
115
|
sources: {
|
|
@@ -112,10 +117,7 @@ export class Monorepo {
|
|
|
112
117
|
vars: this.vars,
|
|
113
118
|
},
|
|
114
119
|
};
|
|
115
|
-
|
|
116
|
-
return expander.expand(strOrRecord, options);
|
|
117
|
-
}
|
|
118
|
-
return expander.expandRecord(strOrRecord, options);
|
|
120
|
+
return expander.expandRecord(toExpand, options);
|
|
119
121
|
}
|
|
120
122
|
async installStore(store) {
|
|
121
123
|
this._store = store || new EMBStore(this);
|
|
@@ -157,20 +159,33 @@ export class Monorepo {
|
|
|
157
159
|
}
|
|
158
160
|
// Helper to build relative path to the root dir
|
|
159
161
|
join(...paths) {
|
|
160
|
-
return join(this.
|
|
162
|
+
return join(this.rootDir, ...paths);
|
|
161
163
|
}
|
|
162
164
|
async run(operation, args) {
|
|
163
165
|
return operation.run(args);
|
|
164
166
|
}
|
|
167
|
+
async expandPatches(patches) {
|
|
168
|
+
const expanded = Promise.all(patches.map(async (patch) => {
|
|
169
|
+
if (!('value' in patch)) {
|
|
170
|
+
return patch;
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
...patch,
|
|
174
|
+
value: await this.expand(patch.value),
|
|
175
|
+
};
|
|
176
|
+
}));
|
|
177
|
+
return expanded;
|
|
178
|
+
}
|
|
165
179
|
async withFlavor(flavorName) {
|
|
166
|
-
const patches = this._config.flavor(flavorName).patches || [];
|
|
180
|
+
const patches = await this.expandPatches(this._config.flavor(flavorName).patches || []);
|
|
167
181
|
const original = this._config.toJSON();
|
|
168
182
|
const errors = jsonpatch.validate(patches || [], original);
|
|
169
183
|
if (errors) {
|
|
170
184
|
throw errors;
|
|
171
185
|
}
|
|
172
|
-
const withComponentPatches = this.components.reduce((
|
|
173
|
-
const
|
|
186
|
+
const withComponentPatches = await this.components.reduce(async (pConfig, cmp) => {
|
|
187
|
+
const config = await pConfig;
|
|
188
|
+
const componentPatches = await this.expandPatches(cmp.flavor(flavorName, false)?.patches || []);
|
|
174
189
|
const errors = jsonpatch.validate(componentPatches || [], config.components[cmp.name]);
|
|
175
190
|
if (errors) {
|
|
176
191
|
throw errors;
|
|
@@ -179,12 +194,12 @@ export class Monorepo {
|
|
|
179
194
|
return jsonpatch.applyReducer(doc, patch, index);
|
|
180
195
|
}, config.components[cmp.name]);
|
|
181
196
|
return config;
|
|
182
|
-
}, original);
|
|
197
|
+
}, Promise.resolve(original));
|
|
183
198
|
const withGlobalPatches = patches.reduce((doc, patch, index) => {
|
|
184
199
|
return jsonpatch.applyReducer(doc, patch, index);
|
|
185
200
|
}, withComponentPatches);
|
|
186
201
|
const newConfig = new MonorepoConfig(withGlobalPatches);
|
|
187
|
-
const repo = new Monorepo(newConfig, flavorName);
|
|
202
|
+
const repo = new Monorepo(newConfig, this._rootDir, flavorName);
|
|
188
203
|
await repo.installStore();
|
|
189
204
|
await repo.installEnv();
|
|
190
205
|
return repo;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Readable } from 'node:stream';
|
|
1
|
+
import { Readable, Writable } from 'node:stream';
|
|
2
2
|
import * as z from 'zod';
|
|
3
3
|
import { AbstractOperation } from '../../../operations/index.js';
|
|
4
4
|
/**
|
|
@@ -10,7 +10,8 @@ declare const schema: z.ZodObject<{
|
|
|
10
10
|
workingDir: z.ZodOptional<z.ZodString>;
|
|
11
11
|
}, z.core.$strip>;
|
|
12
12
|
export declare class ExecuteLocalCommandOperation extends AbstractOperation<typeof schema, Readable> {
|
|
13
|
-
|
|
13
|
+
protected out: Writable;
|
|
14
|
+
constructor(out: Writable);
|
|
14
15
|
protected _run(input: z.input<typeof schema>): Promise<Readable>;
|
|
15
16
|
}
|
|
16
17
|
export {};
|
|
@@ -16,8 +16,10 @@ const schema = z.object({
|
|
|
16
16
|
.describe('The working directory for the exec process inside the container'),
|
|
17
17
|
});
|
|
18
18
|
export class ExecuteLocalCommandOperation extends AbstractOperation {
|
|
19
|
-
|
|
19
|
+
out;
|
|
20
|
+
constructor(out) {
|
|
20
21
|
super(schema);
|
|
22
|
+
this.out = out;
|
|
21
23
|
}
|
|
22
24
|
async _run(input) {
|
|
23
25
|
const process = execa(input.script, {
|
|
@@ -25,6 +27,7 @@ export class ExecuteLocalCommandOperation extends AbstractOperation {
|
|
|
25
27
|
cwd: input.workingDir,
|
|
26
28
|
shell: true,
|
|
27
29
|
});
|
|
30
|
+
process.all.pipe(this.out);
|
|
28
31
|
return process.all;
|
|
29
32
|
}
|
|
30
33
|
}
|
|
@@ -16,5 +16,5 @@ export type TaskWithScript = TaskInfo & {
|
|
|
16
16
|
export declare class RunTasksOperation implements IOperation<RunTasksOperationParams, Array<TaskInfo>> {
|
|
17
17
|
run(params: RunTasksOperationParams): Promise<Array<TaskInfo>>;
|
|
18
18
|
protected runDocker(task: TaskWithScript, out?: Writable): Promise<void>;
|
|
19
|
-
protected runLocal(task: TaskWithScript): Promise<import("stream").Readable>;
|
|
19
|
+
protected runLocal(task: TaskWithScript, out: Writable): Promise<import("stream").Readable>;
|
|
20
20
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getContext } from '../../../index.js';
|
|
2
|
+
import { PassThrough } from 'node:stream';
|
|
2
3
|
import { ComposeExecOperation } from '../../../docker/index.js';
|
|
3
4
|
import { EMBCollection, findRunOrder, taskManagerFactory, } from '../../index.js';
|
|
4
5
|
import { ExecuteLocalCommandOperation } from '../index.js';
|
|
@@ -33,12 +34,16 @@ export class RunTasksOperation {
|
|
|
33
34
|
if (executor === ExecutorType.container && !task.component) {
|
|
34
35
|
throw new Error('Cannot use the container executor with global tasks');
|
|
35
36
|
}
|
|
37
|
+
const tee = new PassThrough();
|
|
38
|
+
const logFile = await monorepo.store.createWriteStream(`logs/tasks/${task.id}.logs`);
|
|
39
|
+
tee.pipe(listrTask.stdout());
|
|
40
|
+
tee.pipe(logFile);
|
|
36
41
|
switch (executor) {
|
|
37
42
|
case ExecutorType.container: {
|
|
38
|
-
return this.runDocker(task,
|
|
43
|
+
return this.runDocker(task, tee);
|
|
39
44
|
}
|
|
40
45
|
case ExecutorType.local: {
|
|
41
|
-
return this.runLocal(task);
|
|
46
|
+
return this.runLocal(task, tee);
|
|
42
47
|
}
|
|
43
48
|
default: {
|
|
44
49
|
throw new Error(`Unssuported executor type: ${executor}`);
|
|
@@ -60,12 +65,12 @@ export class RunTasksOperation {
|
|
|
60
65
|
command: task.script,
|
|
61
66
|
});
|
|
62
67
|
}
|
|
63
|
-
async runLocal(task) {
|
|
68
|
+
async runLocal(task, out) {
|
|
64
69
|
const { monorepo } = getContext();
|
|
65
70
|
const cwd = task.component
|
|
66
71
|
? monorepo.join(monorepo.component(task.component).rootDir)
|
|
67
72
|
: monorepo.rootDir;
|
|
68
|
-
return monorepo.run(new ExecuteLocalCommandOperation(), {
|
|
73
|
+
return monorepo.run(new ExecuteLocalCommandOperation(out), {
|
|
69
74
|
script: task.script,
|
|
70
75
|
workingDir: cwd,
|
|
71
76
|
});
|
|
@@ -19,7 +19,7 @@ export class AutoDockerPlugin extends AbstractPlugin {
|
|
|
19
19
|
async extendConfig(config) {
|
|
20
20
|
const files = await glob(this.config.glob || AutoDockerPluginDefaultOptions.glob, {
|
|
21
21
|
...this.config,
|
|
22
|
-
cwd:
|
|
22
|
+
cwd: this.monorepo.rootDir,
|
|
23
23
|
});
|
|
24
24
|
const overrides = files.reduce((cmps, path) => {
|
|
25
25
|
const name = dirname(path);
|
|
@@ -23,13 +23,13 @@ export class EmbfileLoaderPlugin extends AbstractPlugin {
|
|
|
23
23
|
async extendConfig(config) {
|
|
24
24
|
const files = await glob(this.config.glob, {
|
|
25
25
|
...this.config,
|
|
26
|
-
cwd:
|
|
26
|
+
cwd: this.monorepo.rootDir,
|
|
27
27
|
});
|
|
28
28
|
const newConfig = await files.reduce(async (pConfig, path) => {
|
|
29
29
|
const config = await pConfig;
|
|
30
30
|
const rootDir = dirname(path);
|
|
31
31
|
const name = basename(rootDir);
|
|
32
|
-
const embfile = await join(
|
|
32
|
+
const embfile = await join(this.monorepo.rootDir, path);
|
|
33
33
|
const component = await validateEmbfile(embfile);
|
|
34
34
|
const original = config.components[name];
|
|
35
35
|
const newComponent = deepmerge()(original || {}, {
|
|
@@ -7,16 +7,19 @@ export type ExpansionHistory = {
|
|
|
7
7
|
value: unknown;
|
|
8
8
|
variable: string;
|
|
9
9
|
};
|
|
10
|
+
export type Expandable = readonly Expandable[] | string | {
|
|
11
|
+
readonly [k: string]: Expandable;
|
|
12
|
+
};
|
|
13
|
+
type ExpandResult<T> = T extends string ? string : T extends readonly (infer U)[] ? ReadonlyArray<ExpandResult<U>> : T extends {
|
|
14
|
+
readonly [K in keyof T]: Expandable;
|
|
15
|
+
} ? {
|
|
16
|
+
readonly [K in keyof T]: ExpandResult<T[K]>;
|
|
17
|
+
} : T;
|
|
10
18
|
export declare class TemplateExpander {
|
|
11
|
-
/**
|
|
12
|
-
* Keep track of the sources used for expansions
|
|
13
|
-
* (track source, name, final value)
|
|
14
|
-
*/
|
|
15
19
|
private expansions;
|
|
16
20
|
get expansionCount(): number;
|
|
17
21
|
expand(str: string, options?: ExpandOptions): Promise<string>;
|
|
18
|
-
expandRecord<
|
|
19
|
-
expandRecord<R extends Array<unknown>>(record: R, options: ExpandOptions): Promise<R>;
|
|
22
|
+
expandRecord<T extends Expandable>(record: T, options: ExpandOptions): Promise<ExpandResult<T>>;
|
|
20
23
|
private track;
|
|
21
24
|
}
|
|
22
25
|
export {};
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
const TPL_REGEX = /(?<!\\)\${(?:(\w+):)?(\w+)(?::-(.*?))?}/g;
|
|
2
2
|
export class TemplateExpander {
|
|
3
|
-
/**
|
|
4
|
-
* Keep track of the sources used for expansions
|
|
5
|
-
* (track source, name, final value)
|
|
6
|
-
*/
|
|
7
3
|
expansions = [];
|
|
8
4
|
get expansionCount() {
|
|
9
5
|
return this.expansions.length;
|
|
10
6
|
}
|
|
11
7
|
async expand(str, options = {}) {
|
|
12
|
-
return (
|
|
8
|
+
return (str || '')
|
|
13
9
|
.toString()
|
|
14
|
-
// Expand variables
|
|
15
10
|
.replaceAll(TPL_REGEX, (match, source, key, fallback) => {
|
|
16
|
-
const src = source
|
|
11
|
+
const src = source ?? options.default ?? '';
|
|
17
12
|
const provider = options.sources?.[src];
|
|
18
13
|
if (!provider) {
|
|
19
14
|
if (fallback !== undefined) {
|
|
@@ -22,34 +17,33 @@ export class TemplateExpander {
|
|
|
22
17
|
throw new Error(`Invalid expand provider '${source}' ('${match}')`);
|
|
23
18
|
}
|
|
24
19
|
const val = provider[key];
|
|
25
|
-
// fallback is undefined when not even the :- is present
|
|
26
|
-
// we consider a variable like ${source:key:-} like the information
|
|
27
|
-
// that the variable can be an empty string if not present
|
|
28
20
|
if (!val && fallback === undefined) {
|
|
29
21
|
throw new Error(`Could not expand '${match}' and no default value provided`);
|
|
30
22
|
}
|
|
31
23
|
if (val !== undefined && val !== null) {
|
|
32
24
|
return this.track(src, key, val);
|
|
33
25
|
}
|
|
34
|
-
return this.track(src, key, fallback
|
|
26
|
+
return this.track(src, key, fallback ?? '');
|
|
35
27
|
})
|
|
36
|
-
|
|
37
|
-
.replaceAll('\\${', '${'));
|
|
28
|
+
.replaceAll('\\${', '${');
|
|
38
29
|
}
|
|
39
|
-
async expandRecord(record, options
|
|
30
|
+
async expandRecord(record, options) {
|
|
31
|
+
if (typeof record === 'string') {
|
|
32
|
+
const out = await this.expand(record, options);
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
40
35
|
if (Array.isArray(record)) {
|
|
41
|
-
|
|
36
|
+
const out = await Promise.all(record.map((v) => this.expandRecord(v, options)));
|
|
37
|
+
return out;
|
|
42
38
|
}
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return previous;
|
|
49
|
-
}, Promise.resolve({}));
|
|
39
|
+
const entries = await Promise.all(Object.entries(record).map(async ([k, v]) => {
|
|
40
|
+
const expandedValue = await this.expandRecord(v, options);
|
|
41
|
+
return [k, expandedValue];
|
|
42
|
+
}));
|
|
43
|
+
return Object.fromEntries(entries);
|
|
50
44
|
}
|
|
51
45
|
track(source, variable, value) {
|
|
52
46
|
this.expansions.push({ source, value, variable });
|
|
53
|
-
return value;
|
|
47
|
+
return String(value);
|
|
54
48
|
}
|
|
55
49
|
}
|