@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.
Files changed (29) hide show
  1. package/README.md +1 -1
  2. package/dist/src/cli/abstract/BaseCommand.js +2 -2
  3. package/dist/src/cli/commands/down.js +2 -2
  4. package/dist/src/config/index.d.ts +4 -4
  5. package/dist/src/config/index.js +4 -15
  6. package/dist/src/config/types.d.ts +1 -4
  7. package/dist/src/config/validation.d.ts +2 -2
  8. package/dist/src/config/validation.js +1 -2
  9. package/dist/src/docker/compose/operations/ComposeDownOperation.d.ts +3 -2
  10. package/dist/src/docker/compose/operations/ComposeDownOperation.js +4 -2
  11. package/dist/src/docker/compose/operations/ComposeUpOperation.js +2 -2
  12. package/dist/src/docker/operations/images/BuildImageOperation.js +12 -7
  13. package/dist/src/monorepo/component.d.ts +2 -1
  14. package/dist/src/monorepo/component.js +5 -2
  15. package/dist/src/monorepo/config.d.ts +5 -5
  16. package/dist/src/monorepo/monorepo.d.ts +7 -6
  17. package/dist/src/monorepo/monorepo.js +28 -13
  18. package/dist/src/monorepo/operations/shell/ExecuteLocalCommandOperation.d.ts +3 -2
  19. package/dist/src/monorepo/operations/shell/ExecuteLocalCommandOperation.js +4 -1
  20. package/dist/src/monorepo/operations/tasks/RunTasksOperation.d.ts +1 -1
  21. package/dist/src/monorepo/operations/tasks/RunTasksOperation.js +9 -4
  22. package/dist/src/monorepo/plugins/AutoDockerPlugin.js +1 -1
  23. package/dist/src/monorepo/plugins/EmbfileLoaderPlugin.js +2 -2
  24. package/dist/src/utils/TemplateExpander.d.ts +9 -6
  25. package/dist/src/utils/TemplateExpander.js +17 -23
  26. package/oclif.manifest.json +117 -117
  27. package/package.json +30 -9
  28. package/dist/src/config/convert.d.ts +0 -2
  29. 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
- async expand(strOrRecord, expander = new TemplateExpander()) {
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
- if (typeof strOrRecord === 'string') {
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._config.project.rootDir, ...paths);
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((config, cmp) => {
173
- const componentPatches = cmp.flavor(flavorName, false)?.patches || [];
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
- constructor();
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
- constructor() {
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, listrTask.stdout());
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: config.project.rootDir,
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: config.project.rootDir,
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(config.project.rootDir, path);
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<R extends Record<string, unknown>>(record: R, options: ExpandOptions): Promise<R>;
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 ((str || '')
8
+ return (str || '')
13
9
  .toString()
14
- // Expand variables
15
10
  .replaceAll(TPL_REGEX, (match, source, key, fallback) => {
16
- const src = source || options.default;
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
- // Unescape non-variables left
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
- return Promise.all(record.map((v) => this.expand(v, options)));
36
+ const out = await Promise.all(record.map((v) => this.expandRecord(v, options)));
37
+ return out;
42
38
  }
43
- return Object.entries(record).reduce(async (vars, [name, str]) => {
44
- const previous = await vars;
45
- previous[name] = await (typeof str === 'object'
46
- ? this.expandRecord(str, options)
47
- : this.expand(str, options));
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
  }