@enspirit/emb 0.0.7 → 0.0.9
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 +7 -5
- package/dist/src/cli/commands/components/build.d.ts +1 -0
- package/dist/src/cli/commands/components/build.js +8 -0
- package/dist/src/cli/commands/tasks/run.d.ts +1 -0
- package/dist/src/cli/commands/tasks/run.js +23 -5
- package/dist/src/docker/compose/operations/ComposeUpOperation.js +13 -12
- package/dist/src/docker/operations/images/BuildImageOperation.js +1 -1
- package/dist/src/errors.d.ts +57 -0
- package/dist/src/errors.js +66 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/monorepo/monorepo.d.ts +1 -0
- package/dist/src/monorepo/monorepo.js +15 -11
- package/dist/src/monorepo/operations/components/BuildComponentsOperation.d.ts +3 -0
- package/dist/src/monorepo/operations/components/BuildComponentsOperation.js +46 -19
- package/dist/src/monorepo/operations/tasks/RunTasksOperation.d.ts +1 -2
- package/dist/src/monorepo/operations/tasks/RunTasksOperation.js +6 -16
- package/dist/src/monorepo/taskManagerFactory.js +1 -0
- package/dist/src/monorepo/utils/findRunOrder.js +6 -6
- package/oclif.manifest.json +48 -33
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ $ npm install -g @enspirit/emb
|
|
|
14
14
|
$ emb COMMAND
|
|
15
15
|
running command...
|
|
16
16
|
$ emb (--version)
|
|
17
|
-
@enspirit/emb/0.0.
|
|
17
|
+
@enspirit/emb/0.0.9 darwin-x64 node-v22.18.0
|
|
18
18
|
$ emb --help [COMMAND]
|
|
19
19
|
USAGE
|
|
20
20
|
$ emb COMMAND
|
|
@@ -120,14 +120,15 @@ Build the components of the monorepo
|
|
|
120
120
|
|
|
121
121
|
```
|
|
122
122
|
USAGE
|
|
123
|
-
$ emb components build [COMPONENT...] [--json] [--flavor <value>] [--dry-run]
|
|
123
|
+
$ emb components build [COMPONENT...] [--json] [--flavor <value>] [--dry-run] [-f]
|
|
124
124
|
|
|
125
125
|
ARGUMENTS
|
|
126
126
|
COMPONENT... List of components to build (defaults to all)
|
|
127
127
|
|
|
128
128
|
FLAGS
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
-f, --force Bypass the cache and force the build
|
|
130
|
+
--dry-run Do not build the components but only produce build meta information
|
|
131
|
+
--flavor=<value> Specify the flavor to use.
|
|
131
132
|
|
|
132
133
|
GLOBAL FLAGS
|
|
133
134
|
--json Format output as json.
|
|
@@ -354,12 +355,13 @@ Run tasks.
|
|
|
354
355
|
|
|
355
356
|
```
|
|
356
357
|
USAGE
|
|
357
|
-
$ emb tasks run TASK... [--json] [-x container|local]
|
|
358
|
+
$ emb tasks run TASK... [--json] [-x container|local] [-a]
|
|
358
359
|
|
|
359
360
|
ARGUMENTS
|
|
360
361
|
TASK... List of tasks to run. You can provide either ids or names (eg: component:task or task)
|
|
361
362
|
|
|
362
363
|
FLAGS
|
|
364
|
+
-a, --all-matching Run all tasks matching (when multiple matches)
|
|
363
365
|
-x, --executor=<option> Where to run the task. (experimental!)
|
|
364
366
|
<options: container|local>
|
|
365
367
|
|
|
@@ -8,6 +8,7 @@ export default class BuildCommand extends FlavoredCommand<typeof BuildCommand> {
|
|
|
8
8
|
static examples: string[];
|
|
9
9
|
static flags: {
|
|
10
10
|
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
};
|
|
12
13
|
static enableJsonFlag: boolean;
|
|
13
14
|
static strict: boolean;
|
|
@@ -17,6 +17,12 @@ export default class BuildCommand extends FlavoredCommand {
|
|
|
17
17
|
required: false,
|
|
18
18
|
description: 'Do not build the components but only produce build meta information',
|
|
19
19
|
}),
|
|
20
|
+
force: Flags.boolean({
|
|
21
|
+
name: 'force',
|
|
22
|
+
char: 'f',
|
|
23
|
+
required: false,
|
|
24
|
+
description: 'Bypass the cache and force the build',
|
|
25
|
+
}),
|
|
20
26
|
};
|
|
21
27
|
static enableJsonFlag = true;
|
|
22
28
|
static strict = false;
|
|
@@ -25,6 +31,8 @@ export default class BuildCommand extends FlavoredCommand {
|
|
|
25
31
|
const { monorepo } = getContext();
|
|
26
32
|
return monorepo.run(new BuildComponentsOperation(), {
|
|
27
33
|
dryRun: flags['dry-run'],
|
|
34
|
+
force: flags.force,
|
|
35
|
+
silent: flags.json,
|
|
28
36
|
components: argv.length > 0
|
|
29
37
|
? argv
|
|
30
38
|
: monorepo.components.map((c) => c.name),
|
|
@@ -8,6 +8,7 @@ export default class RunTask extends Command {
|
|
|
8
8
|
static examples: string[];
|
|
9
9
|
static flags: {
|
|
10
10
|
executor: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'all-matching': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
};
|
|
12
13
|
static strict: boolean;
|
|
13
14
|
run(): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getContext } from '../../../index.js';
|
|
1
|
+
import { AmbiguousTaskError, getContext } from '../../../index.js';
|
|
2
2
|
import { Args, Command, Flags } from '@oclif/core';
|
|
3
3
|
import { ExecutorType, RunTasksOperation } from '../../../monorepo/index.js';
|
|
4
4
|
export default class RunTask extends Command {
|
|
@@ -18,14 +18,32 @@ export default class RunTask extends Command {
|
|
|
18
18
|
description: 'Where to run the task. (experimental!)',
|
|
19
19
|
options: Object.values(ExecutorType),
|
|
20
20
|
}),
|
|
21
|
+
'all-matching': Flags.boolean({
|
|
22
|
+
name: 'all-matching',
|
|
23
|
+
char: 'a',
|
|
24
|
+
description: 'Run all tasks matching (when multiple matches)',
|
|
25
|
+
default: false,
|
|
26
|
+
}),
|
|
21
27
|
};
|
|
22
28
|
static strict = false;
|
|
23
29
|
async run() {
|
|
24
30
|
const { argv, flags } = await this.parse(RunTask);
|
|
25
31
|
const { monorepo } = await getContext();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
try {
|
|
33
|
+
await monorepo.run(new RunTasksOperation(), {
|
|
34
|
+
tasks: argv,
|
|
35
|
+
executor: flags.executor,
|
|
36
|
+
allMatching: flags['all-matching'],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (error instanceof AmbiguousTaskError) {
|
|
41
|
+
throw error.toCliError([
|
|
42
|
+
`Specify just one. Eg: \`emb tasks run ${error.options[0]}\``,
|
|
43
|
+
'Run the same command with --all-matches / -a',
|
|
44
|
+
'Review the list of tasks by running `emb tasks`',
|
|
45
|
+
]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
30
48
|
}
|
|
31
49
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getContext } from '../../../index.js';
|
|
2
|
-
import { Listr } from 'listr2';
|
|
3
2
|
import * as z from 'zod';
|
|
4
|
-
import { ExecuteLocalCommandOperation } from '../../../monorepo/index.js';
|
|
3
|
+
import { ExecuteLocalCommandOperation, taskManagerFactory } from '../../../monorepo/index.js';
|
|
5
4
|
import { AbstractOperation } from '../../../operations/index.js';
|
|
6
5
|
/**
|
|
7
6
|
* https://docs.docker.com/reference/cli/docker/compose/up/
|
|
@@ -20,20 +19,22 @@ export class ComposeUpOperation extends AbstractOperation {
|
|
|
20
19
|
}
|
|
21
20
|
async _run(input) {
|
|
22
21
|
const { monorepo } = getContext();
|
|
22
|
+
const manager = taskManagerFactory();
|
|
23
23
|
const command = ['docker', 'compose', 'up', '-d'];
|
|
24
24
|
if (input?.forceRecreate) {
|
|
25
25
|
command.push('--force-recreate');
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
manager.add([
|
|
28
|
+
{
|
|
29
|
+
async task() {
|
|
30
|
+
return monorepo.run(new ExecuteLocalCommandOperation(), {
|
|
31
|
+
script: command.join(' '),
|
|
32
|
+
workingDir: monorepo.rootDir,
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
title: 'Starting project',
|
|
34
36
|
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
await task.run();
|
|
37
|
+
]);
|
|
38
|
+
await manager.runAll();
|
|
38
39
|
}
|
|
39
40
|
}
|
|
@@ -55,7 +55,7 @@ export class BuildImageOperation extends AbstractOperation {
|
|
|
55
55
|
try {
|
|
56
56
|
const { vertexes } = await decodeBuildkitStatusResponse(trace.aux);
|
|
57
57
|
vertexes.forEach((v) => {
|
|
58
|
-
logStream.write(JSON.stringify(v) + '\n');
|
|
58
|
+
// logStream.write(JSON.stringify(v) + '\n');
|
|
59
59
|
this.observer?.(v.name);
|
|
60
60
|
});
|
|
61
61
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export declare class EMBError extends Error {
|
|
2
|
+
code: string;
|
|
3
|
+
message: string;
|
|
4
|
+
constructor(code: string, message: string);
|
|
5
|
+
toCliError(suggestions: string[], ref?: string): CliError;
|
|
6
|
+
}
|
|
7
|
+
export declare class CliError extends EMBError {
|
|
8
|
+
/**
|
|
9
|
+
* a unique error code for this error class
|
|
10
|
+
*/
|
|
11
|
+
code: string;
|
|
12
|
+
/**
|
|
13
|
+
* message to display related to the error
|
|
14
|
+
*/
|
|
15
|
+
message: string;
|
|
16
|
+
/**
|
|
17
|
+
* a suggestion that may be useful or provide additional context
|
|
18
|
+
*/
|
|
19
|
+
suggestions?: string[] | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* a url to find out more information related to this error
|
|
22
|
+
* or fixing the error
|
|
23
|
+
*/
|
|
24
|
+
ref?: string | undefined;
|
|
25
|
+
constructor(
|
|
26
|
+
/**
|
|
27
|
+
* a unique error code for this error class
|
|
28
|
+
*/
|
|
29
|
+
code: string,
|
|
30
|
+
/**
|
|
31
|
+
* message to display related to the error
|
|
32
|
+
*/
|
|
33
|
+
message: string,
|
|
34
|
+
/**
|
|
35
|
+
* a suggestion that may be useful or provide additional context
|
|
36
|
+
*/
|
|
37
|
+
suggestions?: string[] | undefined,
|
|
38
|
+
/**
|
|
39
|
+
* a url to find out more information related to this error
|
|
40
|
+
* or fixing the error
|
|
41
|
+
*/
|
|
42
|
+
ref?: string | undefined);
|
|
43
|
+
}
|
|
44
|
+
export declare class AmbiguousTaskError extends EMBError {
|
|
45
|
+
options: string[];
|
|
46
|
+
constructor(msg: string, options: string[]);
|
|
47
|
+
}
|
|
48
|
+
export declare class UnkownReferenceError extends EMBError {
|
|
49
|
+
constructor(msg: string);
|
|
50
|
+
}
|
|
51
|
+
export declare class TaskNameCollisionError extends EMBError {
|
|
52
|
+
collisions: Array<string>;
|
|
53
|
+
constructor(msg: string, collisions: Array<string>);
|
|
54
|
+
}
|
|
55
|
+
export declare class CircularDependencyError extends EMBError {
|
|
56
|
+
constructor(msg: string);
|
|
57
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export class EMBError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
message;
|
|
4
|
+
constructor(code, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.message = message;
|
|
8
|
+
}
|
|
9
|
+
toCliError(suggestions, ref) {
|
|
10
|
+
return new CliError(this.code, this.message, suggestions, ref);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class CliError extends EMBError {
|
|
14
|
+
code;
|
|
15
|
+
message;
|
|
16
|
+
suggestions;
|
|
17
|
+
ref;
|
|
18
|
+
constructor(
|
|
19
|
+
/**
|
|
20
|
+
* a unique error code for this error class
|
|
21
|
+
*/
|
|
22
|
+
code,
|
|
23
|
+
/**
|
|
24
|
+
* message to display related to the error
|
|
25
|
+
*/
|
|
26
|
+
message,
|
|
27
|
+
/**
|
|
28
|
+
* a suggestion that may be useful or provide additional context
|
|
29
|
+
*/
|
|
30
|
+
suggestions,
|
|
31
|
+
/**
|
|
32
|
+
* a url to find out more information related to this error
|
|
33
|
+
* or fixing the error
|
|
34
|
+
*/
|
|
35
|
+
ref) {
|
|
36
|
+
super(code, message);
|
|
37
|
+
this.code = code;
|
|
38
|
+
this.message = message;
|
|
39
|
+
this.suggestions = suggestions;
|
|
40
|
+
this.ref = ref;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export class AmbiguousTaskError extends EMBError {
|
|
44
|
+
options;
|
|
45
|
+
constructor(msg, options) {
|
|
46
|
+
super('AMBIG_TASK', msg);
|
|
47
|
+
this.options = options;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export class UnkownReferenceError extends EMBError {
|
|
51
|
+
constructor(msg) {
|
|
52
|
+
super('UNKNOWN_REF', msg);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export class TaskNameCollisionError extends EMBError {
|
|
56
|
+
collisions;
|
|
57
|
+
constructor(msg, collisions) {
|
|
58
|
+
super('UNKNOWN_REF', msg);
|
|
59
|
+
this.collisions = collisions;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export class CircularDependencyError extends EMBError {
|
|
63
|
+
constructor(msg) {
|
|
64
|
+
super('CIRCULAR_DEPS', msg);
|
|
65
|
+
}
|
|
66
|
+
}
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -24,6 +24,7 @@ export declare class Monorepo {
|
|
|
24
24
|
expand(str: string, expander?: TemplateExpander): Promise<string>;
|
|
25
25
|
expand<R extends Record<string, unknown>>(record: R, expander?: TemplateExpander): Promise<R>;
|
|
26
26
|
private installStore;
|
|
27
|
+
private installEnv;
|
|
27
28
|
init(): Promise<Monorepo>;
|
|
28
29
|
join(...paths: string[]): string;
|
|
29
30
|
run<I, O>(operation: IOperation<I, O>, args: I): Promise<O>;
|
|
@@ -84,6 +84,19 @@ export class Monorepo {
|
|
|
84
84
|
this._store = store || new EMBStore(this);
|
|
85
85
|
await this._store.init();
|
|
86
86
|
}
|
|
87
|
+
async installEnv() {
|
|
88
|
+
// Expand env vars at the init and then we don't expand anymore
|
|
89
|
+
// The only available source for them is the existing env
|
|
90
|
+
const expander = new TemplateExpander();
|
|
91
|
+
const options = {
|
|
92
|
+
default: 'env',
|
|
93
|
+
sources: {
|
|
94
|
+
env: process.env,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
const expanded = await expander.expandRecord(this._config.env, options);
|
|
98
|
+
Object.assign(process.env, expanded);
|
|
99
|
+
}
|
|
87
100
|
// Initialize
|
|
88
101
|
async init() {
|
|
89
102
|
if (this.initialized) {
|
|
@@ -98,17 +111,7 @@ export class Monorepo {
|
|
|
98
111
|
const newConfig = await plugin.extendConfig?.(await pConfig);
|
|
99
112
|
return newConfig ?? pConfig;
|
|
100
113
|
}, Promise.resolve(this._config));
|
|
101
|
-
|
|
102
|
-
// The only available source for them is the existing env
|
|
103
|
-
const expander = new TemplateExpander();
|
|
104
|
-
const options = {
|
|
105
|
-
default: 'env',
|
|
106
|
-
sources: {
|
|
107
|
-
env: process.env,
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
const expanded = await expander.expandRecord(this._config.env, options);
|
|
111
|
-
Object.assign(process.env, expanded);
|
|
114
|
+
await this.installEnv();
|
|
112
115
|
this.initialized = true;
|
|
113
116
|
await Promise.all(plugins.map(async (p) => {
|
|
114
117
|
await p.init?.();
|
|
@@ -125,6 +128,7 @@ export class Monorepo {
|
|
|
125
128
|
async withFlavor(name) {
|
|
126
129
|
const repo = new Monorepo(this._config.withFlavor(name));
|
|
127
130
|
await repo.installStore();
|
|
131
|
+
await repo.installEnv();
|
|
128
132
|
return repo;
|
|
129
133
|
}
|
|
130
134
|
}
|
|
@@ -4,6 +4,7 @@ import { AbstractOperation } from '../../../operations/index.js';
|
|
|
4
4
|
export type BuildComponentMeta = {
|
|
5
5
|
dryRun?: boolean;
|
|
6
6
|
cacheHit?: boolean;
|
|
7
|
+
force?: boolean;
|
|
7
8
|
build: DockerComponentBuild;
|
|
8
9
|
preBuildMeta?: string;
|
|
9
10
|
sentinelFile: string;
|
|
@@ -11,6 +12,8 @@ export type BuildComponentMeta = {
|
|
|
11
12
|
declare const schema: z.ZodObject<{
|
|
12
13
|
components: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13
14
|
dryRun: z.ZodOptional<z.ZodBoolean>;
|
|
15
|
+
silent: z.ZodOptional<z.ZodBoolean>;
|
|
16
|
+
force: z.ZodOptional<z.ZodBoolean>;
|
|
14
17
|
}, z.core.$strip>;
|
|
15
18
|
export declare class BuildComponentsOperation extends AbstractOperation<typeof schema, Record<string, BuildComponentMeta>> {
|
|
16
19
|
constructor();
|
|
@@ -13,6 +13,14 @@ const schema = z.object({
|
|
|
13
13
|
.boolean()
|
|
14
14
|
.optional()
|
|
15
15
|
.describe('Do not build but return the config that would be used to build the images'),
|
|
16
|
+
silent: z
|
|
17
|
+
.boolean()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe('Do not produce any output on the terminal'),
|
|
20
|
+
force: z
|
|
21
|
+
.boolean()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Bypass the cache and force the build'),
|
|
16
24
|
});
|
|
17
25
|
export class BuildComponentsOperation extends AbstractOperation {
|
|
18
26
|
constructor() {
|
|
@@ -28,22 +36,39 @@ export class BuildComponentsOperation extends AbstractOperation {
|
|
|
28
36
|
forbidIdNameCollision: true,
|
|
29
37
|
});
|
|
30
38
|
const ordered = findRunOrder(selection.map((s) => s.name), collection);
|
|
31
|
-
const tasks =
|
|
39
|
+
const tasks = ordered.map((cmp) => {
|
|
32
40
|
return {
|
|
33
41
|
task: async (context, task) => {
|
|
34
|
-
return this.buildComponent(cmp, task, context,
|
|
42
|
+
return this.buildComponent(cmp, task, context, {
|
|
43
|
+
dryRun: input.dryRun,
|
|
44
|
+
force: input.force,
|
|
45
|
+
});
|
|
35
46
|
},
|
|
36
47
|
title: `Building ${cmp.name}`,
|
|
37
48
|
};
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
});
|
|
50
|
+
manager.add([
|
|
51
|
+
{
|
|
52
|
+
title: 'Build images',
|
|
53
|
+
async task(ctx, task) {
|
|
54
|
+
return task.newListr([...tasks], {
|
|
55
|
+
rendererOptions: {
|
|
56
|
+
collapseSubtasks: true,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
], {
|
|
62
|
+
rendererOptions: {
|
|
63
|
+
collapseSkips: false,
|
|
64
|
+
collapseSubtasks: false,
|
|
65
|
+
},
|
|
41
66
|
ctx: {},
|
|
42
67
|
});
|
|
43
|
-
const results = await
|
|
68
|
+
const results = await manager.runAll();
|
|
44
69
|
return results;
|
|
45
70
|
}
|
|
46
|
-
async buildComponent(cmp, parentTask, parentContext,
|
|
71
|
+
async buildComponent(cmp, parentTask, parentContext, options) {
|
|
47
72
|
const prereqPlugin = new FilePrerequisitePlugin();
|
|
48
73
|
const list = parentTask.newListr([
|
|
49
74
|
// Collect all the prerequisites and other build infos
|
|
@@ -52,6 +77,7 @@ export class BuildComponentsOperation extends AbstractOperation {
|
|
|
52
77
|
async task(ctx) {
|
|
53
78
|
// Install the context for this specific component build chain
|
|
54
79
|
ctx.cacheHit = false;
|
|
80
|
+
ctx.force = options?.force;
|
|
55
81
|
ctx.sentinelFile = getSentinelFile(cmp);
|
|
56
82
|
ctx.build = await cmp.toDockerBuild();
|
|
57
83
|
},
|
|
@@ -59,6 +85,9 @@ export class BuildComponentsOperation extends AbstractOperation {
|
|
|
59
85
|
},
|
|
60
86
|
// Check for sentinal information to see if the build can be skipped
|
|
61
87
|
{
|
|
88
|
+
skip(ctx) {
|
|
89
|
+
return Boolean(ctx.force);
|
|
90
|
+
},
|
|
62
91
|
task: async (ctx) => {
|
|
63
92
|
ctx.preBuildMeta = await prereqPlugin.meta(cmp, ctx.build.prerequisites, 'pre');
|
|
64
93
|
let lastValue;
|
|
@@ -70,19 +99,17 @@ export class BuildComponentsOperation extends AbstractOperation {
|
|
|
70
99
|
}
|
|
71
100
|
if (lastValue) {
|
|
72
101
|
const diff = await prereqPlugin.diff(cmp, ctx.build.prerequisites, lastValue, ctx.preBuildMeta);
|
|
73
|
-
if (!diff) {
|
|
102
|
+
if (!ctx.force && !diff) {
|
|
74
103
|
ctx.cacheHit = true;
|
|
75
|
-
|
|
104
|
+
parentTask.skip(`${parentTask.title} (cache hit)`);
|
|
76
105
|
}
|
|
77
106
|
}
|
|
78
107
|
},
|
|
79
108
|
title: 'Checking prerequisites',
|
|
80
109
|
},
|
|
81
110
|
{
|
|
111
|
+
skip: (ctx) => !ctx.force && (Boolean(ctx.cacheHit) || Boolean(ctx.dryRun)),
|
|
82
112
|
task: async (ctx, task) => {
|
|
83
|
-
if (ctx.cacheHit || ctx.dryRun) {
|
|
84
|
-
return task.skip();
|
|
85
|
-
}
|
|
86
113
|
const title = `Building image ${ctx.build.name}:${ctx.build.tag}`;
|
|
87
114
|
task.title = title;
|
|
88
115
|
const op = new BuildImageOperation((progress) => {
|
|
@@ -104,10 +131,8 @@ export class BuildComponentsOperation extends AbstractOperation {
|
|
|
104
131
|
},
|
|
105
132
|
// Update sentinel file
|
|
106
133
|
{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return task.skip();
|
|
110
|
-
}
|
|
134
|
+
skip: (ctx) => !ctx.force && (Boolean(ctx.cacheHit) || Boolean(ctx.dryRun)),
|
|
135
|
+
task: async (ctx) => {
|
|
111
136
|
const sentinelValue = await prereqPlugin.meta(cmp, ctx.build.prerequisites, 'post');
|
|
112
137
|
await this.context.monorepo.store.writeFile(ctx.sentinelFile, sentinelValue);
|
|
113
138
|
},
|
|
@@ -117,13 +142,15 @@ export class BuildComponentsOperation extends AbstractOperation {
|
|
|
117
142
|
// Return build meta data
|
|
118
143
|
async task(ctx) {
|
|
119
144
|
parentContext[cmp.name] = ctx;
|
|
120
|
-
if (ctx.dryRun) {
|
|
121
|
-
|
|
145
|
+
if (!ctx.force && ctx.dryRun) {
|
|
146
|
+
parentTask.skip(`${parentTask.title} (dry run)`);
|
|
122
147
|
}
|
|
123
148
|
},
|
|
124
149
|
},
|
|
125
150
|
], {
|
|
126
|
-
ctx: {
|
|
151
|
+
ctx: {
|
|
152
|
+
...options,
|
|
153
|
+
},
|
|
127
154
|
});
|
|
128
155
|
return list;
|
|
129
156
|
}
|
|
@@ -8,13 +8,12 @@ export declare enum ExecutorType {
|
|
|
8
8
|
export type RunTasksOperationParams = {
|
|
9
9
|
tasks: Array<string>;
|
|
10
10
|
executor?: ExecutorType | undefined;
|
|
11
|
+
allMatching?: boolean;
|
|
11
12
|
};
|
|
12
13
|
export type TaskWithScript = TaskInfo & {
|
|
13
14
|
script: string;
|
|
14
15
|
};
|
|
15
16
|
export declare class RunTasksOperation implements IOperation<RunTasksOperationParams, Array<TaskInfo>> {
|
|
16
|
-
protected out?: Writable | undefined;
|
|
17
|
-
constructor(out?: Writable | undefined);
|
|
18
17
|
run(params: RunTasksOperationParams): Promise<Array<TaskInfo>>;
|
|
19
18
|
protected runDocker(task: TaskWithScript, out?: Writable): Promise<void>;
|
|
20
19
|
protected runLocal(task: TaskWithScript): Promise<import("stream").Readable>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getContext } from '../../../index.js';
|
|
2
|
-
import { Manager } from '@listr2/manager';
|
|
3
2
|
import { ContainerExecOperation } from '../../../docker/index.js';
|
|
4
|
-
import { EMBCollection, findRunOrder } from '../../index.js';
|
|
3
|
+
import { EMBCollection, findRunOrder, taskManagerFactory, } from '../../index.js';
|
|
5
4
|
import { ExecuteLocalCommandOperation, GetComponentContainerOperation, } from '../index.js';
|
|
6
5
|
export var ExecutorType;
|
|
7
6
|
(function (ExecutorType) {
|
|
@@ -9,10 +8,6 @@ export var ExecutorType;
|
|
|
9
8
|
ExecutorType["local"] = "local";
|
|
10
9
|
})(ExecutorType || (ExecutorType = {}));
|
|
11
10
|
export class RunTasksOperation {
|
|
12
|
-
out;
|
|
13
|
-
constructor(out) {
|
|
14
|
-
this.out = out;
|
|
15
|
-
}
|
|
16
11
|
async run(params) {
|
|
17
12
|
const { monorepo } = getContext();
|
|
18
13
|
// First ensure the selection is valid (user can use task IDs or names)
|
|
@@ -20,16 +15,11 @@ export class RunTasksOperation {
|
|
|
20
15
|
idField: 'id',
|
|
21
16
|
depField: 'pre',
|
|
22
17
|
});
|
|
23
|
-
const ordered = findRunOrder(params.tasks, collection
|
|
24
|
-
|
|
25
|
-
concurrent: false,
|
|
26
|
-
exitOnError: false,
|
|
27
|
-
rendererOptions: {
|
|
28
|
-
collapseSubtasks: false,
|
|
29
|
-
collapseSkips: false,
|
|
30
|
-
},
|
|
18
|
+
const ordered = findRunOrder(params.tasks, collection, {
|
|
19
|
+
onAmbiguous: params.allMatching ? 'runAll' : 'error',
|
|
31
20
|
});
|
|
32
|
-
const
|
|
21
|
+
const manager = taskManagerFactory();
|
|
22
|
+
manager.add(ordered.map((task) => {
|
|
33
23
|
return {
|
|
34
24
|
rendererOptions: {
|
|
35
25
|
persistentOutput: true,
|
|
@@ -58,7 +48,7 @@ export class RunTasksOperation {
|
|
|
58
48
|
title: `Running ${task.id}`,
|
|
59
49
|
};
|
|
60
50
|
}));
|
|
61
|
-
await
|
|
51
|
+
await manager.runAll();
|
|
62
52
|
return ordered;
|
|
63
53
|
}
|
|
64
54
|
async runDocker(task, out) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import graphlib from 'graphlib';
|
|
2
|
+
import { AmbiguousTaskError, CircularDependencyError, TaskNameCollisionError, UnkownReferenceError, } from '../../errors.js';
|
|
2
3
|
export class EMBCollection {
|
|
3
4
|
items;
|
|
4
5
|
idField;
|
|
@@ -63,8 +64,7 @@ export class EMBCollection {
|
|
|
63
64
|
parts.push(`id↔name collisions (${collisions.length}):\n` +
|
|
64
65
|
collisions.join('\n'));
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
-
throw new Error(parts.join('\n\n'));
|
|
67
|
+
throw new TaskNameCollisionError('Collision between task names and ids', parts);
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
/** All items (stable array iteration) */
|
|
@@ -83,13 +83,13 @@ export class EMBCollection {
|
|
|
83
83
|
return opts?.multiple ? [idHit] : idHit;
|
|
84
84
|
const nameHits = this.byName.get(ref) ?? [];
|
|
85
85
|
if (nameHits.length === 0) {
|
|
86
|
-
throw new
|
|
86
|
+
throw new UnkownReferenceError(`Unknown reference "${ref}"`);
|
|
87
87
|
}
|
|
88
88
|
if (opts?.multiple)
|
|
89
89
|
return nameHits;
|
|
90
90
|
if (nameHits.length > 1) {
|
|
91
|
-
const ids = nameHits.map((t) => this.idOf(t))
|
|
92
|
-
throw new
|
|
91
|
+
const ids = nameHits.map((t) => this.idOf(t));
|
|
92
|
+
throw new AmbiguousTaskError(`Ambiguous reference "${ref}" matches multiple id/name`, ids);
|
|
93
93
|
}
|
|
94
94
|
return nameHits[0];
|
|
95
95
|
}
|
|
@@ -136,7 +136,7 @@ export function findRunOrder(selection, collection, { onAmbiguous = 'error' } =
|
|
|
136
136
|
const g = buildGraph(collection, onAmbiguous);
|
|
137
137
|
const cycles = graphlib.alg.findCycles(g);
|
|
138
138
|
if (cycles.length > 0) {
|
|
139
|
-
throw new
|
|
139
|
+
throw new CircularDependencyError(`Circular dependencies detected: ${JSON.stringify(cycles)}`);
|
|
140
140
|
}
|
|
141
141
|
const selectedIds = new Set();
|
|
142
142
|
for (const ref of selection)
|
package/oclif.manifest.json
CHANGED
|
@@ -164,6 +164,14 @@
|
|
|
164
164
|
"required": false,
|
|
165
165
|
"allowNo": false,
|
|
166
166
|
"type": "boolean"
|
|
167
|
+
},
|
|
168
|
+
"force": {
|
|
169
|
+
"char": "f",
|
|
170
|
+
"description": "Bypass the cache and force the build",
|
|
171
|
+
"name": "force",
|
|
172
|
+
"required": false,
|
|
173
|
+
"allowNo": false,
|
|
174
|
+
"type": "boolean"
|
|
167
175
|
}
|
|
168
176
|
},
|
|
169
177
|
"hasDynamicHelp": false,
|
|
@@ -226,12 +234,10 @@
|
|
|
226
234
|
"index.js"
|
|
227
235
|
]
|
|
228
236
|
},
|
|
229
|
-
"
|
|
230
|
-
"aliases": [
|
|
231
|
-
"ps"
|
|
232
|
-
],
|
|
237
|
+
"config:print": {
|
|
238
|
+
"aliases": [],
|
|
233
239
|
"args": {},
|
|
234
|
-
"description": "
|
|
240
|
+
"description": "Print the current config.",
|
|
235
241
|
"examples": [
|
|
236
242
|
"<%= config.bin %> <%= command.id %>"
|
|
237
243
|
],
|
|
@@ -243,18 +249,18 @@
|
|
|
243
249
|
"allowNo": false,
|
|
244
250
|
"type": "boolean"
|
|
245
251
|
},
|
|
246
|
-
"
|
|
247
|
-
"
|
|
248
|
-
"
|
|
249
|
-
"name": "all",
|
|
252
|
+
"flavor": {
|
|
253
|
+
"description": "Specify the flavor to use.",
|
|
254
|
+
"name": "flavor",
|
|
250
255
|
"required": false,
|
|
251
|
-
"
|
|
252
|
-
"
|
|
256
|
+
"hasDynamicHelp": false,
|
|
257
|
+
"multiple": false,
|
|
258
|
+
"type": "option"
|
|
253
259
|
}
|
|
254
260
|
},
|
|
255
261
|
"hasDynamicHelp": false,
|
|
256
262
|
"hiddenAliases": [],
|
|
257
|
-
"id": "
|
|
263
|
+
"id": "config:print",
|
|
258
264
|
"pluginAlias": "@enspirit/emb",
|
|
259
265
|
"pluginName": "@enspirit/emb",
|
|
260
266
|
"pluginType": "core",
|
|
@@ -266,14 +272,16 @@
|
|
|
266
272
|
"src",
|
|
267
273
|
"cli",
|
|
268
274
|
"commands",
|
|
269
|
-
"
|
|
270
|
-
"
|
|
275
|
+
"config",
|
|
276
|
+
"print.js"
|
|
271
277
|
]
|
|
272
278
|
},
|
|
273
|
-
"containers
|
|
274
|
-
"aliases": [
|
|
279
|
+
"containers": {
|
|
280
|
+
"aliases": [
|
|
281
|
+
"ps"
|
|
282
|
+
],
|
|
275
283
|
"args": {},
|
|
276
|
-
"description": "
|
|
284
|
+
"description": "List docker containers.",
|
|
277
285
|
"examples": [
|
|
278
286
|
"<%= config.bin %> <%= command.id %>"
|
|
279
287
|
],
|
|
@@ -284,11 +292,19 @@
|
|
|
284
292
|
"name": "json",
|
|
285
293
|
"allowNo": false,
|
|
286
294
|
"type": "boolean"
|
|
295
|
+
},
|
|
296
|
+
"all": {
|
|
297
|
+
"char": "a",
|
|
298
|
+
"description": "Retun all containers. By default, only running containers are shown",
|
|
299
|
+
"name": "all",
|
|
300
|
+
"required": false,
|
|
301
|
+
"allowNo": false,
|
|
302
|
+
"type": "boolean"
|
|
287
303
|
}
|
|
288
304
|
},
|
|
289
305
|
"hasDynamicHelp": false,
|
|
290
306
|
"hiddenAliases": [],
|
|
291
|
-
"id": "containers
|
|
307
|
+
"id": "containers",
|
|
292
308
|
"pluginAlias": "@enspirit/emb",
|
|
293
309
|
"pluginName": "@enspirit/emb",
|
|
294
310
|
"pluginType": "core",
|
|
@@ -301,13 +317,13 @@
|
|
|
301
317
|
"cli",
|
|
302
318
|
"commands",
|
|
303
319
|
"containers",
|
|
304
|
-
"
|
|
320
|
+
"index.js"
|
|
305
321
|
]
|
|
306
322
|
},
|
|
307
|
-
"
|
|
323
|
+
"containers:prune": {
|
|
308
324
|
"aliases": [],
|
|
309
325
|
"args": {},
|
|
310
|
-
"description": "
|
|
326
|
+
"description": "Prune containers.",
|
|
311
327
|
"examples": [
|
|
312
328
|
"<%= config.bin %> <%= command.id %>"
|
|
313
329
|
],
|
|
@@ -318,19 +334,11 @@
|
|
|
318
334
|
"name": "json",
|
|
319
335
|
"allowNo": false,
|
|
320
336
|
"type": "boolean"
|
|
321
|
-
},
|
|
322
|
-
"flavor": {
|
|
323
|
-
"description": "Specify the flavor to use.",
|
|
324
|
-
"name": "flavor",
|
|
325
|
-
"required": false,
|
|
326
|
-
"hasDynamicHelp": false,
|
|
327
|
-
"multiple": false,
|
|
328
|
-
"type": "option"
|
|
329
337
|
}
|
|
330
338
|
},
|
|
331
339
|
"hasDynamicHelp": false,
|
|
332
340
|
"hiddenAliases": [],
|
|
333
|
-
"id": "
|
|
341
|
+
"id": "containers:prune",
|
|
334
342
|
"pluginAlias": "@enspirit/emb",
|
|
335
343
|
"pluginName": "@enspirit/emb",
|
|
336
344
|
"pluginType": "core",
|
|
@@ -342,8 +350,8 @@
|
|
|
342
350
|
"src",
|
|
343
351
|
"cli",
|
|
344
352
|
"commands",
|
|
345
|
-
"
|
|
346
|
-
"
|
|
353
|
+
"containers",
|
|
354
|
+
"prune.js"
|
|
347
355
|
]
|
|
348
356
|
},
|
|
349
357
|
"images:delete": {
|
|
@@ -538,6 +546,13 @@
|
|
|
538
546
|
"local"
|
|
539
547
|
],
|
|
540
548
|
"type": "option"
|
|
549
|
+
},
|
|
550
|
+
"all-matching": {
|
|
551
|
+
"char": "a",
|
|
552
|
+
"description": "Run all tasks matching (when multiple matches)",
|
|
553
|
+
"name": "all-matching",
|
|
554
|
+
"allowNo": false,
|
|
555
|
+
"type": "boolean"
|
|
541
556
|
}
|
|
542
557
|
},
|
|
543
558
|
"hasDynamicHelp": false,
|
|
@@ -559,5 +574,5 @@
|
|
|
559
574
|
]
|
|
560
575
|
}
|
|
561
576
|
},
|
|
562
|
-
"version": "0.0.
|
|
577
|
+
"version": "0.0.9"
|
|
563
578
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enspirit/emb",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.9",
|
|
5
5
|
"keywords": ["monorepo", "docker", "taskrunner", "ci", "docker compose", "sentinel", "makefile"],
|
|
6
6
|
"author": "Louis Lambeau <louis.lambeau@enspirit.be>",
|
|
7
7
|
"license": "ISC",
|
|
@@ -109,6 +109,11 @@
|
|
|
109
109
|
],
|
|
110
110
|
"topicSeparator": " ",
|
|
111
111
|
"topics": {
|
|
112
|
+
"images": {"description": "List, delete, prune docker containers" },
|
|
113
|
+
"containers": {"description": "List, delete, prune docker images" },
|
|
114
|
+
"components": {"description": "List & build components resources" },
|
|
115
|
+
"config": {"description": "It's all about config" },
|
|
116
|
+
"tasks": {"description": "List and run tasks" }
|
|
112
117
|
}
|
|
113
118
|
}
|
|
114
119
|
}
|