@enspirit/emb 0.0.2 → 0.0.4

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 (164) hide show
  1. package/README.md +190 -164
  2. package/bin/run.js +8 -2
  3. package/dist/src/cli/abstract/FlavouredCommand.d.ts +12 -0
  4. package/dist/src/cli/abstract/FlavouredCommand.js +40 -0
  5. package/dist/src/cli/abstract/index.d.ts +1 -0
  6. package/dist/src/cli/abstract/index.js +1 -0
  7. package/dist/src/cli/commands/clean.d.ts +15 -0
  8. package/dist/src/cli/commands/clean.js +29 -0
  9. package/dist/src/cli/commands/components/build.d.ts +11 -0
  10. package/dist/src/cli/commands/components/build.js +24 -0
  11. package/dist/src/cli/commands/components/index.d.ts +14 -0
  12. package/dist/src/cli/commands/components/index.js +44 -0
  13. package/dist/src/cli/commands/config/print.d.ts +13 -0
  14. package/dist/src/cli/commands/config/print.js +16 -0
  15. package/dist/src/cli/commands/containers/index.d.ts +12 -0
  16. package/dist/src/cli/commands/containers/index.js +66 -0
  17. package/dist/src/cli/commands/containers/prune.d.ts +8 -0
  18. package/dist/src/cli/commands/containers/prune.js +23 -0
  19. package/dist/src/cli/commands/down.d.ts +8 -0
  20. package/dist/src/cli/commands/down.js +42 -0
  21. package/dist/src/cli/commands/images/delete.d.ts +10 -0
  22. package/dist/src/cli/commands/images/delete.js +44 -0
  23. package/dist/src/cli/commands/images/index.d.ts +17 -0
  24. package/dist/src/cli/commands/images/index.js +59 -0
  25. package/dist/src/cli/commands/images/prune.d.ts +11 -0
  26. package/dist/src/cli/commands/images/prune.js +35 -0
  27. package/dist/src/cli/commands/run/index.d.ts +10 -0
  28. package/dist/src/cli/commands/run/index.js +49 -0
  29. package/dist/src/cli/commands/tasks/index.d.ts +9 -0
  30. package/dist/src/cli/commands/tasks/index.js +23 -0
  31. package/dist/src/cli/commands/tasks/run.d.ts +16 -0
  32. package/dist/src/cli/commands/tasks/run.js +113 -0
  33. package/dist/src/cli/commands/up.d.ts +10 -0
  34. package/dist/src/cli/commands/up.js +49 -0
  35. package/dist/src/cli/constants.d.ts +2 -0
  36. package/dist/src/cli/constants.js +6 -0
  37. package/dist/src/cli/hooks/init.d.ts +3 -0
  38. package/dist/src/cli/hooks/init.js +27 -0
  39. package/dist/src/cli/index.d.ts +3 -0
  40. package/dist/src/cli/index.js +3 -0
  41. package/dist/src/config/convert.d.ts +5 -0
  42. package/dist/src/config/convert.js +48 -0
  43. package/dist/src/config/index.d.ts +6 -0
  44. package/dist/src/config/index.js +25 -0
  45. package/dist/src/config/schema.d.ts +102 -0
  46. package/dist/src/config/schema.js +7 -0
  47. package/dist/src/config/schema.json +209 -0
  48. package/dist/src/config/types.d.ts +43 -0
  49. package/dist/src/config/types.js +1 -0
  50. package/dist/src/config/validation.d.ts +1 -0
  51. package/dist/src/config/validation.js +26 -0
  52. package/dist/src/context.d.ts +3 -0
  53. package/dist/src/context.js +7 -0
  54. package/dist/src/docker/compose/index.d.ts +7 -0
  55. package/dist/src/docker/compose/index.js +13 -0
  56. package/dist/src/docker/containers/getContainer.d.ts +2 -0
  57. package/dist/src/docker/containers/getContainer.js +5 -0
  58. package/dist/src/docker/containers/index.d.ts +1 -0
  59. package/dist/src/docker/containers/index.js +1 -0
  60. package/dist/src/docker/images/buildImage.d.ts +19 -0
  61. package/dist/src/docker/images/buildImage.js +64 -0
  62. package/dist/src/docker/images/deleteImage.d.ts +5 -0
  63. package/dist/src/docker/images/deleteImage.js +6 -0
  64. package/dist/src/docker/images/index.d.ts +4 -0
  65. package/dist/src/docker/images/index.js +4 -0
  66. package/dist/src/docker/images/listImages.d.ts +2 -0
  67. package/dist/src/docker/images/listImages.js +8 -0
  68. package/dist/src/docker/images/pruneImages.d.ts +6 -0
  69. package/dist/src/docker/images/pruneImages.js +8 -0
  70. package/dist/src/docker/index.d.ts +7 -0
  71. package/dist/src/docker/index.js +7 -0
  72. package/dist/src/docker/operations/containers/ListContainersOperation.d.ts +18 -0
  73. package/dist/src/docker/operations/containers/ListContainersOperation.js +44 -0
  74. package/dist/src/docker/operations/containers/PruneContainersOperation.d.ts +16 -0
  75. package/dist/src/docker/operations/containers/PruneContainersOperation.js +33 -0
  76. package/dist/src/docker/operations/containers/index.d.ts +2 -0
  77. package/dist/src/docker/operations/containers/index.js +2 -0
  78. package/dist/src/docker/operations/images/BuildImageOperation.d.ts +20 -0
  79. package/dist/src/docker/operations/images/BuildImageOperation.js +69 -0
  80. package/dist/src/docker/operations/images/ListImagesOperation.d.ts +17 -0
  81. package/dist/src/docker/operations/images/ListImagesOperation.js +38 -0
  82. package/dist/src/docker/operations/images/PruneImagesOperation.d.ts +16 -0
  83. package/dist/src/docker/operations/images/PruneImagesOperation.js +33 -0
  84. package/dist/src/docker/operations/images/index.d.ts +3 -0
  85. package/dist/src/docker/operations/images/index.js +3 -0
  86. package/dist/src/docker/operations/index.d.ts +2 -0
  87. package/dist/src/docker/operations/index.js +2 -0
  88. package/dist/src/docker/protobuf/control.proto +48 -0
  89. package/dist/src/docker/protobuf/index.d.ts +5 -0
  90. package/dist/src/docker/protobuf/index.js +29 -0
  91. package/dist/src/docker/types.d.ts +14 -0
  92. package/dist/src/docker/types.js +1 -0
  93. package/dist/src/docker/utils.d.ts +7 -0
  94. package/dist/src/docker/utils.js +10 -0
  95. package/dist/src/executors/docker.d.ts +6 -0
  96. package/dist/src/executors/docker.js +14 -0
  97. package/dist/src/executors/index.d.ts +6 -0
  98. package/dist/src/executors/index.js +7 -0
  99. package/dist/src/executors/shell.d.ts +2 -0
  100. package/dist/src/executors/shell.js +14 -0
  101. package/dist/src/executors/types.d.ts +8 -0
  102. package/dist/src/executors/types.js +1 -0
  103. package/dist/src/index.d.ts +3 -0
  104. package/dist/src/index.js +3 -0
  105. package/dist/src/monorepo/component.d.ts +24 -0
  106. package/dist/src/monorepo/component.js +77 -0
  107. package/dist/src/monorepo/config.d.ts +16 -0
  108. package/dist/src/monorepo/config.js +66 -0
  109. package/dist/src/monorepo/index.d.ts +7 -0
  110. package/dist/src/monorepo/index.js +7 -0
  111. package/dist/src/monorepo/monorepo.d.ts +29 -0
  112. package/dist/src/monorepo/monorepo.js +106 -0
  113. package/dist/src/monorepo/operations/components/BuildComponentsOperation.d.ts +11 -0
  114. package/dist/src/monorepo/operations/components/BuildComponentsOperation.js +144 -0
  115. package/dist/src/monorepo/operations/components/index.d.ts +1 -0
  116. package/dist/src/monorepo/operations/components/index.js +1 -0
  117. package/dist/src/monorepo/operations/index.d.ts +1 -0
  118. package/dist/src/monorepo/operations/index.js +1 -0
  119. package/dist/src/monorepo/plugins/ComponentsDiscover.d.ts +6 -0
  120. package/dist/src/monorepo/plugins/ComponentsDiscover.js +30 -0
  121. package/dist/src/monorepo/plugins/DotEnvPlugin.d.ts +5 -0
  122. package/dist/src/monorepo/plugins/DotEnvPlugin.js +11 -0
  123. package/dist/src/monorepo/plugins/index.d.ts +7 -0
  124. package/dist/src/monorepo/plugins/index.js +20 -0
  125. package/dist/src/monorepo/plugins/plugin.d.ts +15 -0
  126. package/dist/src/monorepo/plugins/plugin.js +12 -0
  127. package/dist/src/monorepo/project.d.ts +6 -0
  128. package/dist/src/monorepo/project.js +8 -0
  129. package/dist/src/monorepo/store/index.d.ts +20 -0
  130. package/dist/src/monorepo/store/index.js +65 -0
  131. package/dist/src/monorepo/types.d.ts +7 -0
  132. package/dist/src/monorepo/types.js +1 -0
  133. package/dist/src/monorepo/utils/findBuildOrder.d.ts +2 -0
  134. package/dist/src/monorepo/utils/findBuildOrder.js +41 -0
  135. package/dist/src/monorepo/utils/index.d.ts +1 -0
  136. package/dist/src/monorepo/utils/index.js +1 -0
  137. package/dist/src/operations/abstract/AbstractOperation.d.ts +10 -0
  138. package/dist/src/operations/abstract/AbstractOperation.js +13 -0
  139. package/dist/src/operations/abstract/index.d.ts +1 -0
  140. package/dist/src/operations/abstract/index.js +1 -0
  141. package/dist/src/operations/index.d.ts +2 -0
  142. package/dist/src/operations/index.js +2 -0
  143. package/dist/src/operations/types.d.ts +3 -0
  144. package/dist/src/operations/types.js +1 -0
  145. package/dist/src/prerequisites/FilePrerequisitePlugin.d.ts +7 -0
  146. package/dist/src/prerequisites/FilePrerequisitePlugin.js +41 -0
  147. package/dist/src/prerequisites/GitPrerequisitePlugin.d.ts +5 -0
  148. package/dist/src/prerequisites/GitPrerequisitePlugin.js +17 -0
  149. package/dist/src/prerequisites/index.d.ts +3 -0
  150. package/dist/src/prerequisites/index.js +3 -0
  151. package/dist/src/prerequisites/types.d.ts +46 -0
  152. package/dist/src/prerequisites/types.js +24 -0
  153. package/dist/src/types.d.ts +13 -0
  154. package/dist/src/types.js +1 -0
  155. package/dist/src/utils/TemplateExpander.d.ts +21 -0
  156. package/dist/src/utils/TemplateExpander.js +53 -0
  157. package/dist/src/utils/deepMergeArray.d.ts +1 -0
  158. package/dist/src/utils/deepMergeArray.js +19 -0
  159. package/dist/src/utils/index.d.ts +3 -0
  160. package/dist/src/utils/index.js +3 -0
  161. package/dist/src/utils/time.d.ts +2 -0
  162. package/dist/src/utils/time.js +19 -0
  163. package/oclif.manifest.json +572 -2
  164. package/package.json +7 -9
@@ -0,0 +1,29 @@
1
+ import { dirname, join } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import protobuf from 'protobufjs';
4
+ let control;
5
+ export const loadControl = async () => {
6
+ if (control) {
7
+ return control;
8
+ }
9
+ const path = join(dirname(fileURLToPath(import.meta.url)), './control.proto');
10
+ control = await protobuf.load(path);
11
+ return control;
12
+ };
13
+ export const decodeBuildkitStatusResponse = async (str) => {
14
+ const control = await loadControl();
15
+ const buffer = typeof str === 'string' ? Buffer.from(str, 'base64') : str;
16
+ const uint8 = new Uint8Array(buffer);
17
+ const Trace = control.lookupType('moby.buildkit.v1.StatusResponse');
18
+ const message = Trace.decode(uint8);
19
+ const object = Trace.toObject(message, {
20
+ arrays: true,
21
+ bytes: String,
22
+ defaults: true,
23
+ enums: String,
24
+ longs: String,
25
+ objects: true,
26
+ oneofs: true,
27
+ });
28
+ return object;
29
+ };
@@ -0,0 +1,14 @@
1
+ import { FilePrerequisite } from '../prerequisites/index.js';
2
+ export type EnvVariable = {
3
+ name: string;
4
+ };
5
+ export interface DockerComponentBuild {
6
+ buildArgs?: Record<string, string>;
7
+ context: string;
8
+ dockerfile: string;
9
+ labels?: Record<string, string>;
10
+ name: string;
11
+ prerequisites: Array<FilePrerequisite>;
12
+ tag: string;
13
+ target?: string;
14
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { Component } from '../monorepo/index.js';
2
+ export declare const shortId: (id: string) => string;
3
+ /**
4
+ * This is too naive and will need better logic to ensure
5
+ * we take care of flavors etc
6
+ */
7
+ export declare const getSentinelFile: (component: Component) => string;
@@ -0,0 +1,10 @@
1
+ export const shortId = (id) => {
2
+ return id.slice(0, 12);
3
+ };
4
+ /**
5
+ * This is too naive and will need better logic to ensure
6
+ * we take care of flavors etc
7
+ */
8
+ export const getSentinelFile = (component) => {
9
+ return `sentinels/docker/build/${component.name}.built`;
10
+ };
@@ -0,0 +1,6 @@
1
+ import { Container } from 'dockerode';
2
+ import { Executor, ExecutorRunOptions } from './types.js';
3
+ export type DockerExecutorRunOptions = ExecutorRunOptions & {
4
+ container: Container;
5
+ };
6
+ export declare const dockerExecutor: Executor<DockerExecutorRunOptions>;
@@ -0,0 +1,14 @@
1
+ export const dockerExecutor = {
2
+ async run(script, options) {
3
+ const exec = await options.container.exec({
4
+ AttachStderr: true,
5
+ AttachStdout: true,
6
+ Cmd: ['bash', '-c', script],
7
+ });
8
+ const stream = await exec.start({});
9
+ if (options.out) {
10
+ options.container.modem.demuxStream(stream, options.out, options.out);
11
+ }
12
+ return stream;
13
+ },
14
+ };
@@ -0,0 +1,6 @@
1
+ export * from './docker.js';
2
+ export * from './shell.js';
3
+ export declare enum ExecutorType {
4
+ container = "container",
5
+ local = "local"
6
+ }
@@ -0,0 +1,7 @@
1
+ export * from './docker.js';
2
+ export * from './shell.js';
3
+ export var ExecutorType;
4
+ (function (ExecutorType) {
5
+ ExecutorType["container"] = "container";
6
+ ExecutorType["local"] = "local";
7
+ })(ExecutorType || (ExecutorType = {}));
@@ -0,0 +1,2 @@
1
+ import { Executor } from './types.js';
2
+ export declare const shellExecutor: Executor;
@@ -0,0 +1,14 @@
1
+ import { execa } from 'execa';
2
+ export const shellExecutor = {
3
+ async run(script, options) {
4
+ const process = execa(script, {
5
+ all: true,
6
+ cwd: options.cwd,
7
+ shell: true,
8
+ });
9
+ if (options.out) {
10
+ process.all?.pipe(options.out);
11
+ }
12
+ return process;
13
+ },
14
+ };
@@ -0,0 +1,8 @@
1
+ import { Writable } from 'node:stream';
2
+ export type ExecutorRunOptions = {
3
+ cwd?: string;
4
+ out?: Writable;
5
+ };
6
+ export type Executor<RO extends ExecutorRunOptions = ExecutorRunOptions, T = unknown> = {
7
+ run(script: string, options?: RO): Promise<T>;
8
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from './context.js';
2
+ export * from './types.js';
3
+ export { run } from '@oclif/core';
@@ -0,0 +1,3 @@
1
+ export * from './context.js';
2
+ export * from './types.js';
3
+ export { run } from '@oclif/core';
@@ -0,0 +1,24 @@
1
+ import { ComponentConfig } from '../config/index.js';
2
+ import { DockerComponentBuild } from '../docker/index.js';
3
+ import { Monorepo, TaskInfo } from './index.js';
4
+ import { FilePrerequisite } from '../prerequisites/index.js';
5
+ export declare class Component {
6
+ protected _config: ComponentConfig;
7
+ protected monorepo: Monorepo;
8
+ constructor(_config: ComponentConfig, monorepo: Monorepo);
9
+ get config(): ComponentConfig;
10
+ get context(): string | undefined;
11
+ get dependencies(): Component[];
12
+ get imageName(): string;
13
+ get imageTag(): string;
14
+ get labels(): {
15
+ 'emb/component': string;
16
+ };
17
+ get name(): string;
18
+ get rootdir(): string;
19
+ get tasks(): Array<TaskInfo>;
20
+ cloneWith(config: Partial<ComponentConfig>): Component;
21
+ getPrerequisites(): Promise<Array<FilePrerequisite>>;
22
+ join(path: string): string;
23
+ toDockerBuild(): Promise<DockerComponentBuild>;
24
+ }
@@ -0,0 +1,77 @@
1
+ import deepmerge from '@fastify/deepmerge';
2
+ import { join } from 'node:path';
3
+ import { GitPrerequisitePlugin } from '../prerequisites/index.js';
4
+ export class Component {
5
+ _config;
6
+ monorepo;
7
+ constructor(_config, monorepo) {
8
+ this._config = _config;
9
+ this.monorepo = monorepo;
10
+ }
11
+ get config() {
12
+ return structuredClone(this._config);
13
+ }
14
+ get context() {
15
+ return this.config.context;
16
+ }
17
+ get dependencies() {
18
+ return this.monorepo.components.filter((c) => this.config.dependencies?.includes(c.name));
19
+ }
20
+ get imageName() {
21
+ return join(this.monorepo.name, this.name);
22
+ }
23
+ get imageTag() {
24
+ return this.monorepo.defaults.docker?.tag || 'latest';
25
+ }
26
+ get labels() {
27
+ return {
28
+ 'emb/component': this.name,
29
+ ...this._config.labels,
30
+ };
31
+ }
32
+ get name() {
33
+ return this.config.name;
34
+ }
35
+ get rootdir() {
36
+ return this.monorepo.join(this.context || this.name);
37
+ }
38
+ get tasks() {
39
+ return (this.config.tasks || [])?.map((t) => {
40
+ return {
41
+ ...t,
42
+ component: this.name,
43
+ id: `${this.name}:${t.name}`,
44
+ };
45
+ });
46
+ }
47
+ cloneWith(config) {
48
+ return new Component({
49
+ ...this.config,
50
+ ...config,
51
+ }, this.monorepo);
52
+ }
53
+ async getPrerequisites() {
54
+ // TODO: move this to config with potential overridzs
55
+ const plugin = new GitPrerequisitePlugin();
56
+ return plugin.collect(this);
57
+ }
58
+ join(path) {
59
+ return this.monorepo.join(this.context || this.name, path);
60
+ }
61
+ async toDockerBuild() {
62
+ return {
63
+ buildArgs: await this.monorepo.expand(deepmerge()(this.monorepo.defaults.docker?.buildArgs || {}, this.config.buildArgs || {})),
64
+ context: this.rootdir,
65
+ dockerfile: this.config.dockerfile || 'Dockerfile',
66
+ labels: deepmerge()({
67
+ ...this.monorepo.defaults.docker?.labels,
68
+ }, this.labels),
69
+ name: this.imageName,
70
+ prerequisites: await this.getPrerequisites(),
71
+ tag: this.imageTag
72
+ ? await this.monorepo.expand(this.imageTag)
73
+ : 'latest',
74
+ target: this.config.target || this.monorepo.defaults?.docker?.target,
75
+ };
76
+ }
77
+ }
@@ -0,0 +1,16 @@
1
+ import { ComponentConfig, DefaultSettings, FlavorConfig, IMonorepoConfig, IProjectConfig, PluginConfig } from '../config/index.js';
2
+ export declare class MonorepoConfig implements IMonorepoConfig {
3
+ components: ComponentConfig[];
4
+ defaults: DefaultSettings;
5
+ env: Record<string, string>;
6
+ flavors: Array<FlavorConfig>;
7
+ plugins: Array<PluginConfig>;
8
+ project: IProjectConfig;
9
+ vars: Record<string, unknown>;
10
+ constructor(config: IMonorepoConfig);
11
+ component(name: string): ComponentConfig;
12
+ flavor(name: string): FlavorConfig;
13
+ toJSON(): Required<IMonorepoConfig>;
14
+ with(overrides: Partial<IMonorepoConfig>): MonorepoConfig;
15
+ withFlavor(name: string): MonorepoConfig;
16
+ }
@@ -0,0 +1,66 @@
1
+ import deepMerge from '@fastify/deepmerge';
2
+ import { deepMergeArray } from '../utils/index.js';
3
+ export class MonorepoConfig {
4
+ components;
5
+ defaults;
6
+ env;
7
+ flavors;
8
+ plugins;
9
+ project;
10
+ vars;
11
+ constructor(config) {
12
+ this.components = config.components;
13
+ this.defaults = config.defaults || {};
14
+ this.project = config.project;
15
+ this.vars = config.vars || {};
16
+ this.flavors = config.flavors || [];
17
+ this.env = config.env || {};
18
+ this.plugins = config.plugins || [];
19
+ }
20
+ component(name) {
21
+ const config = this.components.find((c) => c.name === name);
22
+ if (!config) {
23
+ throw new Error(`Unknown component ${name}`);
24
+ }
25
+ return config;
26
+ }
27
+ flavor(name) {
28
+ const flavor = this.flavors.find((f) => f.name === name);
29
+ if (!flavor) {
30
+ throw new Error(`Unknown flavor: ${name}`);
31
+ }
32
+ return flavor;
33
+ }
34
+ toJSON() {
35
+ return {
36
+ components: this.components,
37
+ defaults: this.defaults,
38
+ env: this.env,
39
+ flavors: this.flavors,
40
+ plugins: this.plugins,
41
+ project: this.project,
42
+ vars: this.vars,
43
+ };
44
+ }
45
+ with(overrides) {
46
+ const newConfig = {
47
+ ...this.toJSON(),
48
+ components: deepMerge({
49
+ mergeArray() {
50
+ // Merge components by identifying them by name
51
+ return (target, source) => deepMergeArray(target, source, (item) => {
52
+ return item.name;
53
+ });
54
+ },
55
+ })(this.components, overrides?.components || []),
56
+ defaults: deepMerge()(this.defaults, overrides.defaults),
57
+ env: deepMerge()(this.env, overrides?.env || {}),
58
+ project: deepMerge()(this.project, overrides?.project || {}),
59
+ vars: deepMerge()(this.vars, overrides?.vars || {}),
60
+ };
61
+ return new MonorepoConfig(newConfig);
62
+ }
63
+ withFlavor(name) {
64
+ return this.with(this.flavor(name));
65
+ }
66
+ }
@@ -0,0 +1,7 @@
1
+ export * from './component.js';
2
+ export * from './config.js';
3
+ export * from './monorepo.js';
4
+ export * from './plugins/index.js';
5
+ export * from './store/index.js';
6
+ export * from './types.js';
7
+ export * from './utils/index.js';
@@ -0,0 +1,7 @@
1
+ export * from './component.js';
2
+ export * from './config.js';
3
+ export * from './monorepo.js';
4
+ export * from './plugins/index.js';
5
+ export * from './store/index.js';
6
+ export * from './types.js';
7
+ export * from './utils/index.js';
@@ -0,0 +1,29 @@
1
+ import { IMonorepoConfig } from '../config/index.js';
2
+ import { IOperation } from '../operations/types.js';
3
+ import { TemplateExpander } from '../utils/index.js';
4
+ import { Component } from './component.js';
5
+ import { EMBStore } from './store/index.js';
6
+ import { TaskInfo } from './types.js';
7
+ export declare class Monorepo {
8
+ private _config;
9
+ private _env;
10
+ private _store;
11
+ private initialized;
12
+ constructor(config: IMonorepoConfig);
13
+ get components(): Component[];
14
+ get config(): IMonorepoConfig;
15
+ get defaults(): import("../config/index.js").DefaultSettings;
16
+ get flavors(): string[];
17
+ get name(): string;
18
+ get rootDir(): string;
19
+ get store(): EMBStore;
20
+ get tasks(): TaskInfo[];
21
+ get vars(): Record<string, unknown>;
22
+ component(name: string): Component;
23
+ expand(str: string, expander?: TemplateExpander): Promise<string>;
24
+ expand<R extends Record<string, unknown>>(record: R, expander?: TemplateExpander): Promise<R>;
25
+ init(): Promise<Monorepo>;
26
+ join(...paths: string[]): string;
27
+ run<I, O>(operation: IOperation<I, O>, args: I): Promise<O>;
28
+ withFlavor(name: string): Promise<Monorepo>;
29
+ }
@@ -0,0 +1,106 @@
1
+ import { join } from 'node:path';
2
+ import { TemplateExpander } from '../utils/index.js';
3
+ import { Component } from './component.js';
4
+ import { MonorepoConfig } from './config.js';
5
+ import { getPlugin } from './plugins/index.js';
6
+ import { EMBStore } from './store/index.js';
7
+ export class Monorepo {
8
+ _config;
9
+ _env;
10
+ _store;
11
+ initialized = false;
12
+ constructor(config) {
13
+ this._config = new MonorepoConfig(config);
14
+ this._env = config.env || {};
15
+ }
16
+ // TODO: cache/improve
17
+ get components() {
18
+ return this._config.components.map((c) => new Component(c, this));
19
+ }
20
+ get config() {
21
+ return this._config.toJSON();
22
+ }
23
+ get defaults() {
24
+ return this._config.defaults;
25
+ }
26
+ get flavors() {
27
+ return this._config.flavors.map((f) => f.name);
28
+ }
29
+ get name() {
30
+ return this._config.project.name;
31
+ }
32
+ get rootDir() {
33
+ return this._config.project.rootDir;
34
+ }
35
+ get store() {
36
+ return this._store;
37
+ }
38
+ get tasks() {
39
+ return this.components.reduce((tasks, cmp) => {
40
+ return [...tasks, ...cmp.tasks];
41
+ }, []);
42
+ }
43
+ get vars() {
44
+ return this._config.vars;
45
+ }
46
+ component(name) {
47
+ return new Component(this._config.component(name), this);
48
+ }
49
+ async expand(strOrRecord, expander = new TemplateExpander()) {
50
+ const options = {
51
+ default: 'vars',
52
+ sources: {
53
+ env: process.env,
54
+ vars: this.vars,
55
+ },
56
+ };
57
+ if (typeof strOrRecord === 'string') {
58
+ return expander.expand(strOrRecord, options);
59
+ }
60
+ return expander.expandRecord(strOrRecord, options);
61
+ }
62
+ // Initialize
63
+ async init() {
64
+ if (this.initialized) {
65
+ throw new Error('Monorepo already initialized');
66
+ }
67
+ this._store = new EMBStore(this);
68
+ await this._store.init();
69
+ const plugins = this._config.plugins.map((p) => {
70
+ const PluginClass = getPlugin(p.name);
71
+ return new PluginClass(p.config, this);
72
+ });
73
+ this._config = await plugins.reduce(async (pConfig, plugin) => {
74
+ const newConfig = await plugin.extendConfig?.(await pConfig);
75
+ return newConfig ?? pConfig;
76
+ }, Promise.resolve(this._config));
77
+ // Expand env vars at the init and then we don't expand anymore
78
+ // The only available source for them is the existing env
79
+ const expander = new TemplateExpander();
80
+ const options = {
81
+ default: 'env',
82
+ sources: {
83
+ env: process.env,
84
+ },
85
+ };
86
+ const expanded = await expander.expandRecord(this._config.env, options);
87
+ Object.assign(process.env, expanded);
88
+ this.initialized = true;
89
+ await Promise.all(plugins.map(async (p) => {
90
+ await p.init?.();
91
+ }));
92
+ return this;
93
+ }
94
+ // Helper to build relative path to the root dir
95
+ join(...paths) {
96
+ return join(this._config.project.rootDir, ...paths);
97
+ }
98
+ async run(operation, args) {
99
+ return operation.run(args);
100
+ }
101
+ async withFlavor(name) {
102
+ const repo = new Monorepo(this._config.withFlavor(name));
103
+ await repo.init();
104
+ return repo;
105
+ }
106
+ }
@@ -0,0 +1,11 @@
1
+ import * as z from 'zod';
2
+ import { AbstractOperation } from '../../../operations/index.js';
3
+ declare const schema: z.ZodObject<{
4
+ components: z.ZodOptional<z.ZodArray<z.ZodString>>;
5
+ }, z.core.$strip>;
6
+ export declare class BuildComponentsOperation extends AbstractOperation<typeof schema, Array<unknown>> {
7
+ constructor();
8
+ protected _run(input: z.input<typeof schema>): Promise<Array<unknown>>;
9
+ private buildComponent;
10
+ }
11
+ export {};
@@ -0,0 +1,144 @@
1
+ import { Manager } from '@listr2/manager';
2
+ import { createColors } from 'colorette';
3
+ import { delay, ListrDefaultRendererLogLevels, PRESET_TIMER, } from 'listr2';
4
+ import * as z from 'zod';
5
+ import { BuildImageOperation, getSentinelFile, } from '../../../docker/index.js';
6
+ import { findBuildOrder } from '../../utils/findBuildOrder.js';
7
+ import { AbstractOperation } from '../../../operations/index.js';
8
+ import { FilePrerequisitePlugin } from '../../../prerequisites/FilePrerequisitePlugin.js';
9
+ import { PrerequisiteType } from '../../../prerequisites/types.js';
10
+ const schema = z.object({
11
+ components: z
12
+ .array(z.string())
13
+ .describe('The list of components to build')
14
+ .optional(),
15
+ });
16
+ export class BuildComponentsOperation extends AbstractOperation {
17
+ constructor() {
18
+ super(schema);
19
+ }
20
+ async _run(input) {
21
+ const ordered = findBuildOrder(this.context.monorepo.components, input.components);
22
+ const tasks = await Promise.all(ordered.map((cmp) => {
23
+ return {
24
+ task: async (context, task) => {
25
+ return this.buildComponent(cmp, task);
26
+ },
27
+ title: `Building ${cmp.name}`,
28
+ };
29
+ }));
30
+ const manager = new Manager({
31
+ collectErrors: 'minimal',
32
+ concurrent: false,
33
+ exitOnError: true,
34
+ rendererOptions: {
35
+ collapseErrors: false,
36
+ collapseSubtasks: false,
37
+ color: {
38
+ // @ts-expect-error not sure why
39
+ [ListrDefaultRendererLogLevels.SKIPPED_WITH_COLLAPSE]: createColors().green,
40
+ },
41
+ icon: {
42
+ [ListrDefaultRendererLogLevels.SKIPPED_WITH_COLLAPSE]: '♺',
43
+ },
44
+ timer: {
45
+ ...PRESET_TIMER,
46
+ },
47
+ },
48
+ });
49
+ manager.add([
50
+ {
51
+ async task(_context, task) {
52
+ return task.newListr([...tasks], {
53
+ rendererOptions: {
54
+ collapseSubtasks: false,
55
+ },
56
+ });
57
+ },
58
+ title: 'Building components',
59
+ },
60
+ ]);
61
+ await manager.runAll();
62
+ return ordered;
63
+ }
64
+ async buildComponent(cmp, parentTask) {
65
+ return parentTask.newListr([
66
+ // Collect all the prerequisites and other build infos
67
+ // (This is when variables are expanded etc)
68
+ {
69
+ async task(ctx) {
70
+ // Reset the context to defaults (as apparently the context is shared amongst branches??)
71
+ // TODO understand and fix
72
+ ctx.skip = false;
73
+ //
74
+ ctx.parentTask = parentTask;
75
+ ctx.sentinelFile = getSentinelFile(cmp);
76
+ ctx.plugin = new FilePrerequisitePlugin();
77
+ ctx.build = await cmp.toDockerBuild();
78
+ },
79
+ title: 'Prepare build context',
80
+ },
81
+ // Check for sentinal information to see if the build can be skipped
82
+ {
83
+ task: async (ctx) => {
84
+ ctx.preBuildMeta = await ctx.plugin.meta(cmp, ctx.build.prerequisites, 'pre');
85
+ let lastValue;
86
+ try {
87
+ lastValue = (await this.context.monorepo.store.readFile(ctx.sentinelFile)).toString();
88
+ }
89
+ catch {
90
+ lastValue = undefined;
91
+ }
92
+ if (lastValue) {
93
+ const diff = await ctx.plugin.diff(cmp, ctx.build.prerequisites, lastValue, ctx.preBuildMeta);
94
+ if (!diff) {
95
+ ctx.skip = true;
96
+ ctx.parentTask.skip(`${ctx.parentTask.title} (cache hit)`);
97
+ }
98
+ }
99
+ },
100
+ title: 'Checking prerequisites',
101
+ },
102
+ {
103
+ task: async (ctx, task) => {
104
+ if (ctx.skip) {
105
+ return task.skip();
106
+ }
107
+ await delay(500);
108
+ const title = `Building image ${ctx.build.name}:${ctx.build.tag}`;
109
+ task.title = title;
110
+ const op = new BuildImageOperation((progress) => {
111
+ task.title = progress;
112
+ });
113
+ await this.context.monorepo.run(op, {
114
+ ...ctx.build,
115
+ src: ctx.build.prerequisites
116
+ .filter((p) => {
117
+ return p.type === PrerequisiteType.file;
118
+ })
119
+ .map((p) => p.path),
120
+ tag: ctx.build.name + ':' + ctx.build.tag,
121
+ });
122
+ // Restore title
123
+ task.title = title;
124
+ },
125
+ title: 'Building image',
126
+ },
127
+ // Update sentinel file
128
+ {
129
+ task: async (ctx, task) => {
130
+ if (ctx.skip) {
131
+ return task.skip();
132
+ }
133
+ const sentinelValue = await ctx.plugin.meta(cmp, ctx.build.prerequisites, 'post');
134
+ await this.context.monorepo.store.writeFile(ctx.sentinelFile, sentinelValue);
135
+ },
136
+ title: 'Dumping cache info',
137
+ },
138
+ ], {
139
+ rendererOptions: {
140
+ collapseSubtasks: true,
141
+ },
142
+ });
143
+ }
144
+ }
@@ -0,0 +1 @@
1
+ export * from './BuildComponentsOperation.js';
@@ -0,0 +1 @@
1
+ export * from './BuildComponentsOperation.js';
@@ -0,0 +1 @@
1
+ export * from './components/index.js';
@@ -0,0 +1 @@
1
+ export * from './components/index.js';
@@ -0,0 +1,6 @@
1
+ import { MonorepoConfig } from '../index.js';
2
+ import { AbstractPlugin } from './plugin.js';
3
+ export declare class ComponentDiscoverPlugin extends AbstractPlugin {
4
+ static name: string;
5
+ extendConfig(config: MonorepoConfig): Promise<MonorepoConfig>;
6
+ }