@enspirit/emb 0.1.3 → 0.2.0

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.1.3 darwin-x64 node-v22.12.0
17
+ @enspirit/emb/0.2.0 darwin-x64 node-v22.12.0
18
18
  $ emb --help [COMMAND]
19
19
  USAGE
20
20
  $ emb COMMAND
@@ -27,6 +27,7 @@ USAGE
27
27
  * [`emb clean`](#emb-clean)
28
28
  * [`emb components`](#emb-components)
29
29
  * [`emb components logs COMPONENT`](#emb-components-logs-component)
30
+ * [`emb components shell COMPONENT`](#emb-components-shell-component)
30
31
  * [`emb config print`](#emb-config-print)
31
32
  * [`emb containers`](#emb-containers)
32
33
  * [`emb containers prune`](#emb-containers-prune)
@@ -39,6 +40,7 @@ USAGE
39
40
  * [`emb ps`](#emb-ps)
40
41
  * [`emb resources`](#emb-resources)
41
42
  * [`emb resources build [COMPONENT]`](#emb-resources-build-component)
43
+ * [`emb shell COMPONENT`](#emb-shell-component)
42
44
  * [`emb tasks`](#emb-tasks)
43
45
  * [`emb tasks run TASK`](#emb-tasks-run-task)
44
46
  * [`emb up`](#emb-up)
@@ -142,6 +144,31 @@ EXAMPLES
142
144
  $ emb components logs
143
145
  ```
144
146
 
147
+ ## `emb components shell COMPONENT`
148
+
149
+ Get a shell on a running component.
150
+
151
+ ```
152
+ USAGE
153
+ $ emb components shell COMPONENT [--flavor <value>] [-s <value>]
154
+
155
+ ARGUMENTS
156
+ COMPONENT The component you want to get a shell on
157
+
158
+ FLAGS
159
+ -s, --shell=<value> [default: bash] The shell to run
160
+ --flavor=<value> Specify the flavor to use.
161
+
162
+ DESCRIPTION
163
+ Get a shell on a running component.
164
+
165
+ ALIASES
166
+ $ emb shell
167
+
168
+ EXAMPLES
169
+ $ emb components shell
170
+ ```
171
+
145
172
  ## `emb config print`
146
173
 
147
174
  Print the current config.
@@ -406,6 +433,31 @@ EXAMPLES
406
433
  $ emb resources build build --flavor development
407
434
  ```
408
435
 
436
+ ## `emb shell COMPONENT`
437
+
438
+ Get a shell on a running component.
439
+
440
+ ```
441
+ USAGE
442
+ $ emb shell COMPONENT [--flavor <value>] [-s <value>]
443
+
444
+ ARGUMENTS
445
+ COMPONENT The component you want to get a shell on
446
+
447
+ FLAGS
448
+ -s, --shell=<value> [default: bash] The shell to run
449
+ --flavor=<value> Specify the flavor to use.
450
+
451
+ DESCRIPTION
452
+ Get a shell on a running component.
453
+
454
+ ALIASES
455
+ $ emb shell
456
+
457
+ EXAMPLES
458
+ $ emb shell
459
+ ```
460
+
409
461
  ## `emb tasks`
410
462
 
411
463
  List tasks.
@@ -0,0 +1,14 @@
1
+ import { FlavoredCommand } from '../../index.js';
2
+ export default class ComponentsLogs extends FlavoredCommand<typeof ComponentsLogs> {
3
+ static aliases: string[];
4
+ static description: string;
5
+ static enableJsonFlag: boolean;
6
+ static examples: string[];
7
+ static flags: {
8
+ shell: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static args: {
11
+ component: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,41 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { FlavoredCommand, getContext } from '../../index.js';
3
+ import { ExecShellOperation } from '../../../docker/index.js';
4
+ import { ShellExitError } from '../../../errors.js';
5
+ export default class ComponentsLogs extends FlavoredCommand {
6
+ static aliases = ['shell'];
7
+ static description = 'Get a shell on a running component.';
8
+ static enableJsonFlag = false;
9
+ static examples = ['<%= config.bin %> <%= command.id %>'];
10
+ static flags = {
11
+ shell: Flags.string({
12
+ name: 'shell',
13
+ char: 's',
14
+ description: 'The shell to run',
15
+ default: 'bash',
16
+ }),
17
+ };
18
+ static args = {
19
+ component: Args.string({
20
+ name: 'component',
21
+ description: 'The component you want to get a shell on',
22
+ required: true,
23
+ }),
24
+ };
25
+ async run() {
26
+ const { flags, args } = await this.parse(ComponentsLogs);
27
+ const { monorepo } = await getContext();
28
+ try {
29
+ await monorepo.run(new ExecShellOperation(), {
30
+ component: args.component,
31
+ shell: flags.shell,
32
+ });
33
+ }
34
+ catch (error) {
35
+ if (error instanceof ShellExitError) {
36
+ this.error(error);
37
+ }
38
+ throw error;
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,11 @@
1
+ import * as z from 'zod';
2
+ import { AbstractOperation } from '../../../operations/index.js';
3
+ declare const schema: z.ZodObject<{
4
+ shell: z.ZodOptional<z.ZodDefault<z.ZodString>>;
5
+ component: z.ZodString;
6
+ }, z.core.$strip>;
7
+ export declare class ExecShellOperation extends AbstractOperation<typeof schema, void> {
8
+ constructor();
9
+ protected _run(input: z.input<typeof schema>): Promise<void>;
10
+ }
11
+ export {};
@@ -0,0 +1,70 @@
1
+ import { getContext, ListContainersOperation, MultipleContainersFoundError, NoContainerFoundError, ShellExitError, } from '../../../index.js';
2
+ import { spawn } from 'node:child_process';
3
+ import * as z from 'zod';
4
+ import { AbstractOperation } from '../../../operations/index.js';
5
+ const schema = z.object({
6
+ shell: z.string().default('bash').optional(),
7
+ component: z
8
+ .string()
9
+ .describe('The name of the component on which to run a shell'),
10
+ });
11
+ export class ExecShellOperation extends AbstractOperation {
12
+ constructor() {
13
+ super(schema);
14
+ }
15
+ async _run(input) {
16
+ const { monorepo } = getContext();
17
+ const containers = await monorepo.run(new ListContainersOperation(), {
18
+ filters: {
19
+ label: [
20
+ `emb/project=${monorepo.name}`,
21
+ `emb/component=${input.component}`,
22
+ `emb/flavor=${monorepo.currentFlavor}`,
23
+ ],
24
+ },
25
+ });
26
+ if (containers.length === 0) {
27
+ throw new NoContainerFoundError(`No container found for component \`${input.component}\``, input.component);
28
+ }
29
+ if (containers.length > 1) {
30
+ throw new MultipleContainersFoundError(`More than one container found for component \`${input.component}\``, input.component);
31
+ }
32
+ const cmd = 'docker';
33
+ const args = ['exec', '-it', containers[0].Id, input?.shell || 'bash'];
34
+ const child = spawn(cmd, args, {
35
+ stdio: 'inherit',
36
+ env: {
37
+ ...process.env,
38
+ DOCKER_CLI_HINTS: 'false',
39
+ },
40
+ });
41
+ const forward = (sig) => {
42
+ try {
43
+ child.kill(sig);
44
+ }
45
+ catch { }
46
+ };
47
+ const signals = [
48
+ 'SIGINT',
49
+ 'SIGTERM',
50
+ 'SIGHUP',
51
+ 'SIGQUIT',
52
+ ];
53
+ signals.forEach((sig) => {
54
+ process.on(sig, () => forward(sig));
55
+ });
56
+ return new Promise((resolve, reject) => {
57
+ child.on('error', (err) => {
58
+ reject(new Error(`Failed to exeucte docker: ${err.message}`));
59
+ });
60
+ child.on('exit', (code, signal) => {
61
+ if (code !== null && code !== 0) {
62
+ reject(new ShellExitError(`The shell exited unexpectedly. ${code}`, input.component, code, signal));
63
+ }
64
+ else {
65
+ resolve();
66
+ }
67
+ });
68
+ });
69
+ }
70
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './ExecContainerOperation.js';
2
+ export * from './ExecShellOperation.js';
2
3
  export * from './ListContainersOperation.js';
3
4
  export * from './PruneContainersOperation.js';
@@ -1,3 +1,4 @@
1
1
  export * from './ExecContainerOperation.js';
2
+ export * from './ExecShellOperation.js';
2
3
  export * from './ListContainersOperation.js';
3
4
  export * from './PruneContainersOperation.js';
@@ -58,3 +58,17 @@ export declare class CircularDependencyError extends EMBError {
58
58
  readonly deps: Array<Array<string>>;
59
59
  constructor(msg: string, deps: Array<Array<string>>);
60
60
  }
61
+ export declare class ShellExitError extends EMBError {
62
+ component: string;
63
+ exitCode: number;
64
+ signal?: (NodeJS.Signals | null) | undefined;
65
+ constructor(msg: string, component: string, exitCode: number, signal?: (NodeJS.Signals | null) | undefined);
66
+ }
67
+ export declare class NoContainerFoundError extends EMBError {
68
+ component: string;
69
+ constructor(msg: string, component: string);
70
+ }
71
+ export declare class MultipleContainersFoundError extends EMBError {
72
+ component: string;
73
+ constructor(msg: string, component: string);
74
+ }
@@ -44,7 +44,7 @@ export class AmbiguousReferenceError extends EMBError {
44
44
  ref;
45
45
  matches;
46
46
  constructor(msg, ref, matches) {
47
- super('AMBIGUOUS_TASK', msg);
47
+ super('AMBIGUOUS_REF', msg);
48
48
  this.ref = ref;
49
49
  this.matches = matches;
50
50
  }
@@ -70,3 +70,28 @@ export class CircularDependencyError extends EMBError {
70
70
  this.deps = deps;
71
71
  }
72
72
  }
73
+ export class ShellExitError extends EMBError {
74
+ component;
75
+ exitCode;
76
+ signal;
77
+ constructor(msg, component, exitCode, signal) {
78
+ super('SHELL_EXIT_ERR', msg);
79
+ this.component = component;
80
+ this.exitCode = exitCode;
81
+ this.signal = signal;
82
+ }
83
+ }
84
+ export class NoContainerFoundError extends EMBError {
85
+ component;
86
+ constructor(msg, component) {
87
+ super('CMP_NO_CONTAINER', msg);
88
+ this.component = component;
89
+ }
90
+ }
91
+ export class MultipleContainersFoundError extends EMBError {
92
+ component;
93
+ constructor(msg, component) {
94
+ super('CMP_NO_CONTAINER', msg);
95
+ this.component = component;
96
+ }
97
+ }
@@ -217,6 +217,57 @@
217
217
  "logs.js"
218
218
  ]
219
219
  },
220
+ "components:shell": {
221
+ "aliases": [
222
+ "shell"
223
+ ],
224
+ "args": {
225
+ "component": {
226
+ "description": "The component you want to get a shell on",
227
+ "name": "component",
228
+ "required": true
229
+ }
230
+ },
231
+ "description": "Get a shell on a running component.",
232
+ "examples": [
233
+ "<%= config.bin %> <%= command.id %>"
234
+ ],
235
+ "flags": {
236
+ "flavor": {
237
+ "description": "Specify the flavor to use.",
238
+ "name": "flavor",
239
+ "required": false,
240
+ "hasDynamicHelp": false,
241
+ "multiple": false,
242
+ "type": "option"
243
+ },
244
+ "shell": {
245
+ "char": "s",
246
+ "description": "The shell to run",
247
+ "name": "shell",
248
+ "default": "bash",
249
+ "hasDynamicHelp": false,
250
+ "multiple": false,
251
+ "type": "option"
252
+ }
253
+ },
254
+ "hasDynamicHelp": false,
255
+ "hiddenAliases": [],
256
+ "id": "components:shell",
257
+ "pluginAlias": "@enspirit/emb",
258
+ "pluginName": "@enspirit/emb",
259
+ "pluginType": "core",
260
+ "enableJsonFlag": false,
261
+ "isESM": true,
262
+ "relativePath": [
263
+ "dist",
264
+ "src",
265
+ "cli",
266
+ "commands",
267
+ "components",
268
+ "shell.js"
269
+ ]
270
+ },
220
271
  "config:print": {
221
272
  "aliases": [],
222
273
  "args": {},
@@ -469,18 +520,12 @@
469
520
  "prune.js"
470
521
  ]
471
522
  },
472
- "resources:build": {
523
+ "tasks": {
473
524
  "aliases": [],
474
- "args": {
475
- "component": {
476
- "description": "List of resources to build (defaults to all)",
477
- "name": "component",
478
- "required": false
479
- }
480
- },
481
- "description": "Build the resources of the monorepo",
525
+ "args": {},
526
+ "description": "List tasks.",
482
527
  "examples": [
483
- "<%= config.bin %> <%= command.id %> build --flavor development"
528
+ "<%= config.bin %> <%= command.id %>"
484
529
  ],
485
530
  "flags": {
486
531
  "json": {
@@ -489,38 +534,15 @@
489
534
  "name": "json",
490
535
  "allowNo": false,
491
536
  "type": "boolean"
492
- },
493
- "flavor": {
494
- "description": "Specify the flavor to use.",
495
- "name": "flavor",
496
- "required": false,
497
- "hasDynamicHelp": false,
498
- "multiple": false,
499
- "type": "option"
500
- },
501
- "dry-run": {
502
- "description": "Do not build the resources but only produce build meta information",
503
- "name": "dry-run",
504
- "required": false,
505
- "allowNo": false,
506
- "type": "boolean"
507
- },
508
- "force": {
509
- "char": "f",
510
- "description": "Bypass the cache and force the build",
511
- "name": "force",
512
- "required": false,
513
- "allowNo": false,
514
- "type": "boolean"
515
537
  }
516
538
  },
517
539
  "hasDynamicHelp": false,
518
540
  "hiddenAliases": [],
519
- "id": "resources:build",
541
+ "id": "tasks",
520
542
  "pluginAlias": "@enspirit/emb",
521
543
  "pluginName": "@enspirit/emb",
522
544
  "pluginType": "core",
523
- "strict": false,
545
+ "strict": true,
524
546
  "enableJsonFlag": true,
525
547
  "isESM": true,
526
548
  "relativePath": [
@@ -528,14 +550,20 @@
528
550
  "src",
529
551
  "cli",
530
552
  "commands",
531
- "resources",
532
- "build.js"
553
+ "tasks",
554
+ "index.js"
533
555
  ]
534
556
  },
535
- "resources": {
557
+ "tasks:run": {
536
558
  "aliases": [],
537
- "args": {},
538
- "description": "List resources.",
559
+ "args": {
560
+ "task": {
561
+ "description": "List of tasks to run. You can provide either ids or names (eg: component:task or task)",
562
+ "name": "task",
563
+ "required": true
564
+ }
565
+ },
566
+ "description": "Run tasks.",
539
567
  "examples": [
540
568
  "<%= config.bin %> <%= command.id %>"
541
569
  ],
@@ -547,21 +575,33 @@
547
575
  "allowNo": false,
548
576
  "type": "boolean"
549
577
  },
550
- "flavor": {
551
- "description": "Specify the flavor to use.",
552
- "name": "flavor",
553
- "required": false,
578
+ "executor": {
579
+ "char": "x",
580
+ "description": "Where to run the task. (experimental!)",
581
+ "name": "executor",
554
582
  "hasDynamicHelp": false,
555
583
  "multiple": false,
584
+ "options": [
585
+ "container",
586
+ "local"
587
+ ],
556
588
  "type": "option"
589
+ },
590
+ "all-matching": {
591
+ "char": "a",
592
+ "description": "Run all tasks matching (when multiple matches)",
593
+ "name": "all-matching",
594
+ "allowNo": false,
595
+ "type": "boolean"
557
596
  }
558
597
  },
559
598
  "hasDynamicHelp": false,
560
599
  "hiddenAliases": [],
561
- "id": "resources",
600
+ "id": "tasks:run",
562
601
  "pluginAlias": "@enspirit/emb",
563
602
  "pluginName": "@enspirit/emb",
564
603
  "pluginType": "core",
604
+ "strict": false,
565
605
  "enableJsonFlag": true,
566
606
  "isESM": true,
567
607
  "relativePath": [
@@ -569,16 +609,22 @@
569
609
  "src",
570
610
  "cli",
571
611
  "commands",
572
- "resources",
573
- "index.js"
612
+ "tasks",
613
+ "run.js"
574
614
  ]
575
615
  },
576
- "tasks": {
616
+ "resources:build": {
577
617
  "aliases": [],
578
- "args": {},
579
- "description": "List tasks.",
618
+ "args": {
619
+ "component": {
620
+ "description": "List of resources to build (defaults to all)",
621
+ "name": "component",
622
+ "required": false
623
+ }
624
+ },
625
+ "description": "Build the resources of the monorepo",
580
626
  "examples": [
581
- "<%= config.bin %> <%= command.id %>"
627
+ "<%= config.bin %> <%= command.id %> build --flavor development"
582
628
  ],
583
629
  "flags": {
584
630
  "json": {
@@ -587,15 +633,38 @@
587
633
  "name": "json",
588
634
  "allowNo": false,
589
635
  "type": "boolean"
636
+ },
637
+ "flavor": {
638
+ "description": "Specify the flavor to use.",
639
+ "name": "flavor",
640
+ "required": false,
641
+ "hasDynamicHelp": false,
642
+ "multiple": false,
643
+ "type": "option"
644
+ },
645
+ "dry-run": {
646
+ "description": "Do not build the resources but only produce build meta information",
647
+ "name": "dry-run",
648
+ "required": false,
649
+ "allowNo": false,
650
+ "type": "boolean"
651
+ },
652
+ "force": {
653
+ "char": "f",
654
+ "description": "Bypass the cache and force the build",
655
+ "name": "force",
656
+ "required": false,
657
+ "allowNo": false,
658
+ "type": "boolean"
590
659
  }
591
660
  },
592
661
  "hasDynamicHelp": false,
593
662
  "hiddenAliases": [],
594
- "id": "tasks",
663
+ "id": "resources:build",
595
664
  "pluginAlias": "@enspirit/emb",
596
665
  "pluginName": "@enspirit/emb",
597
666
  "pluginType": "core",
598
- "strict": true,
667
+ "strict": false,
599
668
  "enableJsonFlag": true,
600
669
  "isESM": true,
601
670
  "relativePath": [
@@ -603,20 +672,14 @@
603
672
  "src",
604
673
  "cli",
605
674
  "commands",
606
- "tasks",
607
- "index.js"
675
+ "resources",
676
+ "build.js"
608
677
  ]
609
678
  },
610
- "tasks:run": {
679
+ "resources": {
611
680
  "aliases": [],
612
- "args": {
613
- "task": {
614
- "description": "List of tasks to run. You can provide either ids or names (eg: component:task or task)",
615
- "name": "task",
616
- "required": true
617
- }
618
- },
619
- "description": "Run tasks.",
681
+ "args": {},
682
+ "description": "List resources.",
620
683
  "examples": [
621
684
  "<%= config.bin %> <%= command.id %>"
622
685
  ],
@@ -628,33 +691,21 @@
628
691
  "allowNo": false,
629
692
  "type": "boolean"
630
693
  },
631
- "executor": {
632
- "char": "x",
633
- "description": "Where to run the task. (experimental!)",
634
- "name": "executor",
694
+ "flavor": {
695
+ "description": "Specify the flavor to use.",
696
+ "name": "flavor",
697
+ "required": false,
635
698
  "hasDynamicHelp": false,
636
699
  "multiple": false,
637
- "options": [
638
- "container",
639
- "local"
640
- ],
641
700
  "type": "option"
642
- },
643
- "all-matching": {
644
- "char": "a",
645
- "description": "Run all tasks matching (when multiple matches)",
646
- "name": "all-matching",
647
- "allowNo": false,
648
- "type": "boolean"
649
701
  }
650
702
  },
651
703
  "hasDynamicHelp": false,
652
704
  "hiddenAliases": [],
653
- "id": "tasks:run",
705
+ "id": "resources",
654
706
  "pluginAlias": "@enspirit/emb",
655
707
  "pluginName": "@enspirit/emb",
656
708
  "pluginType": "core",
657
- "strict": false,
658
709
  "enableJsonFlag": true,
659
710
  "isESM": true,
660
711
  "relativePath": [
@@ -662,10 +713,10 @@
662
713
  "src",
663
714
  "cli",
664
715
  "commands",
665
- "tasks",
666
- "run.js"
716
+ "resources",
717
+ "index.js"
667
718
  ]
668
719
  }
669
720
  },
670
- "version": "0.1.3"
721
+ "version": "0.2.0"
671
722
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@enspirit/emb",
3
3
  "type": "module",
4
- "version": "0.1.3",
4
+ "version": "0.2.0",
5
5
  "keywords": ["monorepo", "docker", "taskrunner", "ci", "docker compose", "sentinel", "makefile"],
6
6
  "author": "Louis Lambeau <louis.lambeau@enspirit.be>",
7
7
  "license": "ISC",