@enspirit/emb 0.0.1 → 0.0.3
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 +190 -164
- package/bin/run.js +8 -2
- package/dist/src/cli/abstract/FlavouredCommand.d.ts +12 -0
- package/dist/src/cli/abstract/FlavouredCommand.js +40 -0
- package/dist/src/cli/abstract/index.d.ts +1 -0
- package/dist/src/cli/abstract/index.js +1 -0
- package/dist/src/cli/commands/clean.d.ts +15 -0
- package/dist/src/cli/commands/clean.js +29 -0
- package/dist/src/cli/commands/components/build.d.ts +11 -0
- package/dist/src/cli/commands/components/build.js +24 -0
- package/dist/src/cli/commands/components/index.d.ts +14 -0
- package/dist/src/cli/commands/components/index.js +44 -0
- package/dist/src/cli/commands/config/print.d.ts +13 -0
- package/dist/src/cli/commands/config/print.js +16 -0
- package/dist/src/cli/commands/containers/index.d.ts +12 -0
- package/dist/src/cli/commands/containers/index.js +66 -0
- package/dist/src/cli/commands/containers/prune.d.ts +8 -0
- package/dist/src/cli/commands/containers/prune.js +23 -0
- package/dist/src/cli/commands/down.d.ts +8 -0
- package/dist/src/cli/commands/down.js +42 -0
- package/dist/src/cli/commands/images/delete.d.ts +10 -0
- package/dist/src/cli/commands/images/delete.js +44 -0
- package/dist/src/cli/commands/images/index.d.ts +17 -0
- package/dist/src/cli/commands/images/index.js +59 -0
- package/dist/src/cli/commands/images/prune.d.ts +11 -0
- package/dist/src/cli/commands/images/prune.js +35 -0
- package/dist/src/cli/commands/run/index.d.ts +10 -0
- package/dist/src/cli/commands/run/index.js +49 -0
- package/dist/src/cli/commands/tasks/index.d.ts +9 -0
- package/dist/src/cli/commands/tasks/index.js +23 -0
- package/dist/src/cli/commands/tasks/run.d.ts +16 -0
- package/dist/src/cli/commands/tasks/run.js +113 -0
- package/dist/src/cli/commands/up.d.ts +10 -0
- package/dist/src/cli/commands/up.js +49 -0
- package/dist/src/cli/constants.d.ts +2 -0
- package/dist/src/cli/constants.js +6 -0
- package/dist/src/cli/hooks/init.d.ts +3 -0
- package/dist/src/cli/hooks/init.js +27 -0
- package/dist/src/cli/index.d.ts +3 -0
- package/dist/src/cli/index.js +3 -0
- package/dist/src/config/convert.d.ts +5 -0
- package/dist/src/config/convert.js +48 -0
- package/dist/src/config/index.d.ts +6 -0
- package/dist/src/config/index.js +25 -0
- package/dist/src/config/schema.d.ts +102 -0
- package/dist/src/config/schema.js +7 -0
- package/dist/src/config/schema.json +209 -0
- package/dist/src/config/types.d.ts +43 -0
- package/dist/src/config/types.js +1 -0
- package/dist/src/config/validation.d.ts +1 -0
- package/dist/src/config/validation.js +27 -0
- package/dist/src/context.d.ts +3 -0
- package/dist/src/context.js +7 -0
- package/dist/src/docker/compose/index.d.ts +7 -0
- package/dist/src/docker/compose/index.js +13 -0
- package/dist/src/docker/containers/getContainer.d.ts +2 -0
- package/dist/src/docker/containers/getContainer.js +5 -0
- package/dist/src/docker/containers/index.d.ts +1 -0
- package/dist/src/docker/containers/index.js +1 -0
- package/dist/src/docker/images/buildImage.d.ts +19 -0
- package/dist/src/docker/images/buildImage.js +64 -0
- package/dist/src/docker/images/deleteImage.d.ts +5 -0
- package/dist/src/docker/images/deleteImage.js +6 -0
- package/dist/src/docker/images/index.d.ts +4 -0
- package/dist/src/docker/images/index.js +4 -0
- package/dist/src/docker/images/listImages.d.ts +2 -0
- package/dist/src/docker/images/listImages.js +8 -0
- package/dist/src/docker/images/pruneImages.d.ts +6 -0
- package/dist/src/docker/images/pruneImages.js +8 -0
- package/dist/src/docker/index.d.ts +7 -0
- package/dist/src/docker/index.js +7 -0
- package/dist/src/docker/operations/containers/ListContainersOperation.d.ts +18 -0
- package/dist/src/docker/operations/containers/ListContainersOperation.js +44 -0
- package/dist/src/docker/operations/containers/PruneContainersOperation.d.ts +16 -0
- package/dist/src/docker/operations/containers/PruneContainersOperation.js +33 -0
- package/dist/src/docker/operations/containers/index.d.ts +2 -0
- package/dist/src/docker/operations/containers/index.js +2 -0
- package/dist/src/docker/operations/images/BuildImageOperation.d.ts +20 -0
- package/dist/src/docker/operations/images/BuildImageOperation.js +69 -0
- package/dist/src/docker/operations/images/ListImagesOperation.d.ts +17 -0
- package/dist/src/docker/operations/images/ListImagesOperation.js +38 -0
- package/dist/src/docker/operations/images/PruneImagesOperation.d.ts +16 -0
- package/dist/src/docker/operations/images/PruneImagesOperation.js +33 -0
- package/dist/src/docker/operations/images/index.d.ts +3 -0
- package/dist/src/docker/operations/images/index.js +3 -0
- package/dist/src/docker/operations/index.d.ts +2 -0
- package/dist/src/docker/operations/index.js +2 -0
- package/dist/src/docker/protobuf/control.proto +48 -0
- package/dist/src/docker/protobuf/index.d.ts +5 -0
- package/dist/src/docker/protobuf/index.js +29 -0
- package/dist/src/docker/types.d.ts +14 -0
- package/dist/src/docker/types.js +1 -0
- package/dist/src/docker/utils.d.ts +7 -0
- package/dist/src/docker/utils.js +10 -0
- package/dist/src/executors/docker.d.ts +6 -0
- package/dist/src/executors/docker.js +14 -0
- package/dist/src/executors/index.d.ts +6 -0
- package/dist/src/executors/index.js +7 -0
- package/dist/src/executors/shell.d.ts +2 -0
- package/dist/src/executors/shell.js +14 -0
- package/dist/src/executors/types.d.ts +8 -0
- package/dist/src/executors/types.js +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +3 -0
- package/dist/src/monorepo/component.d.ts +24 -0
- package/dist/src/monorepo/component.js +77 -0
- package/dist/src/monorepo/config.d.ts +16 -0
- package/dist/src/monorepo/config.js +66 -0
- package/dist/src/monorepo/index.d.ts +7 -0
- package/dist/src/monorepo/index.js +7 -0
- package/dist/src/monorepo/monorepo.d.ts +29 -0
- package/dist/src/monorepo/monorepo.js +106 -0
- package/dist/src/monorepo/operations/components/BuildComponentsOperation.d.ts +11 -0
- package/dist/src/monorepo/operations/components/BuildComponentsOperation.js +144 -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 +1 -0
- package/dist/src/monorepo/operations/index.js +1 -0
- package/dist/src/monorepo/plugins/ComponentsDiscover.d.ts +6 -0
- package/dist/src/monorepo/plugins/ComponentsDiscover.js +30 -0
- package/dist/src/monorepo/plugins/DotEnvPlugin.d.ts +5 -0
- package/dist/src/monorepo/plugins/DotEnvPlugin.js +11 -0
- package/dist/src/monorepo/plugins/index.d.ts +7 -0
- package/dist/src/monorepo/plugins/index.js +20 -0
- package/dist/src/monorepo/plugins/plugin.d.ts +15 -0
- package/dist/src/monorepo/plugins/plugin.js +12 -0
- package/dist/src/monorepo/project.d.ts +6 -0
- package/dist/src/monorepo/project.js +8 -0
- package/dist/src/monorepo/store/index.d.ts +20 -0
- package/dist/src/monorepo/store/index.js +65 -0
- package/dist/src/monorepo/types.d.ts +7 -0
- package/dist/src/monorepo/types.js +1 -0
- package/dist/src/monorepo/utils/findBuildOrder.d.ts +2 -0
- package/dist/src/monorepo/utils/findBuildOrder.js +41 -0
- package/dist/src/monorepo/utils/index.d.ts +1 -0
- package/dist/src/monorepo/utils/index.js +1 -0
- package/dist/src/operations/abstract/AbstractOperation.d.ts +10 -0
- package/dist/src/operations/abstract/AbstractOperation.js +13 -0
- package/dist/src/operations/abstract/index.d.ts +1 -0
- package/dist/src/operations/abstract/index.js +1 -0
- package/dist/src/operations/index.d.ts +2 -0
- package/dist/src/operations/index.js +2 -0
- package/dist/src/operations/types.d.ts +3 -0
- package/dist/src/operations/types.js +1 -0
- package/dist/src/prerequisites/FilePrerequisitePlugin.d.ts +7 -0
- package/dist/src/prerequisites/FilePrerequisitePlugin.js +41 -0
- package/dist/src/prerequisites/GitPrerequisitePlugin.d.ts +5 -0
- package/dist/src/prerequisites/GitPrerequisitePlugin.js +17 -0
- package/dist/src/prerequisites/index.d.ts +3 -0
- package/dist/src/prerequisites/index.js +3 -0
- package/dist/src/prerequisites/types.d.ts +46 -0
- package/dist/src/prerequisites/types.js +24 -0
- package/dist/src/types.d.ts +13 -0
- package/dist/src/types.js +1 -0
- package/dist/src/utils/TemplateExpander.d.ts +21 -0
- package/dist/src/utils/TemplateExpander.js +53 -0
- package/dist/src/utils/deepMergeArray.d.ts +1 -0
- package/dist/src/utils/deepMergeArray.js +19 -0
- package/dist/src/utils/index.d.ts +3 -0
- package/dist/src/utils/index.js +3 -0
- package/dist/src/utils/time.d.ts +2 -0
- package/dist/src/utils/time.js +19 -0
- package/oclif.manifest.json +572 -2
- package/package.json +10 -9
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import deepmerge from '@fastify/deepmerge';
|
|
2
|
+
import { glob } from 'glob';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
4
|
+
import { MonorepoConfig } from '../index.js';
|
|
5
|
+
import { AbstractPlugin } from './plugin.js';
|
|
6
|
+
export class ComponentDiscoverPlugin extends AbstractPlugin {
|
|
7
|
+
static name = 'autodiscover';
|
|
8
|
+
async extendConfig(config) {
|
|
9
|
+
const files = await glob('*/Dockerfile', {
|
|
10
|
+
cwd: config.project.rootDir,
|
|
11
|
+
});
|
|
12
|
+
const overrides = files.map((path) => {
|
|
13
|
+
const name = dirname(path);
|
|
14
|
+
const component = config.components.find((cmp) => cmp.name === name);
|
|
15
|
+
const cfg = {
|
|
16
|
+
context: name,
|
|
17
|
+
name,
|
|
18
|
+
};
|
|
19
|
+
return component ? deepmerge()(component, cfg) : cfg;
|
|
20
|
+
});
|
|
21
|
+
const untouched = config.components.filter((c) => !overrides.find((o) => {
|
|
22
|
+
return o.name === c.name;
|
|
23
|
+
}));
|
|
24
|
+
const components = [...overrides, ...untouched];
|
|
25
|
+
return new MonorepoConfig({
|
|
26
|
+
...config,
|
|
27
|
+
components,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { configDotenv } from 'dotenv';
|
|
2
|
+
import { AbstractPlugin } from './plugin.js';
|
|
3
|
+
export class DotEnvPlugin extends AbstractPlugin {
|
|
4
|
+
static name = 'dotenv';
|
|
5
|
+
async init() {
|
|
6
|
+
configDotenv({
|
|
7
|
+
path: this.config.map((p) => this.monorepo.join(p)),
|
|
8
|
+
quiet: true,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AbstractPlugin } from './plugin.js';
|
|
2
|
+
export * from './ComponentsDiscover.js';
|
|
3
|
+
export * from './DotEnvPlugin.js';
|
|
4
|
+
import { Monorepo } from '../monorepo.js';
|
|
5
|
+
export type AbstractPluginConstructor = new <C, P extends AbstractPlugin<C>>(config: C, monorepo: Monorepo) => P;
|
|
6
|
+
export declare const registerPlugin: (plugin: AbstractPluginConstructor) => void;
|
|
7
|
+
export declare const getPlugin: (name: string) => AbstractPluginConstructor;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export * from './ComponentsDiscover.js';
|
|
2
|
+
export * from './DotEnvPlugin.js';
|
|
3
|
+
import { ComponentDiscoverPlugin } from './ComponentsDiscover.js';
|
|
4
|
+
import { DotEnvPlugin } from './DotEnvPlugin.js';
|
|
5
|
+
const PluginRegistry = new Map();
|
|
6
|
+
export const registerPlugin = (plugin) => {
|
|
7
|
+
if (PluginRegistry.has(plugin.name)) {
|
|
8
|
+
throw new Error(`Plugin name confict: '${plugin.name}' already registered`);
|
|
9
|
+
}
|
|
10
|
+
PluginRegistry.set(plugin.name, plugin);
|
|
11
|
+
};
|
|
12
|
+
export const getPlugin = (name) => {
|
|
13
|
+
if (!PluginRegistry.has(name)) {
|
|
14
|
+
throw new Error(`Unknown plugin: ${name}`);
|
|
15
|
+
}
|
|
16
|
+
return PluginRegistry.get(name);
|
|
17
|
+
};
|
|
18
|
+
/** Not sure why we need casting */
|
|
19
|
+
registerPlugin(ComponentDiscoverPlugin);
|
|
20
|
+
registerPlugin(DotEnvPlugin);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Monorepo, MonorepoConfig } from '../index.js';
|
|
2
|
+
export declare abstract class AbstractPlugin<C = unknown> {
|
|
3
|
+
protected config: C;
|
|
4
|
+
protected monorepo: Monorepo;
|
|
5
|
+
/**
|
|
6
|
+
* The name of the plugin (must be unique)
|
|
7
|
+
*/
|
|
8
|
+
static name: string;
|
|
9
|
+
constructor(config: C, monorepo: Monorepo);
|
|
10
|
+
extendConfig?(config: MonorepoConfig): Promise<MonorepoConfig>;
|
|
11
|
+
/**
|
|
12
|
+
* Initialization of a plugin.
|
|
13
|
+
*/
|
|
14
|
+
init?(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Monorepo } from '../index.js';
|
|
2
|
+
/**
|
|
3
|
+
* A first implementation of a "store" where
|
|
4
|
+
* everyone can create things (logs, sentinel files, ...)
|
|
5
|
+
*
|
|
6
|
+
* For now it's a hidden folder on the root of the monorepo
|
|
7
|
+
*/
|
|
8
|
+
export declare class EMBStore {
|
|
9
|
+
private monorepo;
|
|
10
|
+
private path;
|
|
11
|
+
constructor(monorepo: Monorepo, dirname?: string);
|
|
12
|
+
createReadStream(path: string): Promise<import("fs").ReadStream>;
|
|
13
|
+
createWriteStream(path: string, flags?: string | undefined): Promise<import("fs").WriteStream>;
|
|
14
|
+
init(): Promise<void>;
|
|
15
|
+
join(path: string): string;
|
|
16
|
+
mkdirp(path: string): Promise<void>;
|
|
17
|
+
readFile(path: string): Promise<Buffer<ArrayBufferLike>>;
|
|
18
|
+
trash(): Promise<void>;
|
|
19
|
+
writeFile(path: string, data: string): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { constants, createReadStream, createWriteStream } from 'node:fs';
|
|
2
|
+
import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join, normalize } from 'node:path';
|
|
4
|
+
/**
|
|
5
|
+
* A first implementation of a "store" where
|
|
6
|
+
* everyone can create things (logs, sentinel files, ...)
|
|
7
|
+
*
|
|
8
|
+
* For now it's a hidden folder on the root of the monorepo
|
|
9
|
+
*/
|
|
10
|
+
export class EMBStore {
|
|
11
|
+
monorepo;
|
|
12
|
+
path;
|
|
13
|
+
constructor(monorepo, dirname = '.emb') {
|
|
14
|
+
this.monorepo = monorepo;
|
|
15
|
+
this.path = this.monorepo.join(dirname);
|
|
16
|
+
}
|
|
17
|
+
async createReadStream(path) {
|
|
18
|
+
await this.mkdirp(dirname(path));
|
|
19
|
+
return createReadStream(this.join(path));
|
|
20
|
+
}
|
|
21
|
+
async createWriteStream(path, flags = 'w') {
|
|
22
|
+
await this.mkdirp(dirname(path));
|
|
23
|
+
return createWriteStream(this.join(path), {
|
|
24
|
+
flags: flags ?? 'w',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async init() {
|
|
28
|
+
let exists;
|
|
29
|
+
try {
|
|
30
|
+
await access(this.path, constants.F_OK);
|
|
31
|
+
exists = true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
exists = false;
|
|
35
|
+
}
|
|
36
|
+
if (exists) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
await mkdir(this.path, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
const msg = error instanceof Error ? error.message : error;
|
|
44
|
+
throw new Error(`Unable to create the emb store: ${msg}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
join(path) {
|
|
48
|
+
return join(this.path, path);
|
|
49
|
+
}
|
|
50
|
+
async mkdirp(path) {
|
|
51
|
+
// Avoid getting out of the store by ensuring nothing goes past ../
|
|
52
|
+
const normalized = normalize(join('/', path));
|
|
53
|
+
await mkdir(this.join(normalized), { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
async readFile(path) {
|
|
56
|
+
return readFile(this.join(path));
|
|
57
|
+
}
|
|
58
|
+
async trash() {
|
|
59
|
+
return rm(this.path, { force: true, recursive: true });
|
|
60
|
+
}
|
|
61
|
+
async writeFile(path, data) {
|
|
62
|
+
await this.mkdirp(dirname(path));
|
|
63
|
+
return writeFile(this.join(path), data);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import graphlib from 'graphlib';
|
|
2
|
+
const toGraph = (components) => {
|
|
3
|
+
const graph = new graphlib.Graph();
|
|
4
|
+
// Add all components as nodes
|
|
5
|
+
for (const comp of components) {
|
|
6
|
+
graph.setNode(comp.name);
|
|
7
|
+
}
|
|
8
|
+
// Add edges
|
|
9
|
+
for (const comp of components) {
|
|
10
|
+
for (const dep of comp.dependencies ?? []) {
|
|
11
|
+
graph.setEdge(dep.name, comp.name);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return graph;
|
|
15
|
+
};
|
|
16
|
+
export const findBuildOrder = (components, selection) => {
|
|
17
|
+
const hash = components.reduce((cmps, cmp) => {
|
|
18
|
+
cmps[cmp.name] = cmp;
|
|
19
|
+
return cmps;
|
|
20
|
+
}, {});
|
|
21
|
+
const graph = toGraph(components);
|
|
22
|
+
// Detect cycles
|
|
23
|
+
const cycles = graphlib.alg.findCycles(graph);
|
|
24
|
+
if (cycles.length > 0) {
|
|
25
|
+
throw new Error('Circular dependencies detected: ' + JSON.stringify(cycles));
|
|
26
|
+
}
|
|
27
|
+
// Pick nodes that we want to build and rebuild a graph only with these
|
|
28
|
+
const toBuild = selection || components.map((c) => c.name);
|
|
29
|
+
const includingDeps = toBuild
|
|
30
|
+
.reduce((set, name) => {
|
|
31
|
+
graph.predecessors(name)?.forEach((name) => {
|
|
32
|
+
set.add(name);
|
|
33
|
+
});
|
|
34
|
+
return set;
|
|
35
|
+
}, new Set(toBuild))
|
|
36
|
+
.values();
|
|
37
|
+
const newGraph = toGraph([...includingDeps].map((name) => hash[name]));
|
|
38
|
+
// Get build order
|
|
39
|
+
const order = graphlib.alg.topsort(newGraph);
|
|
40
|
+
return order.map((name) => hash[name]);
|
|
41
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './findBuildOrder.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './findBuildOrder.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { EmbContext } from '../../index.js';
|
|
2
|
+
import * as z from 'zod';
|
|
3
|
+
import { IOperation } from '../types.js';
|
|
4
|
+
export declare abstract class AbstractOperation<S extends z.Schema, O = unknown> implements IOperation<z.infer<S>, O> {
|
|
5
|
+
protected inputSchema: S;
|
|
6
|
+
protected context: EmbContext;
|
|
7
|
+
constructor(inputSchema: S);
|
|
8
|
+
protected abstract _run(input: z.infer<S>): Promise<O>;
|
|
9
|
+
run(input: z.infer<S>): Promise<O>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getContext } from '../../index.js';
|
|
2
|
+
export class AbstractOperation {
|
|
3
|
+
inputSchema;
|
|
4
|
+
context;
|
|
5
|
+
constructor(inputSchema) {
|
|
6
|
+
this.inputSchema = inputSchema;
|
|
7
|
+
this.context = getContext();
|
|
8
|
+
}
|
|
9
|
+
async run(input) {
|
|
10
|
+
const dressed = this.inputSchema.parse(input);
|
|
11
|
+
return this._run(dressed);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './AbstractOperation.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './AbstractOperation.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Component } from '../monorepo/index.js';
|
|
2
|
+
import { FilePrerequisite, PrerequisitePlugin, PrerequisiteType } from './types.js';
|
|
3
|
+
export declare class FilePrerequisitePlugin implements PrerequisitePlugin<PrerequisiteType.file, FilePrerequisite, string> {
|
|
4
|
+
diff(component: Component, prerequisites: Array<FilePrerequisite>, previous: string, _actual: string): Promise<Array<FilePrerequisite> | null>;
|
|
5
|
+
meta(component: Component, prerequisites: FilePrerequisite[], mode: 'post' | 'pre'): Promise<string>;
|
|
6
|
+
private getStats;
|
|
7
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { stat } from 'node:fs/promises';
|
|
2
|
+
import pMap from 'p-map';
|
|
3
|
+
export class FilePrerequisitePlugin {
|
|
4
|
+
async diff(component, prerequisites, previous, _actual) {
|
|
5
|
+
const stats = await this.getStats(component, prerequisites);
|
|
6
|
+
const changes = stats
|
|
7
|
+
.filter((s) => {
|
|
8
|
+
return s.mtimeMs > Number.parseInt(previous, 10);
|
|
9
|
+
})
|
|
10
|
+
.map((s) => ({
|
|
11
|
+
path: s.path,
|
|
12
|
+
type: s.type,
|
|
13
|
+
}));
|
|
14
|
+
return changes.length > 0 ? changes : null;
|
|
15
|
+
}
|
|
16
|
+
async meta(component, prerequisites, mode) {
|
|
17
|
+
switch (mode) {
|
|
18
|
+
case 'post': {
|
|
19
|
+
return Date.now().toString();
|
|
20
|
+
}
|
|
21
|
+
case 'pre': {
|
|
22
|
+
const stats = await this.getStats(component, prerequisites);
|
|
23
|
+
const max = stats.reduce((minimum, stat) => {
|
|
24
|
+
return Math.max(minimum, stat.mtimeMs);
|
|
25
|
+
}, 0);
|
|
26
|
+
return max.toFixed(0).toString();
|
|
27
|
+
}
|
|
28
|
+
default: {
|
|
29
|
+
throw new Error(`Invalid mode passed to 'meta()': ${mode}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async getStats(component, prerequisites) {
|
|
34
|
+
return pMap(prerequisites, async (file) => {
|
|
35
|
+
return {
|
|
36
|
+
...file,
|
|
37
|
+
...(await stat(component.join(file.path))),
|
|
38
|
+
};
|
|
39
|
+
}, { concurrency: 30 });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Component } from '../monorepo/index.js';
|
|
2
|
+
import { FilePrerequisite, PrerequisitePlugin, PrerequisiteType } from './types.js';
|
|
3
|
+
export declare class GitPrerequisitePlugin implements PrerequisitePlugin<PrerequisiteType.file, FilePrerequisite> {
|
|
4
|
+
collect(component: Component): Promise<Array<FilePrerequisite>>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { simpleGit } from 'simple-git';
|
|
2
|
+
import { PrerequisiteType, } from './types.js';
|
|
3
|
+
export class GitPrerequisitePlugin {
|
|
4
|
+
async collect(component) {
|
|
5
|
+
const repo = simpleGit(component.rootdir);
|
|
6
|
+
return (await repo.raw('ls-files', component.rootdir))
|
|
7
|
+
.split('\n')
|
|
8
|
+
.map((s) => s.trim())
|
|
9
|
+
.filter(Boolean)
|
|
10
|
+
.map((path) => {
|
|
11
|
+
return {
|
|
12
|
+
path,
|
|
13
|
+
type: PrerequisiteType.file,
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Component } from '../monorepo/index.js';
|
|
2
|
+
export declare enum PrerequisiteType {
|
|
3
|
+
file = "file",
|
|
4
|
+
variable = "variable"
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* A prerequisite must at least include its type name
|
|
8
|
+
*/
|
|
9
|
+
export interface Prerequisite<T extends PrerequisiteType | string> {
|
|
10
|
+
type: T;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A file-like prerequisite includes at least its path
|
|
14
|
+
*/
|
|
15
|
+
export interface FilePrerequisite extends Prerequisite<PrerequisiteType.file> {
|
|
16
|
+
path: string;
|
|
17
|
+
type: PrerequisiteType.file;
|
|
18
|
+
}
|
|
19
|
+
export interface PrerequisitePlugin<T extends PrerequisiteType, P extends Prerequisite<T>, Output = unknown, Changes = unknown> {
|
|
20
|
+
/**
|
|
21
|
+
* Collect/discover prerequisistes for a specific component
|
|
22
|
+
*/
|
|
23
|
+
collect?(component: Component): Promise<Array<P>>;
|
|
24
|
+
/**
|
|
25
|
+
* Returns the list of changes between the last collection and the new
|
|
26
|
+
* collection.
|
|
27
|
+
* Eg:
|
|
28
|
+
* list of files that have changed since the last build
|
|
29
|
+
* list of variables/value that have changed since the last build
|
|
30
|
+
*
|
|
31
|
+
* In case nothing has changed, the plugin must resolve to null
|
|
32
|
+
*/
|
|
33
|
+
diff?(component: Component, prerequisistes: Array<P>, previous: Output, actual: Output): Promise<Changes | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Computes the meta-information for a list of prerequisites
|
|
36
|
+
* this meta information will be saved and used later to decide whether
|
|
37
|
+
*
|
|
38
|
+
* The mode specifies if the computation is made before a build is triggered
|
|
39
|
+
* (and before deciding if the build should be triggered)
|
|
40
|
+
* or after a new build has been made
|
|
41
|
+
*
|
|
42
|
+
* or not a component needs to rebuild
|
|
43
|
+
* (eg: some prerequisite files have changed, some vars have changed, etc)
|
|
44
|
+
*/
|
|
45
|
+
meta?(component: Component, prerequisites: Array<P>, mode: 'post' | 'pre'): Promise<Output>;
|
|
46
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Prerequisites
|
|
2
|
+
export var PrerequisiteType;
|
|
3
|
+
(function (PrerequisiteType) {
|
|
4
|
+
PrerequisiteType["file"] = "file";
|
|
5
|
+
PrerequisiteType["variable"] = "variable";
|
|
6
|
+
})(PrerequisiteType || (PrerequisiteType = {}));
|
|
7
|
+
// Checker for files:
|
|
8
|
+
//
|
|
9
|
+
// Collect all files a component depends on
|
|
10
|
+
// and compute their last updated time
|
|
11
|
+
//
|
|
12
|
+
// No previous build available for this checker?
|
|
13
|
+
// -> build and then return now() as output
|
|
14
|
+
// Previous build? receive the last output (now())
|
|
15
|
+
// and compare it with most recent prerequisite.
|
|
16
|
+
// Checker for variables:
|
|
17
|
+
//
|
|
18
|
+
// Collect all variables a component depends on
|
|
19
|
+
// and compute a hash key=>value
|
|
20
|
+
//
|
|
21
|
+
// No previous build available for this checker?
|
|
22
|
+
// -> build and then return the hash as output
|
|
23
|
+
// Previous build? receive the last output (hash)
|
|
24
|
+
// and compare it with current hash -> different ? true : false
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type Docker from 'dockerode';
|
|
2
|
+
import { Monorepo } from './monorepo/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* The context is meant to be what all plugins can decorate
|
|
5
|
+
* to install their own things
|
|
6
|
+
*
|
|
7
|
+
* Similar to Request in Expressjs projects, feel free to extend the type
|
|
8
|
+
* and install here things that to be accessible by operations during a CLI run
|
|
9
|
+
*/
|
|
10
|
+
export interface EmbContext {
|
|
11
|
+
docker: Docker;
|
|
12
|
+
monorepo: Monorepo;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type ExpandOptions = {
|
|
2
|
+
default?: string;
|
|
3
|
+
sources?: Record<string, Record<string, unknown>>;
|
|
4
|
+
};
|
|
5
|
+
export type ExpansionHistory = {
|
|
6
|
+
source: string;
|
|
7
|
+
value: unknown;
|
|
8
|
+
variable: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class TemplateExpander {
|
|
11
|
+
/**
|
|
12
|
+
* Keep track of the sources used for expansions
|
|
13
|
+
* (track source, name, final value)
|
|
14
|
+
*/
|
|
15
|
+
private expansions;
|
|
16
|
+
get expansionCount(): number;
|
|
17
|
+
expand(str: string, options?: ExpandOptions): Promise<string>;
|
|
18
|
+
expandRecord<R extends Record<string, unknown>>(record: R, options?: ExpandOptions): Promise<R>;
|
|
19
|
+
private track;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const TPL_REGEX = /(?<!\\)\${(?:(\w+):)?(\w+)(?::-(.*?))?}/g;
|
|
2
|
+
export class TemplateExpander {
|
|
3
|
+
/**
|
|
4
|
+
* Keep track of the sources used for expansions
|
|
5
|
+
* (track source, name, final value)
|
|
6
|
+
*/
|
|
7
|
+
expansions = [];
|
|
8
|
+
get expansionCount() {
|
|
9
|
+
return this.expansions.length;
|
|
10
|
+
}
|
|
11
|
+
async expand(str, options = {}) {
|
|
12
|
+
return ((str || '')
|
|
13
|
+
.toString()
|
|
14
|
+
// Expand variables
|
|
15
|
+
.replaceAll(TPL_REGEX, (match, source, key, fallback) => {
|
|
16
|
+
const src = source || options.default;
|
|
17
|
+
const provider = options.sources?.[src];
|
|
18
|
+
if (!provider) {
|
|
19
|
+
if (fallback !== undefined) {
|
|
20
|
+
return this.track(src, key, fallback);
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`Invalid expand provider '${source}' ('${match}')`);
|
|
23
|
+
}
|
|
24
|
+
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
|
+
if (!val && fallback === undefined) {
|
|
29
|
+
throw new Error(`Could not expand '${match}' and no default value provided`);
|
|
30
|
+
}
|
|
31
|
+
if (val !== undefined && val !== null) {
|
|
32
|
+
return this.track(src, key, val);
|
|
33
|
+
}
|
|
34
|
+
return this.track(src, key, fallback || '');
|
|
35
|
+
})
|
|
36
|
+
// Unescape non-variables left
|
|
37
|
+
.replaceAll('\\${', '${'));
|
|
38
|
+
}
|
|
39
|
+
async expandRecord(record, options = {}) {
|
|
40
|
+
return Object.entries(record).reduce(async (vars, [name, str]) => {
|
|
41
|
+
const previous = await vars;
|
|
42
|
+
// @ts-expect-error dunno
|
|
43
|
+
previous[name] = await (typeof str === 'object'
|
|
44
|
+
? this.expandRecord(str, options)
|
|
45
|
+
: this.expand(str, options));
|
|
46
|
+
return previous;
|
|
47
|
+
}, Promise.resolve({}));
|
|
48
|
+
}
|
|
49
|
+
track(source, variable, value) {
|
|
50
|
+
this.expansions.push({ source, value, variable });
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const deepMergeArray: <T>(target: Array<T>, source: Array<T>, identifierFn?: (item: T) => unknown) => Array<T>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import deepmerge from '@fastify/deepmerge';
|
|
2
|
+
export const deepMergeArray = (target, source, identifierFn) => {
|
|
3
|
+
if (!identifierFn) {
|
|
4
|
+
return deepmerge()(target, source);
|
|
5
|
+
}
|
|
6
|
+
const overridden = target.map((item) => {
|
|
7
|
+
const id = identifierFn(item);
|
|
8
|
+
const override = source.find((item) => identifierFn(item) === id);
|
|
9
|
+
if (override) {
|
|
10
|
+
return deepmerge()(item, override);
|
|
11
|
+
}
|
|
12
|
+
return item;
|
|
13
|
+
});
|
|
14
|
+
const additional = source.filter((item) => {
|
|
15
|
+
const id = identifierFn(item);
|
|
16
|
+
return !target.find((item) => identifierFn(item) === id);
|
|
17
|
+
});
|
|
18
|
+
return [...overridden, ...additional];
|
|
19
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DateTime } from 'luxon';
|
|
2
|
+
const units = [
|
|
3
|
+
'year',
|
|
4
|
+
'month',
|
|
5
|
+
'week',
|
|
6
|
+
'day',
|
|
7
|
+
'hour',
|
|
8
|
+
'minute',
|
|
9
|
+
'second',
|
|
10
|
+
];
|
|
11
|
+
export const timeAgo = (date) => {
|
|
12
|
+
const dateTime = date instanceof Date ? DateTime.fromJSDate(date) : date;
|
|
13
|
+
const diff = dateTime.diffNow().shiftTo(...units);
|
|
14
|
+
const unit = units.find((unit) => diff.get(unit) !== 0) || 'second';
|
|
15
|
+
const relativeFormatter = new Intl.RelativeTimeFormat('en', {
|
|
16
|
+
numeric: 'auto',
|
|
17
|
+
});
|
|
18
|
+
return relativeFormatter.format(Math.trunc(diff.as(unit)), unit);
|
|
19
|
+
};
|