@enspirit/emb 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +61 -13
  2. package/dist/src/cli/commands/clean.d.ts +3 -1
  3. package/dist/src/cli/commands/clean.js +13 -2
  4. package/dist/src/cli/commands/components/build.d.ts +6 -2
  5. package/dist/src/cli/commands/components/build.js +17 -7
  6. package/dist/src/cli/commands/config/print.js +2 -3
  7. package/dist/src/cli/commands/down.d.ts +2 -2
  8. package/dist/src/cli/commands/down.js +5 -25
  9. package/dist/src/cli/commands/tasks/index.js +11 -5
  10. package/dist/src/cli/commands/tasks/run.d.ts +2 -1
  11. package/dist/src/cli/commands/tasks/run.js +30 -45
  12. package/dist/src/cli/commands/up.d.ts +1 -1
  13. package/dist/src/cli/commands/up.js +12 -34
  14. package/dist/src/config/convert.js +2 -4
  15. package/dist/src/config/index.d.ts +1 -0
  16. package/dist/src/config/index.js +1 -0
  17. package/dist/src/config/schema.d.ts +32 -25
  18. package/dist/src/config/schema.json +48 -26
  19. package/dist/src/config/types.d.ts +10 -6
  20. package/dist/src/docker/compose/index.d.ts +1 -7
  21. package/dist/src/docker/compose/index.js +1 -13
  22. package/dist/src/docker/compose/operations/ComposeDownOperation.d.ts +12 -0
  23. package/dist/src/docker/compose/operations/ComposeDownOperation.js +21 -0
  24. package/dist/src/docker/compose/operations/ComposeUpOperation.d.ts +13 -0
  25. package/dist/src/docker/compose/operations/ComposeUpOperation.js +39 -0
  26. package/dist/src/docker/compose/operations/index.d.ts +2 -0
  27. package/dist/src/docker/compose/operations/index.js +2 -0
  28. package/dist/src/errors.d.ts +57 -0
  29. package/dist/src/errors.js +66 -0
  30. package/dist/src/index.d.ts +1 -0
  31. package/dist/src/index.js +1 -0
  32. package/dist/src/monorepo/component.d.ts +2 -2
  33. package/dist/src/monorepo/component.js +6 -6
  34. package/dist/src/monorepo/config.d.ts +4 -2
  35. package/dist/src/monorepo/config.js +14 -2
  36. package/dist/src/monorepo/index.d.ts +2 -0
  37. package/dist/src/monorepo/index.js +2 -0
  38. package/dist/src/monorepo/monorepo.d.ts +5 -2
  39. package/dist/src/monorepo/monorepo.js +45 -17
  40. package/dist/src/monorepo/operations/components/BuildComponentsOperation.d.ts +12 -2
  41. package/dist/src/monorepo/operations/components/BuildComponentsOperation.js +50 -59
  42. package/dist/src/monorepo/operations/components/GetComponentContainerOperation.d.ts +1 -1
  43. package/dist/src/monorepo/operations/components/GetComponentContainerOperation.js +1 -1
  44. package/dist/src/monorepo/operations/shell/ExecuteLocalCommandOperation.d.ts +4 -6
  45. package/dist/src/monorepo/operations/shell/ExecuteLocalCommandOperation.js +2 -7
  46. package/dist/src/monorepo/operations/tasks/RunTasksOperation.d.ts +20 -0
  47. package/dist/src/monorepo/operations/tasks/RunTasksOperation.js +82 -0
  48. package/dist/src/monorepo/operations/tasks/index.d.ts +1 -1
  49. package/dist/src/monorepo/operations/tasks/index.js +1 -1
  50. package/dist/src/monorepo/plugins/ComponentDiscoverPlugin.js +3 -1
  51. package/dist/src/monorepo/plugins/EmbfileLoaderPlugin.d.ts +3 -3
  52. package/dist/src/monorepo/plugins/EmbfileLoaderPlugin.js +9 -5
  53. package/dist/src/monorepo/store/index.js +5 -3
  54. package/dist/src/monorepo/taskManagerFactory.d.ts +3 -0
  55. package/dist/src/monorepo/taskManagerFactory.js +20 -0
  56. package/dist/src/monorepo/types.d.ts +2 -1
  57. package/dist/src/monorepo/utils/findRunOrder.d.ts +34 -0
  58. package/dist/src/monorepo/utils/findRunOrder.js +165 -0
  59. package/dist/src/monorepo/utils/index.d.ts +1 -1
  60. package/dist/src/monorepo/utils/index.js +1 -1
  61. package/oclif.manifest.json +94 -64
  62. package/package.json +9 -2
  63. package/dist/src/monorepo/operations/tasks/RunTaskOperation.d.ts +0 -18
  64. package/dist/src/monorepo/operations/tasks/RunTaskOperation.js +0 -50
  65. package/dist/src/monorepo/utils/findBuildOrder.d.ts +0 -2
  66. package/dist/src/monorepo/utils/findBuildOrder.js +0 -41
@@ -0,0 +1,165 @@
1
+ import graphlib from 'graphlib';
2
+ import { AmbiguousTaskError, CircularDependencyError, TaskNameCollisionError, UnkownReferenceError, } from '../../errors.js';
3
+ export class EMBCollection {
4
+ items;
5
+ idField;
6
+ depField;
7
+ byId;
8
+ byName;
9
+ constructor(items, cfg) {
10
+ this.items = [];
11
+ this.idField = cfg.idField;
12
+ this.depField = cfg.depField;
13
+ this.byId = new Map();
14
+ this.byName = new Map();
15
+ // single-pass validation state
16
+ const seenIds = new Set();
17
+ const seenNames = new Set();
18
+ const dupIdReports = [];
19
+ const collisions = [];
20
+ for (const t of items) {
21
+ const id = t[this.idField];
22
+ const { name } = t;
23
+ // duplicate id?
24
+ if (seenIds.has(id)) {
25
+ const firstOwner = this.byId.get(id); // first occurrence already stored
26
+ dupIdReports.push(`id "${id}" used by "${firstOwner.name}" and "${name}"`);
27
+ // keep the first owner in byId; do not overwrite
28
+ }
29
+ else {
30
+ this.byId.set(id, t);
31
+ seenIds.add(id);
32
+ }
33
+ // --- Optional validation: forbid id <-> name collisions ---
34
+ const checkCollisions = cfg.forbidIdNameCollision && this.idField !== 'name';
35
+ if (checkCollisions) {
36
+ if (seenNames.has(id)) {
37
+ const nameOwners = this.byName.get(id) ?? [];
38
+ const ownerIds = nameOwners.map((o) => o[this.idField]).join(', ');
39
+ collisions.push(`value "${id}" is an id of "${name}" and also a name of item(s) with id(s): [${ownerIds}]`);
40
+ }
41
+ if (seenIds.has(name)) {
42
+ const idOwner = this.byId.get(name);
43
+ collisions.push(`value "${name}" is a name of "${t.name}" and also an id of "${idOwner.name}"`);
44
+ }
45
+ }
46
+ // byName index
47
+ const list = this.byName.get(name);
48
+ if (list)
49
+ list.push(t);
50
+ else
51
+ this.byName.set(name, [t]);
52
+ // keep item list (stable order)
53
+ this.items.push(t);
54
+ // record name after checks so current name won’t collide with itself
55
+ seenNames.add(name);
56
+ }
57
+ if (dupIdReports.length > 0 || collisions.length > 0) {
58
+ const parts = [];
59
+ if (dupIdReports.length > 0) {
60
+ parts.push(`Duplicate ${String(this.idField)} values (${dupIdReports.length}):\n` +
61
+ dupIdReports.join('\n'));
62
+ }
63
+ if (collisions.length > 0) {
64
+ parts.push(`id↔name collisions (${collisions.length}):\n` +
65
+ collisions.join('\n'));
66
+ }
67
+ throw new TaskNameCollisionError('Collision between task names and ids', parts);
68
+ }
69
+ }
70
+ /** All items (stable array iteration) */
71
+ get all() {
72
+ return this.items;
73
+ }
74
+ idOf(t) {
75
+ return t[this.idField];
76
+ }
77
+ depsOf(t) {
78
+ return (t[this.depField] ?? []);
79
+ }
80
+ matches(ref, opts) {
81
+ const idHit = this.byId.get(ref);
82
+ if (idHit)
83
+ return opts?.multiple ? [idHit] : idHit;
84
+ const nameHits = this.byName.get(ref) ?? [];
85
+ if (nameHits.length === 0) {
86
+ throw new UnkownReferenceError(`Unknown reference "${ref}"`);
87
+ }
88
+ if (opts?.multiple)
89
+ return nameHits;
90
+ if (nameHits.length > 1) {
91
+ const ids = nameHits.map((t) => this.idOf(t));
92
+ throw new AmbiguousTaskError(`Ambiguous reference "${ref}" matches multiple id/name`, ids);
93
+ }
94
+ return nameHits[0];
95
+ }
96
+ }
97
+ /* ----------------- run-order helpers unchanged (for completeness) ---------------- */
98
+ function resolveRefSet(col, ref, policy) {
99
+ if (policy === 'runAll')
100
+ return col.matches(ref, { multiple: true }).map((t) => col.idOf(t));
101
+ return [col.idOf(col.matches(ref))];
102
+ }
103
+ function collectPredecessorClosure(g, seeds) {
104
+ const seen = new Set();
105
+ const q = [];
106
+ for (const s of seeds) {
107
+ if (!seen.has(s)) {
108
+ seen.add(s);
109
+ q.push(s);
110
+ }
111
+ }
112
+ while (q.length > 0) {
113
+ const cur = q.shift();
114
+ for (const p of g.predecessors(cur) ?? [])
115
+ if (!seen.has(p)) {
116
+ seen.add(p);
117
+ q.push(p);
118
+ }
119
+ }
120
+ return seen;
121
+ }
122
+ function buildGraph(col, policy) {
123
+ const g = new graphlib.Graph({ directed: true });
124
+ for (const t of col.all)
125
+ g.setNode(col.idOf(t));
126
+ for (const t of col.all) {
127
+ const toId = col.idOf(t);
128
+ for (const ref of col.depsOf(t)) {
129
+ for (const fromId of resolveRefSet(col, ref, policy))
130
+ g.setEdge(fromId, toId);
131
+ }
132
+ }
133
+ return g;
134
+ }
135
+ export function findRunOrder(selection, collection, { onAmbiguous = 'error' } = {}) {
136
+ const g = buildGraph(collection, onAmbiguous);
137
+ const cycles = graphlib.alg.findCycles(g);
138
+ if (cycles.length > 0) {
139
+ throw new CircularDependencyError(`Circular dependencies detected: ${JSON.stringify(cycles)}`);
140
+ }
141
+ const selectedIds = new Set();
142
+ for (const ref of selection)
143
+ for (const id of resolveRefSet(collection, ref, onAmbiguous))
144
+ selectedIds.add(id);
145
+ if (selectedIds.size === 0)
146
+ throw new Error('Selection resolved to no items.');
147
+ const include = collectPredecessorClosure(g, selectedIds.values());
148
+ const sub = new graphlib.Graph({ directed: true });
149
+ for (const id of include)
150
+ sub.setNode(id);
151
+ for (const id of include)
152
+ for (const p of g.predecessors(id) ?? [])
153
+ if (include.has(p))
154
+ sub.setEdge(p, id);
155
+ const ids = graphlib.alg.topsort(sub);
156
+ const byId = new Map();
157
+ for (const t of collection.all)
158
+ byId.set(collection.idOf(t), t);
159
+ return ids.map((id) => {
160
+ const t = byId.get(id);
161
+ if (!t)
162
+ throw new Error(`Internal error: missing item for id "${id}"`);
163
+ return t;
164
+ });
165
+ }
@@ -1 +1 @@
1
- export * from './findBuildOrder.js';
1
+ export * from './findRunOrder.js';
@@ -1 +1 @@
1
- export * from './findBuildOrder.js';
1
+ export * from './findRunOrder.js';
@@ -14,6 +14,13 @@
14
14
  "name": "json",
15
15
  "allowNo": false,
16
16
  "type": "boolean"
17
+ },
18
+ "force": {
19
+ "char": "f",
20
+ "description": "Force the deletion of containers & images",
21
+ "name": "force",
22
+ "allowNo": false,
23
+ "type": "boolean"
17
24
  }
18
25
  },
19
26
  "hasDynamicHelp": false,
@@ -47,6 +54,14 @@
47
54
  "name": "json",
48
55
  "allowNo": false,
49
56
  "type": "boolean"
57
+ },
58
+ "flavor": {
59
+ "description": "Specify the flavor to use.",
60
+ "name": "flavor",
61
+ "required": false,
62
+ "hasDynamicHelp": false,
63
+ "multiple": false,
64
+ "type": "option"
50
65
  }
51
66
  },
52
67
  "hasDynamicHelp": false,
@@ -89,10 +104,10 @@
89
104
  "multiple": false,
90
105
  "type": "option"
91
106
  },
92
- "force-recreate": {
107
+ "force": {
93
108
  "char": "f",
94
- "description": "Recreate containers even if their configuration and image haven't changed",
95
- "name": "force-recreate",
109
+ "description": "Bypass caches, force the recreation of containers, etc",
110
+ "name": "force",
96
111
  "allowNo": false,
97
112
  "type": "boolean"
98
113
  }
@@ -118,7 +133,7 @@
118
133
  "aliases": [],
119
134
  "args": {
120
135
  "component": {
121
- "description": "List of components to build",
136
+ "description": "List of components to build (defaults to all)",
122
137
  "name": "component",
123
138
  "required": false
124
139
  }
@@ -142,6 +157,13 @@
142
157
  "hasDynamicHelp": false,
143
158
  "multiple": false,
144
159
  "type": "option"
160
+ },
161
+ "dry-run": {
162
+ "description": "Do not build the components but only produce build meta information",
163
+ "name": "dry-run",
164
+ "required": false,
165
+ "allowNo": false,
166
+ "type": "boolean"
145
167
  }
146
168
  },
147
169
  "hasDynamicHelp": false,
@@ -324,10 +346,10 @@
324
346
  "prune.js"
325
347
  ]
326
348
  },
327
- "images:delete": {
349
+ "tasks": {
328
350
  "aliases": [],
329
351
  "args": {},
330
- "description": "Delete project images.",
352
+ "description": "List tasks.",
331
353
  "examples": [
332
354
  "<%= config.bin %> <%= command.id %>"
333
355
  ],
@@ -338,19 +360,11 @@
338
360
  "name": "json",
339
361
  "allowNo": false,
340
362
  "type": "boolean"
341
- },
342
- "force": {
343
- "char": "f",
344
- "description": "Remove the image even if it is being used by stopped containers or has other tags",
345
- "name": "force",
346
- "required": false,
347
- "allowNo": false,
348
- "type": "boolean"
349
363
  }
350
364
  },
351
365
  "hasDynamicHelp": false,
352
366
  "hiddenAliases": [],
353
- "id": "images:delete",
367
+ "id": "tasks",
354
368
  "pluginAlias": "@enspirit/emb",
355
369
  "pluginName": "@enspirit/emb",
356
370
  "pluginType": "core",
@@ -362,14 +376,20 @@
362
376
  "src",
363
377
  "cli",
364
378
  "commands",
365
- "images",
366
- "delete.js"
379
+ "tasks",
380
+ "index.js"
367
381
  ]
368
382
  },
369
- "images": {
383
+ "tasks:run": {
370
384
  "aliases": [],
371
- "args": {},
372
- "description": "List docker images.",
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.",
373
393
  "examples": [
374
394
  "<%= config.bin %> <%= command.id %>"
375
395
  ],
@@ -381,22 +401,33 @@
381
401
  "allowNo": false,
382
402
  "type": "boolean"
383
403
  },
384
- "all": {
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": {
385
417
  "char": "a",
386
- "description": "Show all images. Only images from a final layer (no children) are shown by default.",
387
- "name": "all",
388
- "required": false,
418
+ "description": "Run all tasks matching (when multiple matches)",
419
+ "name": "all-matching",
389
420
  "allowNo": false,
390
421
  "type": "boolean"
391
422
  }
392
423
  },
393
424
  "hasDynamicHelp": false,
394
425
  "hiddenAliases": [],
395
- "id": "images",
426
+ "id": "tasks:run",
396
427
  "pluginAlias": "@enspirit/emb",
397
428
  "pluginName": "@enspirit/emb",
398
429
  "pluginType": "core",
399
- "strict": true,
430
+ "strict": false,
400
431
  "enableJsonFlag": true,
401
432
  "isESM": true,
402
433
  "relativePath": [
@@ -404,14 +435,14 @@
404
435
  "src",
405
436
  "cli",
406
437
  "commands",
407
- "images",
408
- "index.js"
438
+ "tasks",
439
+ "run.js"
409
440
  ]
410
441
  },
411
- "images:prune": {
442
+ "images:delete": {
412
443
  "aliases": [],
413
444
  "args": {},
414
- "description": "Prune project images.",
445
+ "description": "Delete project images.",
415
446
  "examples": [
416
447
  "<%= config.bin %> <%= command.id %>"
417
448
  ],
@@ -423,10 +454,10 @@
423
454
  "allowNo": false,
424
455
  "type": "boolean"
425
456
  },
426
- "all": {
427
- "char": "a",
428
- "description": "Prune all images. When set to true all images will be pruned, not only dangling ones",
429
- "name": "all",
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",
430
461
  "required": false,
431
462
  "allowNo": false,
432
463
  "type": "boolean"
@@ -434,7 +465,7 @@
434
465
  },
435
466
  "hasDynamicHelp": false,
436
467
  "hiddenAliases": [],
437
- "id": "images:prune",
468
+ "id": "images:delete",
438
469
  "pluginAlias": "@enspirit/emb",
439
470
  "pluginName": "@enspirit/emb",
440
471
  "pluginType": "core",
@@ -447,13 +478,13 @@
447
478
  "cli",
448
479
  "commands",
449
480
  "images",
450
- "prune.js"
481
+ "delete.js"
451
482
  ]
452
483
  },
453
- "tasks": {
484
+ "images": {
454
485
  "aliases": [],
455
486
  "args": {},
456
- "description": "List tasks.",
487
+ "description": "List docker images.",
457
488
  "examples": [
458
489
  "<%= config.bin %> <%= command.id %>"
459
490
  ],
@@ -464,11 +495,19 @@
464
495
  "name": "json",
465
496
  "allowNo": false,
466
497
  "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"
467
506
  }
468
507
  },
469
508
  "hasDynamicHelp": false,
470
509
  "hiddenAliases": [],
471
- "id": "tasks",
510
+ "id": "images",
472
511
  "pluginAlias": "@enspirit/emb",
473
512
  "pluginName": "@enspirit/emb",
474
513
  "pluginType": "core",
@@ -480,20 +519,14 @@
480
519
  "src",
481
520
  "cli",
482
521
  "commands",
483
- "tasks",
522
+ "images",
484
523
  "index.js"
485
524
  ]
486
525
  },
487
- "tasks:run": {
526
+ "images:prune": {
488
527
  "aliases": [],
489
- "args": {
490
- "task": {
491
- "description": "List of tasks ids to run (eg: component:task)",
492
- "name": "task",
493
- "required": false
494
- }
495
- },
496
- "description": "Run a task.",
528
+ "args": {},
529
+ "description": "Prune project images.",
497
530
  "examples": [
498
531
  "<%= config.bin %> <%= command.id %>"
499
532
  ],
@@ -505,25 +538,22 @@
505
538
  "allowNo": false,
506
539
  "type": "boolean"
507
540
  },
508
- "executor": {
509
- "char": "x",
510
- "name": "executor",
511
- "hasDynamicHelp": false,
512
- "multiple": false,
513
- "options": [
514
- "container",
515
- "local"
516
- ],
517
- "type": "option"
541
+ "all": {
542
+ "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,
546
+ "allowNo": false,
547
+ "type": "boolean"
518
548
  }
519
549
  },
520
550
  "hasDynamicHelp": false,
521
551
  "hiddenAliases": [],
522
- "id": "tasks:run",
552
+ "id": "images:prune",
523
553
  "pluginAlias": "@enspirit/emb",
524
554
  "pluginName": "@enspirit/emb",
525
555
  "pluginType": "core",
526
- "strict": false,
556
+ "strict": true,
527
557
  "enableJsonFlag": true,
528
558
  "isESM": true,
529
559
  "relativePath": [
@@ -531,10 +561,10 @@
531
561
  "src",
532
562
  "cli",
533
563
  "commands",
534
- "tasks",
535
- "run.js"
564
+ "images",
565
+ "prune.js"
536
566
  ]
537
567
  }
538
568
  },
539
- "version": "0.0.6"
569
+ "version": "0.0.8"
540
570
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@enspirit/emb",
3
3
  "type": "module",
4
- "version": "0.0.6",
4
+ "version": "0.0.8",
5
5
  "keywords": ["monorepo", "docker", "taskrunner", "ci", "docker compose", "sentinel", "makefile"],
6
6
  "author": "Louis Lambeau <louis.lambeau@enspirit.be>",
7
7
  "license": "ISC",
@@ -35,6 +35,7 @@
35
35
  "@oclif/plugin-autocomplete": "^3.2.34",
36
36
  "@oclif/plugin-help": "^6.2.32",
37
37
  "@oclif/plugin-not-found": "^3.2.63",
38
+ "@oclif/plugin-update": "^4.7.3",
38
39
  "@oclif/table": "^0.4.12",
39
40
  "ajv": "^8.17.1",
40
41
  "colorette": "^2.0.20",
@@ -103,10 +104,16 @@
103
104
  "plugins": [
104
105
  "@oclif/plugin-help",
105
106
  "@oclif/plugin-autocomplete",
106
- "@oclif/plugin-not-found"
107
+ "@oclif/plugin-not-found",
108
+ "@oclif/plugin-update"
107
109
  ],
108
110
  "topicSeparator": " ",
109
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" }
110
117
  }
111
118
  }
112
119
  }
@@ -1,18 +0,0 @@
1
- import { Writable } from 'node:stream';
2
- import { TaskInfo } from '../../types.js';
3
- import { IOperation } from '../../../operations/types.js';
4
- export declare enum ExecutorType {
5
- container = "container",
6
- local = "local"
7
- }
8
- export type RunTaskOperationParams = {
9
- executor: ExecutorType;
10
- task: TaskInfo;
11
- };
12
- export declare class RunTaskOperation implements IOperation<RunTaskOperationParams, void> {
13
- protected out?: Writable | undefined;
14
- constructor(out?: Writable | undefined);
15
- run(params: RunTaskOperationParams): Promise<void>;
16
- private runDocker;
17
- private runLocal;
18
- }
@@ -1,50 +0,0 @@
1
- import { getContext } from '../../../index.js';
2
- import { ContainerExecOperation } from '../../../docker/index.js';
3
- import { ExecuteLocalCommandOperation, GetComponentContainerOperation, } from '../index.js';
4
- export var ExecutorType;
5
- (function (ExecutorType) {
6
- ExecutorType["container"] = "container";
7
- ExecutorType["local"] = "local";
8
- })(ExecutorType || (ExecutorType = {}));
9
- export class RunTaskOperation {
10
- out;
11
- constructor(out) {
12
- this.out = out;
13
- }
14
- async run(params) {
15
- switch (params.executor) {
16
- case ExecutorType.container: {
17
- return this.runDocker(params);
18
- }
19
- case ExecutorType.local: {
20
- return this.runLocal(params);
21
- }
22
- default: {
23
- throw new Error(`Unsupported executor type: ${params.executor}`);
24
- }
25
- }
26
- }
27
- async runDocker(params) {
28
- const { monorepo } = getContext();
29
- if (!params.task.component) {
30
- throw new Error(`Support for non-component tasks not implemented`);
31
- }
32
- const containerInfo = await monorepo.run(new GetComponentContainerOperation(), params.task.component);
33
- await monorepo.run(new ContainerExecOperation(this.out), {
34
- attachStderr: true,
35
- attachStdout: true,
36
- container: containerInfo.Id,
37
- script: params.task.script,
38
- });
39
- }
40
- async runLocal(params) {
41
- const { monorepo } = getContext();
42
- const cwd = params.task.component
43
- ? monorepo.component(params.task.component).rootdir
44
- : monorepo.rootDir;
45
- await monorepo.run(new ExecuteLocalCommandOperation(this.out), {
46
- script: params.task.script,
47
- workingDir: cwd,
48
- });
49
- }
50
- }
@@ -1,2 +0,0 @@
1
- import { Component } from '../index.js';
2
- export declare const findBuildOrder: (components: Array<Component>, selection?: Array<string>) => Array<Component>;
@@ -1,41 +0,0 @@
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
- };