@enspirit/emb 0.0.8 → 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.8 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.
@@ -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,7 @@ 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,
28
35
  silent: flags.json,
29
36
  components: argv.length > 0
30
37
  ? argv
@@ -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
  }
@@ -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;
@@ -12,6 +13,7 @@ declare const schema: z.ZodObject<{
12
13
  components: z.ZodOptional<z.ZodArray<z.ZodString>>;
13
14
  dryRun: z.ZodOptional<z.ZodBoolean>;
14
15
  silent: z.ZodOptional<z.ZodBoolean>;
16
+ force: z.ZodOptional<z.ZodBoolean>;
15
17
  }, z.core.$strip>;
16
18
  export declare class BuildComponentsOperation extends AbstractOperation<typeof schema, Record<string, BuildComponentMeta>> {
17
19
  constructor();
@@ -17,6 +17,10 @@ const schema = z.object({
17
17
  .boolean()
18
18
  .optional()
19
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'),
20
24
  });
21
25
  export class BuildComponentsOperation extends AbstractOperation {
22
26
  constructor() {
@@ -32,23 +36,39 @@ export class BuildComponentsOperation extends AbstractOperation {
32
36
  forbidIdNameCollision: true,
33
37
  });
34
38
  const ordered = findRunOrder(selection.map((s) => s.name), collection);
35
- const tasks = await Promise.all(ordered.map((cmp) => {
39
+ const tasks = ordered.map((cmp) => {
36
40
  return {
37
41
  task: async (context, task) => {
38
- return this.buildComponent(cmp, task, context, input.dryRun);
42
+ return this.buildComponent(cmp, task, context, {
43
+ dryRun: input.dryRun,
44
+ force: input.force,
45
+ });
39
46
  },
40
47
  title: `Building ${cmp.name}`,
41
48
  };
42
- }));
43
- const list = manager.newListr([...tasks], {
44
- renderer: input.silent ? 'silent' : 'default',
45
- 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
+ },
46
66
  ctx: {},
47
67
  });
48
- const results = await list.run();
68
+ const results = await manager.runAll();
49
69
  return results;
50
70
  }
51
- async buildComponent(cmp, parentTask, parentContext, dryRun = false) {
71
+ async buildComponent(cmp, parentTask, parentContext, options) {
52
72
  const prereqPlugin = new FilePrerequisitePlugin();
53
73
  const list = parentTask.newListr([
54
74
  // Collect all the prerequisites and other build infos
@@ -57,6 +77,7 @@ export class BuildComponentsOperation extends AbstractOperation {
57
77
  async task(ctx) {
58
78
  // Install the context for this specific component build chain
59
79
  ctx.cacheHit = false;
80
+ ctx.force = options?.force;
60
81
  ctx.sentinelFile = getSentinelFile(cmp);
61
82
  ctx.build = await cmp.toDockerBuild();
62
83
  },
@@ -64,6 +85,9 @@ export class BuildComponentsOperation extends AbstractOperation {
64
85
  },
65
86
  // Check for sentinal information to see if the build can be skipped
66
87
  {
88
+ skip(ctx) {
89
+ return Boolean(ctx.force);
90
+ },
67
91
  task: async (ctx) => {
68
92
  ctx.preBuildMeta = await prereqPlugin.meta(cmp, ctx.build.prerequisites, 'pre');
69
93
  let lastValue;
@@ -75,7 +99,7 @@ export class BuildComponentsOperation extends AbstractOperation {
75
99
  }
76
100
  if (lastValue) {
77
101
  const diff = await prereqPlugin.diff(cmp, ctx.build.prerequisites, lastValue, ctx.preBuildMeta);
78
- if (!diff) {
102
+ if (!ctx.force && !diff) {
79
103
  ctx.cacheHit = true;
80
104
  parentTask.skip(`${parentTask.title} (cache hit)`);
81
105
  }
@@ -84,10 +108,8 @@ export class BuildComponentsOperation extends AbstractOperation {
84
108
  title: 'Checking prerequisites',
85
109
  },
86
110
  {
111
+ skip: (ctx) => !ctx.force && (Boolean(ctx.cacheHit) || Boolean(ctx.dryRun)),
87
112
  task: async (ctx, task) => {
88
- if (ctx.cacheHit || ctx.dryRun) {
89
- return task.skip();
90
- }
91
113
  const title = `Building image ${ctx.build.name}:${ctx.build.tag}`;
92
114
  task.title = title;
93
115
  const op = new BuildImageOperation((progress) => {
@@ -109,10 +131,8 @@ export class BuildComponentsOperation extends AbstractOperation {
109
131
  },
110
132
  // Update sentinel file
111
133
  {
112
- task: async (ctx, task) => {
113
- if (ctx.cacheHit || ctx.dryRun) {
114
- return task.skip();
115
- }
134
+ skip: (ctx) => !ctx.force && (Boolean(ctx.cacheHit) || Boolean(ctx.dryRun)),
135
+ task: async (ctx) => {
116
136
  const sentinelValue = await prereqPlugin.meta(cmp, ctx.build.prerequisites, 'post');
117
137
  await this.context.monorepo.store.writeFile(ctx.sentinelFile, sentinelValue);
118
138
  },
@@ -122,13 +142,15 @@ export class BuildComponentsOperation extends AbstractOperation {
122
142
  // Return build meta data
123
143
  async task(ctx) {
124
144
  parentContext[cmp.name] = ctx;
125
- if (ctx.dryRun) {
145
+ if (!ctx.force && ctx.dryRun) {
126
146
  parentTask.skip(`${parentTask.title} (dry run)`);
127
147
  }
128
148
  },
129
149
  },
130
150
  ], {
131
- ctx: { dryRun },
151
+ ctx: {
152
+ ...options,
153
+ },
132
154
  });
133
155
  return list;
134
156
  }
@@ -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) {
@@ -19,15 +18,8 @@ export class RunTasksOperation {
19
18
  const ordered = findRunOrder(params.tasks, collection, {
20
19
  onAmbiguous: params.allMatching ? 'runAll' : 'error',
21
20
  });
22
- const runner = new Manager({
23
- concurrent: false,
24
- exitOnError: false,
25
- rendererOptions: {
26
- collapseSubtasks: false,
27
- collapseSkips: false,
28
- },
29
- });
30
- const list = runner.newListr(ordered.map((task) => {
21
+ const manager = taskManagerFactory();
22
+ manager.add(ordered.map((task) => {
31
23
  return {
32
24
  rendererOptions: {
33
25
  persistentOutput: true,
@@ -56,7 +48,7 @@ export class RunTasksOperation {
56
48
  title: `Running ${task.id}`,
57
49
  };
58
50
  }));
59
- await list.run();
51
+ await manager.runAll();
60
52
  return ordered;
61
53
  }
62
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
  },
@@ -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,
@@ -346,10 +354,10 @@
346
354
  "prune.js"
347
355
  ]
348
356
  },
349
- "tasks": {
357
+ "images:delete": {
350
358
  "aliases": [],
351
359
  "args": {},
352
- "description": "List tasks.",
360
+ "description": "Delete project images.",
353
361
  "examples": [
354
362
  "<%= config.bin %> <%= command.id %>"
355
363
  ],
@@ -360,11 +368,19 @@
360
368
  "name": "json",
361
369
  "allowNo": false,
362
370
  "type": "boolean"
371
+ },
372
+ "force": {
373
+ "char": "f",
374
+ "description": "Remove the image even if it is being used by stopped containers or has other tags",
375
+ "name": "force",
376
+ "required": false,
377
+ "allowNo": false,
378
+ "type": "boolean"
363
379
  }
364
380
  },
365
381
  "hasDynamicHelp": false,
366
382
  "hiddenAliases": [],
367
- "id": "tasks",
383
+ "id": "images:delete",
368
384
  "pluginAlias": "@enspirit/emb",
369
385
  "pluginName": "@enspirit/emb",
370
386
  "pluginType": "core",
@@ -376,20 +392,14 @@
376
392
  "src",
377
393
  "cli",
378
394
  "commands",
379
- "tasks",
380
- "index.js"
395
+ "images",
396
+ "delete.js"
381
397
  ]
382
398
  },
383
- "tasks:run": {
399
+ "images": {
384
400
  "aliases": [],
385
- "args": {
386
- "task": {
387
- "description": "List of tasks to run. You can provide either ids or names (eg: component:task or task)",
388
- "name": "task",
389
- "required": true
390
- }
391
- },
392
- "description": "Run tasks.",
401
+ "args": {},
402
+ "description": "List docker images.",
393
403
  "examples": [
394
404
  "<%= config.bin %> <%= command.id %>"
395
405
  ],
@@ -401,33 +411,22 @@
401
411
  "allowNo": false,
402
412
  "type": "boolean"
403
413
  },
404
- "executor": {
405
- "char": "x",
406
- "description": "Where to run the task. (experimental!)",
407
- "name": "executor",
408
- "hasDynamicHelp": false,
409
- "multiple": false,
410
- "options": [
411
- "container",
412
- "local"
413
- ],
414
- "type": "option"
415
- },
416
- "all-matching": {
414
+ "all": {
417
415
  "char": "a",
418
- "description": "Run all tasks matching (when multiple matches)",
419
- "name": "all-matching",
416
+ "description": "Show all images. Only images from a final layer (no children) are shown by default.",
417
+ "name": "all",
418
+ "required": false,
420
419
  "allowNo": false,
421
420
  "type": "boolean"
422
421
  }
423
422
  },
424
423
  "hasDynamicHelp": false,
425
424
  "hiddenAliases": [],
426
- "id": "tasks:run",
425
+ "id": "images",
427
426
  "pluginAlias": "@enspirit/emb",
428
427
  "pluginName": "@enspirit/emb",
429
428
  "pluginType": "core",
430
- "strict": false,
429
+ "strict": true,
431
430
  "enableJsonFlag": true,
432
431
  "isESM": true,
433
432
  "relativePath": [
@@ -435,14 +434,14 @@
435
434
  "src",
436
435
  "cli",
437
436
  "commands",
438
- "tasks",
439
- "run.js"
437
+ "images",
438
+ "index.js"
440
439
  ]
441
440
  },
442
- "images:delete": {
441
+ "images:prune": {
443
442
  "aliases": [],
444
443
  "args": {},
445
- "description": "Delete project images.",
444
+ "description": "Prune project images.",
446
445
  "examples": [
447
446
  "<%= config.bin %> <%= command.id %>"
448
447
  ],
@@ -454,10 +453,10 @@
454
453
  "allowNo": false,
455
454
  "type": "boolean"
456
455
  },
457
- "force": {
458
- "char": "f",
459
- "description": "Remove the image even if it is being used by stopped containers or has other tags",
460
- "name": "force",
456
+ "all": {
457
+ "char": "a",
458
+ "description": "Prune all images. When set to true all images will be pruned, not only dangling ones",
459
+ "name": "all",
461
460
  "required": false,
462
461
  "allowNo": false,
463
462
  "type": "boolean"
@@ -465,7 +464,7 @@
465
464
  },
466
465
  "hasDynamicHelp": false,
467
466
  "hiddenAliases": [],
468
- "id": "images:delete",
467
+ "id": "images:prune",
469
468
  "pluginAlias": "@enspirit/emb",
470
469
  "pluginName": "@enspirit/emb",
471
470
  "pluginType": "core",
@@ -478,13 +477,13 @@
478
477
  "cli",
479
478
  "commands",
480
479
  "images",
481
- "delete.js"
480
+ "prune.js"
482
481
  ]
483
482
  },
484
- "images": {
483
+ "tasks": {
485
484
  "aliases": [],
486
485
  "args": {},
487
- "description": "List docker images.",
486
+ "description": "List tasks.",
488
487
  "examples": [
489
488
  "<%= config.bin %> <%= command.id %>"
490
489
  ],
@@ -495,19 +494,11 @@
495
494
  "name": "json",
496
495
  "allowNo": false,
497
496
  "type": "boolean"
498
- },
499
- "all": {
500
- "char": "a",
501
- "description": "Show all images. Only images from a final layer (no children) are shown by default.",
502
- "name": "all",
503
- "required": false,
504
- "allowNo": false,
505
- "type": "boolean"
506
497
  }
507
498
  },
508
499
  "hasDynamicHelp": false,
509
500
  "hiddenAliases": [],
510
- "id": "images",
501
+ "id": "tasks",
511
502
  "pluginAlias": "@enspirit/emb",
512
503
  "pluginName": "@enspirit/emb",
513
504
  "pluginType": "core",
@@ -519,14 +510,20 @@
519
510
  "src",
520
511
  "cli",
521
512
  "commands",
522
- "images",
513
+ "tasks",
523
514
  "index.js"
524
515
  ]
525
516
  },
526
- "images:prune": {
517
+ "tasks:run": {
527
518
  "aliases": [],
528
- "args": {},
529
- "description": "Prune project images.",
519
+ "args": {
520
+ "task": {
521
+ "description": "List of tasks to run. You can provide either ids or names (eg: component:task or task)",
522
+ "name": "task",
523
+ "required": true
524
+ }
525
+ },
526
+ "description": "Run tasks.",
530
527
  "examples": [
531
528
  "<%= config.bin %> <%= command.id %>"
532
529
  ],
@@ -538,22 +535,33 @@
538
535
  "allowNo": false,
539
536
  "type": "boolean"
540
537
  },
541
- "all": {
538
+ "executor": {
539
+ "char": "x",
540
+ "description": "Where to run the task. (experimental!)",
541
+ "name": "executor",
542
+ "hasDynamicHelp": false,
543
+ "multiple": false,
544
+ "options": [
545
+ "container",
546
+ "local"
547
+ ],
548
+ "type": "option"
549
+ },
550
+ "all-matching": {
542
551
  "char": "a",
543
- "description": "Prune all images. When set to true all images will be pruned, not only dangling ones",
544
- "name": "all",
545
- "required": false,
552
+ "description": "Run all tasks matching (when multiple matches)",
553
+ "name": "all-matching",
546
554
  "allowNo": false,
547
555
  "type": "boolean"
548
556
  }
549
557
  },
550
558
  "hasDynamicHelp": false,
551
559
  "hiddenAliases": [],
552
- "id": "images:prune",
560
+ "id": "tasks:run",
553
561
  "pluginAlias": "@enspirit/emb",
554
562
  "pluginName": "@enspirit/emb",
555
563
  "pluginType": "core",
556
- "strict": true,
564
+ "strict": false,
557
565
  "enableJsonFlag": true,
558
566
  "isESM": true,
559
567
  "relativePath": [
@@ -561,10 +569,10 @@
561
569
  "src",
562
570
  "cli",
563
571
  "commands",
564
- "images",
565
- "prune.js"
572
+ "tasks",
573
+ "run.js"
566
574
  ]
567
575
  }
568
576
  },
569
- "version": "0.0.8"
577
+ "version": "0.0.9"
570
578
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@enspirit/emb",
3
3
  "type": "module",
4
- "version": "0.0.8",
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",