@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 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.7 darwin-x64 node-v22.12.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
- --dry-run Do not build the components but only produce build meta information
130
- --flavor=<value> Specify the flavor to use.
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
- await monorepo.run(new RunTasksOperation(), {
27
- tasks: argv,
28
- executor: flags.executor,
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
- const task = new Listr({
28
- rendererOptions: { persistentOutput: true },
29
- async task() {
30
- return monorepo.run(new ExecuteLocalCommandOperation(), {
31
- script: command.join(' '),
32
- workingDir: monorepo.rootDir,
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
- title: 'Starting project',
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
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './context.js';
2
+ export * from './errors.js';
2
3
  export * from './types.js';
3
4
  export { run } from '@oclif/core';
package/dist/src/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './context.js';
2
+ export * from './errors.js';
2
3
  export * from './types.js';
3
4
  export { run } from '@oclif/core';
@@ -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
- // Expand env vars at the init and then we don't expand anymore
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 = await Promise.all(ordered.map((cmp) => {
39
+ const tasks = ordered.map((cmp) => {
32
40
  return {
33
41
  task: async (context, task) => {
34
- return this.buildComponent(cmp, task, context, input.dryRun);
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
- const list = manager.newListr([...tasks], {
40
- rendererOptions: { persistentOutput: true },
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 list.run();
68
+ const results = await manager.runAll();
44
69
  return results;
45
70
  }
46
- async buildComponent(cmp, parentTask, parentContext, dryRun = false) {
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
- // parentTask.skip(`${parentTask.title} (cache hit)`);
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
- task: async (ctx, task) => {
108
- if (ctx.cacheHit || ctx.dryRun) {
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
- // parentTask.skip(`${parentTask.title} (dry run)`);
145
+ if (!ctx.force && ctx.dryRun) {
146
+ parentTask.skip(`${parentTask.title} (dry run)`);
122
147
  }
123
148
  },
124
149
  },
125
150
  ], {
126
- ctx: { dryRun },
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
- const runner = new Manager({
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 list = runner.newListr(ordered.map((task) => {
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 list.run();
51
+ await manager.runAll();
62
52
  return ordered;
63
53
  }
64
54
  async runDocker(task, out) {
@@ -8,6 +8,7 @@ export function taskManagerFactory(override) {
8
8
  rendererOptions: {
9
9
  collapseErrors: false,
10
10
  collapseSubtasks: false,
11
+ collapseSkips: false,
11
12
  icon: {
12
13
  [ListrDefaultRendererLogLevels.SKIPPED_WITH_COLLAPSE]: '♺',
13
14
  },
@@ -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
- // eslint-disable-next-line unicorn/error-message
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 Error(`Unknown reference "${ref}"`);
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)).join(', ');
92
- throw new Error(`Ambiguous reference "${ref}" matches multiple ids: [${ids}]`);
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 Error(`Circular dependencies detected: ${JSON.stringify(cycles)}`);
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)
@@ -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
- "containers": {
230
- "aliases": [
231
- "ps"
232
- ],
237
+ "config:print": {
238
+ "aliases": [],
233
239
  "args": {},
234
- "description": "List docker containers.",
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
- "all": {
247
- "char": "a",
248
- "description": "Retun all containers. By default, only running containers are shown",
249
- "name": "all",
252
+ "flavor": {
253
+ "description": "Specify the flavor to use.",
254
+ "name": "flavor",
250
255
  "required": false,
251
- "allowNo": false,
252
- "type": "boolean"
256
+ "hasDynamicHelp": false,
257
+ "multiple": false,
258
+ "type": "option"
253
259
  }
254
260
  },
255
261
  "hasDynamicHelp": false,
256
262
  "hiddenAliases": [],
257
- "id": "containers",
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
- "containers",
270
- "index.js"
275
+ "config",
276
+ "print.js"
271
277
  ]
272
278
  },
273
- "containers:prune": {
274
- "aliases": [],
279
+ "containers": {
280
+ "aliases": [
281
+ "ps"
282
+ ],
275
283
  "args": {},
276
- "description": "Prune containers.",
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:prune",
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
- "prune.js"
320
+ "index.js"
305
321
  ]
306
322
  },
307
- "config:print": {
323
+ "containers:prune": {
308
324
  "aliases": [],
309
325
  "args": {},
310
- "description": "Print the current config.",
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": "config:print",
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
- "config",
346
- "print.js"
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.7"
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.7",
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
  }