cdd-cli 3.2.0 → 4.0.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/.atl/skill-registry.md +30 -0
- package/.editorconfig +12 -0
- package/.eslintrc.json +1 -1
- package/.prettierignore +4 -0
- package/.prettierrc +8 -0
- package/.~lock.ROADMAP.md# +1 -0
- package/CHANGELOG.md +80 -0
- package/README.es.md +198 -0
- package/README.md +81 -23
- package/ROADMAP.md +647 -0
- package/SECURITY.md +5 -0
- package/__mocks__/ink.cjs +17 -0
- package/coverage/clover.xml +471 -500
- package/coverage/coverage-final.json +16 -16
- package/coverage/lcov-report/ContainerCreationPrompt.jsx.html +400 -0
- package/coverage/lcov-report/ControlsHUD.jsx.html +256 -0
- package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +394 -0
- package/coverage/lcov-report/components/PromptField.jsx.html +229 -0
- package/coverage/lcov-report/components/SuggestionPanel.jsx.html +268 -0
- package/coverage/lcov-report/components/index.html +146 -0
- package/coverage/lcov-report/helpers/constants.js.html +139 -22
- package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +75 -42
- package/coverage/lcov-report/helpers/dockerHubService.js.html +310 -0
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +162 -84
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +50 -32
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +21 -21
- package/coverage/lcov-report/helpers/imageNameUtils.js.html +181 -0
- package/coverage/lcov-report/helpers/index.html +84 -54
- package/coverage/lcov-report/helpers/logger.js.html +68 -68
- package/coverage/lcov-report/helpers/safeCall.js.html +15 -15
- package/coverage/lcov-report/helpers/validationHelpers.js.html +66 -114
- package/coverage/lcov-report/hooks/creation/index.html +20 -20
- package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +38 -17
- package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +593 -89
- package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +7 -7
- package/coverage/lcov-report/hooks/debug/index.html +5 -5
- package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +35 -20
- package/coverage/lcov-report/hooks/index.html +40 -40
- package/coverage/lcov-report/hooks/navigation/index.html +19 -19
- package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +38 -29
- package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +119 -80
- package/coverage/lcov-report/hooks/useControls.js.html +306 -123
- package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +8 -8
- package/coverage/lcov-report/hooks/useExitHandler.js.html +50 -41
- package/coverage/lcov-report/index.html +52 -67
- package/coverage/lcov.info +899 -952
- package/dist/App.js +18 -12
- package/dist/components/ContainerCreationPrompt.js +50 -13
- package/dist/components/ContainerList.js +3 -3
- package/dist/components/ContainerRow.js +20 -20
- package/dist/components/ContainerSection.js +3 -3
- package/dist/components/ControlsHUD.js +106 -0
- package/dist/components/Footer.js +2 -2
- package/dist/components/Header.js +7 -7
- package/dist/components/LogViewer.js +3 -3
- package/dist/components/MessageFeedback.js +2 -2
- package/dist/components/PromptField.js +4 -4
- package/dist/components/StatsBar.js +5 -5
- package/dist/components/SuggestionPanel.js +53 -0
- package/dist/components/UsageMenu.js +3 -3
- package/dist/helpers/appInfo.js +7 -7
- package/dist/helpers/constants.js +154 -1
- package/dist/helpers/containerOptionsBuilder.js +3 -3
- package/dist/helpers/dockerHubService.js +111 -0
- package/dist/helpers/dockerService/dockerService.js +1 -1
- package/dist/helpers/dockerService/serviceComponents/containerActions.js +99 -40
- package/dist/helpers/dockerService/serviceComponents/containerList.js +11 -11
- package/dist/helpers/dockerService/serviceComponents/containerLogs.js +9 -9
- package/dist/helpers/dockerService/serviceComponents/containerStats.js +5 -5
- package/dist/helpers/dockerService/serviceComponents/imageUtils.js +1 -1
- package/dist/helpers/exitWithMessage.js +10 -10
- package/dist/helpers/imageNameUtils.js +32 -0
- package/dist/helpers/logger.js +13 -13
- package/dist/helpers/safeCall.js +2 -2
- package/dist/helpers/validationHelpers.js +76 -21
- package/dist/hooks/creation/useContainerActions.js +28 -11
- package/dist/hooks/creation/useContainerCreation.js +452 -62
- package/dist/hooks/creation/useLogsViewer.js +2 -2
- package/dist/hooks/debug/useDebugLogs.js +4 -4
- package/dist/hooks/useContainerCommandRouter.js +14 -14
- package/dist/hooks/useContainers.js +3 -3
- package/dist/hooks/useControls.js +85 -38
- package/dist/hooks/useEraseConfirmation.js +3 -3
- package/dist/index.js +9 -2
- package/jest.config.cjs +7 -0
- package/package.json +7 -3
- package/src/App.jsx +39 -16
- package/src/components/ContainerCreationPrompt.jsx +54 -10
- package/src/components/ContainerList.jsx +3 -3
- package/src/components/ContainerRow.jsx +19 -16
- package/src/components/ContainerSection.jsx +3 -3
- package/src/components/ControlsHUD.jsx +66 -0
- package/src/components/Footer.jsx +2 -2
- package/src/components/Header.jsx +7 -7
- package/src/components/LogViewer.jsx +5 -3
- package/src/components/MessageFeedback.jsx +2 -2
- package/src/components/PromptField.jsx +4 -4
- package/src/components/StatsBar.jsx +11 -7
- package/src/components/SuggestionPanel.jsx +61 -0
- package/src/components/UsageMenu.jsx +18 -13
- package/src/helpers/appInfo.js +7 -7
- package/src/helpers/constants.js +48 -9
- package/src/helpers/containerOptionsBuilder.js +16 -5
- package/src/helpers/dockerHubService.js +75 -0
- package/src/helpers/dockerService/dockerService.js +1 -1
- package/src/helpers/dockerService/serviceComponents/containerActions.js +87 -61
- package/src/helpers/dockerService/serviceComponents/containerList.js +31 -25
- package/src/helpers/dockerService/serviceComponents/containerLogs.js +41 -26
- package/src/helpers/dockerService/serviceComponents/containerStats.js +13 -11
- package/src/helpers/dockerService/serviceComponents/imageUtils.js +5 -4
- package/src/helpers/exitWithMessage.js +13 -7
- package/src/helpers/imageNameUtils.js +32 -0
- package/src/helpers/logger.js +15 -16
- package/src/helpers/safeCall.js +2 -2
- package/src/helpers/validationHelpers.js +13 -29
- package/src/hooks/creation/useContainerActions.js +32 -12
- package/src/hooks/creation/useContainerCreation.js +293 -47
- package/src/hooks/creation/useLogsViewer.js +2 -2
- package/src/hooks/debug/useDebugLogs.js +15 -10
- package/src/hooks/navigation/useContainerSelection.js +17 -14
- package/src/hooks/useContainerCommandRouter.js +71 -58
- package/src/hooks/useContainers.js +4 -4
- package/src/hooks/useControls.js +135 -72
- package/src/hooks/useEraseConfirmation.js +3 -3
- package/src/hooks/useExitHandler.js +29 -26
- package/src/index.js +8 -4
- package/test/ContainerCreationPrompt.dom.test.js +313 -0
- package/test/ControlsHUD.dom.test.js +125 -0
- package/test/SuggestionPanel.dom.test.js +83 -0
- package/test/constants.test.js +41 -0
- package/test/dockerHubService.test.js +213 -0
- package/test/logger.test.js +58 -0
- package/test/resolveImageTag.test.js +26 -0
- package/test/useContainerCreation.dom.test.js +428 -2
- package/test/useContainerCreationHub.dom.test.js +184 -0
- package/test/useControls.dom.test.js +406 -4
package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html
CHANGED
|
@@ -23,30 +23,30 @@
|
|
|
23
23
|
<div class='clearfix'>
|
|
24
24
|
|
|
25
25
|
<div class='fl pad1y space-right2'>
|
|
26
|
-
<span class="strong">
|
|
26
|
+
<span class="strong">71.42% </span>
|
|
27
27
|
<span class="quiet">Statements</span>
|
|
28
|
-
<span class='fraction'>
|
|
28
|
+
<span class='fraction'>85/119</span>
|
|
29
29
|
</div>
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
<div class='fl pad1y space-right2'>
|
|
33
|
-
<span class="strong">
|
|
33
|
+
<span class="strong">61.29% </span>
|
|
34
34
|
<span class="quiet">Branches</span>
|
|
35
|
-
<span class='fraction'>
|
|
35
|
+
<span class='fraction'>38/62</span>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
<div class='fl pad1y space-right2'>
|
|
40
|
-
<span class="strong">
|
|
40
|
+
<span class="strong">80% </span>
|
|
41
41
|
<span class="quiet">Functions</span>
|
|
42
|
-
<span class='fraction'>
|
|
42
|
+
<span class='fraction'>12/15</span>
|
|
43
43
|
</div>
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
<div class='fl pad1y space-right2'>
|
|
47
|
-
<span class="strong">
|
|
47
|
+
<span class="strong">72.03% </span>
|
|
48
48
|
<span class="quiet">Lines</span>
|
|
49
|
-
<span class='fraction'>
|
|
49
|
+
<span class='fraction'>85/118</span>
|
|
50
50
|
</div>
|
|
51
51
|
|
|
52
52
|
|
|
@@ -319,7 +319,34 @@
|
|
|
319
319
|
<a name='L254'></a><a href='#L254'>254</a>
|
|
320
320
|
<a name='L255'></a><a href='#L255'>255</a>
|
|
321
321
|
<a name='L256'></a><a href='#L256'>256</a>
|
|
322
|
-
<a name='L257'></a><a href='#L257'>257</a
|
|
322
|
+
<a name='L257'></a><a href='#L257'>257</a>
|
|
323
|
+
<a name='L258'></a><a href='#L258'>258</a>
|
|
324
|
+
<a name='L259'></a><a href='#L259'>259</a>
|
|
325
|
+
<a name='L260'></a><a href='#L260'>260</a>
|
|
326
|
+
<a name='L261'></a><a href='#L261'>261</a>
|
|
327
|
+
<a name='L262'></a><a href='#L262'>262</a>
|
|
328
|
+
<a name='L263'></a><a href='#L263'>263</a>
|
|
329
|
+
<a name='L264'></a><a href='#L264'>264</a>
|
|
330
|
+
<a name='L265'></a><a href='#L265'>265</a>
|
|
331
|
+
<a name='L266'></a><a href='#L266'>266</a>
|
|
332
|
+
<a name='L267'></a><a href='#L267'>267</a>
|
|
333
|
+
<a name='L268'></a><a href='#L268'>268</a>
|
|
334
|
+
<a name='L269'></a><a href='#L269'>269</a>
|
|
335
|
+
<a name='L270'></a><a href='#L270'>270</a>
|
|
336
|
+
<a name='L271'></a><a href='#L271'>271</a>
|
|
337
|
+
<a name='L272'></a><a href='#L272'>272</a>
|
|
338
|
+
<a name='L273'></a><a href='#L273'>273</a>
|
|
339
|
+
<a name='L274'></a><a href='#L274'>274</a>
|
|
340
|
+
<a name='L275'></a><a href='#L275'>275</a>
|
|
341
|
+
<a name='L276'></a><a href='#L276'>276</a>
|
|
342
|
+
<a name='L277'></a><a href='#L277'>277</a>
|
|
343
|
+
<a name='L278'></a><a href='#L278'>278</a>
|
|
344
|
+
<a name='L279'></a><a href='#L279'>279</a>
|
|
345
|
+
<a name='L280'></a><a href='#L280'>280</a>
|
|
346
|
+
<a name='L281'></a><a href='#L281'>281</a>
|
|
347
|
+
<a name='L282'></a><a href='#L282'>282</a>
|
|
348
|
+
<a name='L283'></a><a href='#L283'>283</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
|
349
|
+
<span class="cline-any cline-neutral"> </span>
|
|
323
350
|
<span class="cline-any cline-neutral"> </span>
|
|
324
351
|
<span class="cline-any cline-neutral"> </span>
|
|
325
352
|
<span class="cline-any cline-neutral"> </span>
|
|
@@ -369,19 +396,6 @@
|
|
|
369
396
|
<span class="cline-any cline-neutral"> </span>
|
|
370
397
|
<span class="cline-any cline-neutral"> </span>
|
|
371
398
|
<span class="cline-any cline-neutral"> </span>
|
|
372
|
-
<span class="cline-any cline-yes">3x</span>
|
|
373
|
-
<span class="cline-any cline-yes">3x</span>
|
|
374
|
-
<span class="cline-any cline-yes">3x</span>
|
|
375
|
-
<span class="cline-any cline-yes">3x</span>
|
|
376
|
-
<span class="cline-any cline-yes">3x</span>
|
|
377
|
-
<span class="cline-any cline-yes">3x</span>
|
|
378
|
-
<span class="cline-any cline-yes">3x</span>
|
|
379
|
-
<span class="cline-any cline-neutral"> </span>
|
|
380
|
-
<span class="cline-any cline-neutral"> </span>
|
|
381
|
-
<span class="cline-any cline-neutral"> </span>
|
|
382
|
-
<span class="cline-any cline-neutral"> </span>
|
|
383
|
-
<span class="cline-any cline-neutral"> </span>
|
|
384
|
-
<span class="cline-any cline-neutral"> </span>
|
|
385
399
|
<span class="cline-any cline-neutral"> </span>
|
|
386
400
|
<span class="cline-any cline-neutral"> </span>
|
|
387
401
|
<span class="cline-any cline-neutral"> </span>
|
|
@@ -415,6 +429,8 @@
|
|
|
415
429
|
<span class="cline-any cline-yes">6x</span>
|
|
416
430
|
<span class="cline-any cline-neutral"> </span>
|
|
417
431
|
<span class="cline-any cline-neutral"> </span>
|
|
432
|
+
<span class="cline-any cline-neutral"> </span>
|
|
433
|
+
<span class="cline-any cline-neutral"> </span>
|
|
418
434
|
<span class="cline-any cline-yes">6x</span>
|
|
419
435
|
<span class="cline-any cline-neutral"> </span>
|
|
420
436
|
<span class="cline-any cline-yes">6x</span>
|
|
@@ -425,6 +441,9 @@
|
|
|
425
441
|
<span class="cline-any cline-yes">6x</span>
|
|
426
442
|
<span class="cline-any cline-yes">6x</span>
|
|
427
443
|
<span class="cline-any cline-yes">6x</span>
|
|
444
|
+
<span class="cline-any cline-neutral"> </span>
|
|
445
|
+
<span class="cline-any cline-neutral"> </span>
|
|
446
|
+
<span class="cline-any cline-neutral"> </span>
|
|
428
447
|
<span class="cline-any cline-yes">6x</span>
|
|
429
448
|
<span class="cline-any cline-yes">1x</span>
|
|
430
449
|
<span class="cline-any cline-yes">1x</span>
|
|
@@ -436,6 +455,9 @@
|
|
|
436
455
|
<span class="cline-any cline-no"> </span>
|
|
437
456
|
<span class="cline-any cline-neutral"> </span>
|
|
438
457
|
<span class="cline-any cline-neutral"> </span>
|
|
458
|
+
<span class="cline-any cline-neutral"> </span>
|
|
459
|
+
<span class="cline-any cline-neutral"> </span>
|
|
460
|
+
<span class="cline-any cline-neutral"> </span>
|
|
439
461
|
<span class="cline-any cline-yes">6x</span>
|
|
440
462
|
<span class="cline-any cline-yes">4x</span>
|
|
441
463
|
<span class="cline-any cline-yes">4x</span>
|
|
@@ -465,7 +487,13 @@
|
|
|
465
487
|
<span class="cline-any cline-neutral"> </span>
|
|
466
488
|
<span class="cline-any cline-neutral"> </span>
|
|
467
489
|
<span class="cline-any cline-yes">6x</span>
|
|
490
|
+
<span class="cline-any cline-neutral"> </span>
|
|
491
|
+
<span class="cline-any cline-neutral"> </span>
|
|
492
|
+
<span class="cline-any cline-neutral"> </span>
|
|
493
|
+
<span class="cline-any cline-neutral"> </span>
|
|
468
494
|
<span class="cline-any cline-yes">6x</span>
|
|
495
|
+
<span class="cline-any cline-neutral"> </span>
|
|
496
|
+
<span class="cline-any cline-neutral"> </span>
|
|
469
497
|
<span class="cline-any cline-yes">6x</span>
|
|
470
498
|
<span class="cline-any cline-neutral"> </span>
|
|
471
499
|
<span class="cline-any cline-neutral"> </span>
|
|
@@ -483,26 +511,42 @@
|
|
|
483
511
|
<span class="cline-any cline-yes">4x</span>
|
|
484
512
|
<span class="cline-any cline-yes">4x</span>
|
|
485
513
|
<span class="cline-any cline-yes">4x</span>
|
|
514
|
+
<span class="cline-any cline-neutral"> </span>
|
|
515
|
+
<span class="cline-any cline-neutral"> </span>
|
|
486
516
|
<span class="cline-any cline-yes">4x</span>
|
|
487
517
|
<span class="cline-any cline-yes">4x</span>
|
|
488
518
|
<span class="cline-any cline-yes">4x</span>
|
|
489
519
|
<span class="cline-any cline-neutral"> </span>
|
|
490
520
|
<span class="cline-any cline-yes">4x</span>
|
|
521
|
+
<span class="cline-any cline-neutral"> </span>
|
|
522
|
+
<span class="cline-any cline-neutral"> </span>
|
|
523
|
+
<span class="cline-any cline-neutral"> </span>
|
|
491
524
|
<span class="cline-any cline-yes">4x</span>
|
|
492
525
|
<span class="cline-any cline-yes">4x</span>
|
|
493
526
|
<span class="cline-any cline-yes">4x</span>
|
|
494
527
|
<span class="cline-any cline-yes">4x</span>
|
|
495
528
|
<span class="cline-any cline-yes">4x</span>
|
|
529
|
+
<span class="cline-any cline-neutral"> </span>
|
|
530
|
+
<span class="cline-any cline-neutral"> </span>
|
|
496
531
|
<span class="cline-any cline-yes">4x</span>
|
|
497
532
|
<span class="cline-any cline-neutral"> </span>
|
|
498
533
|
<span class="cline-any cline-neutral"> </span>
|
|
499
534
|
<span class="cline-any cline-neutral"> </span>
|
|
500
535
|
<span class="cline-any cline-neutral"> </span>
|
|
501
536
|
<span class="cline-any cline-neutral"> </span>
|
|
537
|
+
<span class="cline-any cline-neutral"> </span>
|
|
538
|
+
<span class="cline-any cline-neutral"> </span>
|
|
539
|
+
<span class="cline-any cline-neutral"> </span>
|
|
540
|
+
<span class="cline-any cline-neutral"> </span>
|
|
541
|
+
<span class="cline-any cline-neutral"> </span>
|
|
502
542
|
<span class="cline-any cline-no"> </span>
|
|
503
543
|
<span class="cline-any cline-neutral"> </span>
|
|
504
544
|
<span class="cline-any cline-neutral"> </span>
|
|
505
545
|
<span class="cline-any cline-neutral"> </span>
|
|
546
|
+
<span class="cline-any cline-neutral"> </span>
|
|
547
|
+
<span class="cline-any cline-neutral"> </span>
|
|
548
|
+
<span class="cline-any cline-neutral"> </span>
|
|
549
|
+
<span class="cline-any cline-neutral"> </span>
|
|
506
550
|
<span class="cline-any cline-no"> </span>
|
|
507
551
|
<span class="cline-any cline-no"> </span>
|
|
508
552
|
<span class="cline-any cline-no"> </span>
|
|
@@ -514,8 +558,16 @@
|
|
|
514
558
|
<span class="cline-any cline-neutral"> </span>
|
|
515
559
|
<span class="cline-any cline-neutral"> </span>
|
|
516
560
|
<span class="cline-any cline-neutral"> </span>
|
|
561
|
+
<span class="cline-any cline-neutral"> </span>
|
|
562
|
+
<span class="cline-any cline-neutral"> </span>
|
|
563
|
+
<span class="cline-any cline-neutral"> </span>
|
|
564
|
+
<span class="cline-any cline-neutral"> </span>
|
|
565
|
+
<span class="cline-any cline-neutral"> </span>
|
|
517
566
|
<span class="cline-any cline-yes">6x</span>
|
|
518
567
|
<span class="cline-any cline-yes">6x</span>
|
|
568
|
+
<span class="cline-any cline-neutral"> </span>
|
|
569
|
+
<span class="cline-any cline-neutral"> </span>
|
|
570
|
+
<span class="cline-any cline-neutral"> </span>
|
|
519
571
|
<span class="cline-any cline-yes">6x</span>
|
|
520
572
|
<span class="cline-any cline-yes">6x</span>
|
|
521
573
|
<span class="cline-any cline-yes">6x</span>
|
|
@@ -575,10 +627,11 @@
|
|
|
575
627
|
<span class="cline-any cline-no"> </span>
|
|
576
628
|
<span class="cline-any cline-neutral"> </span>
|
|
577
629
|
<span class="cline-any cline-neutral"> </span>
|
|
578
|
-
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { docker } from
|
|
579
|
-
import { imageExists, pullImage } from
|
|
580
|
-
import { TIMEOUTS, IMAGE_PROFILES } from
|
|
581
|
-
import { logger } from
|
|
630
|
+
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { docker } from '../dockerService.js';
|
|
631
|
+
import { imageExists, pullImage } from './imageUtils.js';
|
|
632
|
+
import { TIMEOUTS, IMAGE_PROFILES } from '../../constants.js';
|
|
633
|
+
import { logger } from '../../logger.js';
|
|
634
|
+
import { normalizeImageName } from '../../imageNameUtils.js';
|
|
582
635
|
|
|
583
636
|
/**
|
|
584
637
|
* Helper to add timeout to promises. If the provided promise does not settle
|
|
@@ -592,9 +645,9 @@ import { logger } from "../../logger.js";
|
|
|
592
645
|
function withTimeout(promise, ms = <span class="branch-0 cbranch-no" title="branch not covered" >TIMEOUTS.CONTAINER_OP)</span> {
|
|
593
646
|
return Promise.race([
|
|
594
647
|
promise,
|
|
595
|
-
new Promise((_, reject) =>
|
|
648
|
+
new Promise((_, reject) =>
|
|
596
649
|
setTimeout(<span class="fstat-no" title="function not covered" >()</span> => <span class="cstat-no" title="statement not covered" >reject(new Error('Operation timed out')),</span> ms)
|
|
597
|
-
)
|
|
650
|
+
),
|
|
598
651
|
]);
|
|
599
652
|
}
|
|
600
653
|
|
|
@@ -606,34 +659,17 @@ function withTimeout(promise, ms = <span class="branch-0 cbranch-no" title="bran
|
|
|
606
659
|
* @throws {Error} If Docker reports an error
|
|
607
660
|
*/
|
|
608
661
|
export async function removeContainer(containerId) {
|
|
609
|
-
logger.debug(
|
|
662
|
+
logger.debug('Removing container %s', containerId);
|
|
610
663
|
const container = docker.getContainer(containerId);
|
|
611
664
|
try {
|
|
612
665
|
await withTimeout(container.remove({ force: true }), TIMEOUTS.CONTAINER_OP);
|
|
613
|
-
logger.info(
|
|
666
|
+
logger.info('Removed container %s', containerId);
|
|
614
667
|
} catch (err) {
|
|
615
|
-
<span class="cstat-no" title="statement not covered" > logger.error(
|
|
668
|
+
<span class="cstat-no" title="statement not covered" > logger.error('Failed to remove container %s', containerId, err);</span>
|
|
616
669
|
<span class="cstat-no" title="statement not covered" > throw new Error('Error removing container: ' + err.message);</span>
|
|
617
670
|
}
|
|
618
671
|
}
|
|
619
672
|
|
|
620
|
-
/**
|
|
621
|
-
* Normalize image name to base name (strips registry prefix and tag).
|
|
622
|
-
* e.g. "docker.io/library/postgres:16" → "postgres"
|
|
623
|
-
*
|
|
624
|
-
* @param {string} imageName - Raw image name
|
|
625
|
-
* @returns {string} Base name (lowercase)
|
|
626
|
-
*/
|
|
627
|
-
function normalizeImageName(imageName) {
|
|
628
|
-
<span class="missing-if-branch" title="if path not taken" >I</span>if (!imageName) <span class="cstat-no" title="statement not covered" >return "";</span>
|
|
629
|
-
let name = imageName;
|
|
630
|
-
const slashIdx = name.lastIndexOf("/");
|
|
631
|
-
<span class="missing-if-branch" title="if path not taken" >I</span>if (slashIdx !== -1) <span class="cstat-no" title="statement not covered" >name = name.slice(slashIdx + 1);</span>
|
|
632
|
-
const colonIdx = name.indexOf(":");
|
|
633
|
-
<span class="missing-if-branch" title="else path not taken" >E</span>if (colonIdx !== -1) name = name.slice(0, colonIdx);
|
|
634
|
-
return name.toLowerCase();
|
|
635
|
-
}
|
|
636
|
-
|
|
637
673
|
/**
|
|
638
674
|
* Create a new container from an image. If the image is missing locally, it will be pulled.
|
|
639
675
|
*
|
|
@@ -643,22 +679,26 @@ function normalizeImageName(imageName) {
|
|
|
643
679
|
* @returns {Promise<{id: string, ports: Array<{containerPort: string, hostPort: string, protocol: string, source: string}>}>}
|
|
644
680
|
* @throws {Error} If image listing/pull or creation fails
|
|
645
681
|
*/
|
|
646
|
-
export async function createContainer(
|
|
647
|
-
|
|
682
|
+
export async function createContainer(
|
|
683
|
+
imageName,
|
|
684
|
+
options = {},
|
|
685
|
+
imageProfiles = IMAGE_PROFILES
|
|
686
|
+
) {
|
|
687
|
+
logger.info('Creating container from image %s', imageName);
|
|
648
688
|
let exists;
|
|
649
689
|
try {
|
|
650
690
|
exists = await withTimeout(imageExists(imageName), 10000);
|
|
651
691
|
} catch (err) {
|
|
652
|
-
<span class="cstat-no" title="statement not covered" > logger.error(
|
|
692
|
+
<span class="cstat-no" title="statement not covered" > logger.error('Could not check local images for %s', imageName, err);</span>
|
|
653
693
|
<span class="cstat-no" title="statement not covered" > throw new Error('Error listing local images: ' + err.message);</span>
|
|
654
694
|
}
|
|
655
695
|
if (!exists) {
|
|
656
|
-
logger.info(
|
|
696
|
+
logger.info('Image %s not found locally, pulling', imageName);
|
|
657
697
|
try {
|
|
658
698
|
await withTimeout(pullImage(imageName), TIMEOUTS.PULL_IMAGE);
|
|
659
|
-
logger.info(
|
|
699
|
+
logger.info('Pulled image %s', imageName);
|
|
660
700
|
} catch (err) {
|
|
661
|
-
<span class="cstat-no" title="statement not covered" > logger.error(
|
|
701
|
+
<span class="cstat-no" title="statement not covered" > logger.error('Failed to pull image %s', imageName, err);</span>
|
|
662
702
|
<span class="cstat-no" title="statement not covered" > throw new Error('Could not pull image: ' + err.message);</span>
|
|
663
703
|
}
|
|
664
704
|
}
|
|
@@ -668,7 +708,9 @@ export async function createContainer(imageName, options = {}, imageProfiles = I
|
|
|
668
708
|
...options,
|
|
669
709
|
};
|
|
670
710
|
|
|
671
|
-
const hasUserDefinedPorts = Boolean(
|
|
711
|
+
const hasUserDefinedPorts = Boolean(
|
|
712
|
+
createOpts.ExposedPorts && <span class="branch-1 cbranch-no" title="branch not covered" >Object.keys(createOpts.ExposedPorts).length</span>
|
|
713
|
+
);
|
|
672
714
|
|
|
673
715
|
/** @type {Array<{containerPort: string, hostPort: string, protocol: string, source: string}>} */
|
|
674
716
|
const assignedPorts = [];
|
|
@@ -676,11 +718,14 @@ export async function createContainer(imageName, options = {}, imageProfiles = I
|
|
|
676
718
|
if (!hasUserDefinedPorts) {
|
|
677
719
|
try {
|
|
678
720
|
const image = docker.getImage(imageName);
|
|
679
|
-
<span class="missing-if-branch" title="else path not taken" >E</span>if (image && typeof image.inspect ===
|
|
721
|
+
<span class="missing-if-branch" title="else path not taken" >E</span>if (image && typeof image.inspect === 'function') {
|
|
680
722
|
let usedHostPorts = new Set();
|
|
681
|
-
<span class="missing-if-branch" title="else path not taken" >E</span>if (typeof docker.listContainers ===
|
|
723
|
+
<span class="missing-if-branch" title="else path not taken" >E</span>if (typeof docker.listContainers === 'function') {
|
|
682
724
|
try {
|
|
683
|
-
const containers = await withTimeout(
|
|
725
|
+
const containers = await withTimeout(
|
|
726
|
+
docker.listContainers({ all: true }),
|
|
727
|
+
TIMEOUTS.CONTAINER_OP
|
|
728
|
+
);
|
|
684
729
|
containers.forEach((container) => {
|
|
685
730
|
(container.Ports || <span class="branch-1 cbranch-no" title="branch not covered" >[])</span>.forEach((portInfo) => {
|
|
686
731
|
<span class="missing-if-branch" title="else path not taken" >E</span>if (portInfo && portInfo.PublicPort) {
|
|
@@ -689,7 +734,10 @@ export async function createContainer(imageName, options = {}, imageProfiles = I
|
|
|
689
734
|
});
|
|
690
735
|
});
|
|
691
736
|
} catch (err) {
|
|
692
|
-
<span class="cstat-no" title="statement not covered" > logger.warn(
|
|
737
|
+
<span class="cstat-no" title="statement not covered" > logger.warn(</span>
|
|
738
|
+
'Could not inspect existing containers to auto-map ports',
|
|
739
|
+
err
|
|
740
|
+
);
|
|
693
741
|
}
|
|
694
742
|
}
|
|
695
743
|
const reservePort = (port) => {
|
|
@@ -720,8 +768,14 @@ export async function createContainer(imageName, options = {}, imageProfiles = I
|
|
|
720
768
|
return String(candidate);
|
|
721
769
|
};
|
|
722
770
|
|
|
723
|
-
const inspectData = await withTimeout(
|
|
724
|
-
|
|
771
|
+
const inspectData = await withTimeout(
|
|
772
|
+
image.inspect(),
|
|
773
|
+
TIMEOUTS.CONTAINER_OP
|
|
774
|
+
);
|
|
775
|
+
let exposed =
|
|
776
|
+
inspectData?.Config?.ExposedPorts ||
|
|
777
|
+
<span class="branch-1 cbranch-no" title="branch not covered" > inspectData?.ContainerConfig?.ExposedPorts </span>||
|
|
778
|
+
<span class="branch-2 cbranch-no" title="branch not covered" > {};</span>
|
|
725
779
|
let exposedKeys = Object.keys(exposed || <span class="branch-1 cbranch-no" title="branch not covered" >{})</span>;
|
|
726
780
|
|
|
727
781
|
// If no ExposedPorts in image, fall back to IMAGE_PROFILES defaultPort
|
|
@@ -738,45 +792,69 @@ export async function createContainer(imageName, options = {}, imageProfiles = I
|
|
|
738
792
|
if (exposedKeys.length) {
|
|
739
793
|
createOpts.ExposedPorts = createOpts.ExposedPorts || {};
|
|
740
794
|
createOpts.HostConfig = { ...(createOpts.HostConfig || {}) };
|
|
741
|
-
createOpts.HostConfig.PortBindings = {
|
|
795
|
+
createOpts.HostConfig.PortBindings = {
|
|
796
|
+
...(createOpts.HostConfig.PortBindings || {}),
|
|
797
|
+
};
|
|
742
798
|
exposedKeys.forEach((portKey) => {
|
|
743
799
|
<span class="missing-if-branch" title="else path not taken" >E</span>if (!createOpts.ExposedPorts[portKey]) {
|
|
744
800
|
createOpts.ExposedPorts[portKey] = exposed[portKey] || <span class="branch-1 cbranch-no" title="branch not covered" >{};</span>
|
|
745
801
|
}
|
|
746
|
-
<span class="missing-if-branch" title="else path not taken" >E</span>if (
|
|
747
|
-
|
|
802
|
+
<span class="missing-if-branch" title="else path not taken" >E</span>if (
|
|
803
|
+
!createOpts.HostConfig.PortBindings[portKey] ||
|
|
804
|
+
<span class="branch-1 cbranch-no" title="branch not covered" > !createOpts.HostConfig.PortBindings[portKey].length</span>
|
|
805
|
+
) {
|
|
806
|
+
const parts = portKey.split('/');
|
|
748
807
|
const containerPort = parts[0];
|
|
749
|
-
const protocol = parts[1] || <span class="branch-1 cbranch-no" title="branch not covered" >
|
|
808
|
+
const protocol = parts[1] || <span class="branch-1 cbranch-no" title="branch not covered" >'tcp';</span>
|
|
750
809
|
const hostPort = pickNextAvailablePort(containerPort);
|
|
751
|
-
createOpts.HostConfig.PortBindings[portKey] = [
|
|
752
|
-
|
|
810
|
+
createOpts.HostConfig.PortBindings[portKey] = [
|
|
811
|
+
{ HostPort: hostPort },
|
|
812
|
+
];
|
|
813
|
+
assignedPorts.push({
|
|
814
|
+
containerPort,
|
|
815
|
+
hostPort,
|
|
816
|
+
protocol,
|
|
817
|
+
source: 'auto',
|
|
818
|
+
});
|
|
753
819
|
}
|
|
754
820
|
});
|
|
755
821
|
}
|
|
756
822
|
}
|
|
757
823
|
} catch (err) {
|
|
758
|
-
<span class="cstat-no" title="statement not covered" > logger.warn(
|
|
824
|
+
<span class="cstat-no" title="statement not covered" > logger.warn(</span>
|
|
825
|
+
'Could not inspect image %s for default ports',
|
|
826
|
+
imageName,
|
|
827
|
+
err
|
|
828
|
+
);
|
|
759
829
|
}
|
|
760
830
|
} else <span class="missing-if-branch" title="else path not taken" >E</span>{
|
|
761
831
|
// User-defined ports: collect them with source: "user"
|
|
762
832
|
const portBindings = <span class="cstat-no" title="statement not covered" >createOpts.HostConfig?.PortBindings || {};</span>
|
|
763
833
|
<span class="cstat-no" title="statement not covered" > Object.entries(portBindings).forEach(<span class="fstat-no" title="function not covered" >([</span>portKey, bindings]) => {</span>
|
|
764
|
-
const parts = <span class="cstat-no" title="statement not covered" >portKey.split(
|
|
834
|
+
const parts = <span class="cstat-no" title="statement not covered" >portKey.split('/');</span>
|
|
765
835
|
const containerPort = <span class="cstat-no" title="statement not covered" >parts[0];</span>
|
|
766
|
-
const protocol = <span class="cstat-no" title="statement not covered" >parts[1] ||
|
|
836
|
+
const protocol = <span class="cstat-no" title="statement not covered" >parts[1] || 'tcp';</span>
|
|
767
837
|
<span class="cstat-no" title="statement not covered" > (bindings || []).forEach(<span class="fstat-no" title="function not covered" >(b</span>) => {</span>
|
|
768
|
-
<span class="cstat-no" title="statement not covered" > assignedPorts.push({
|
|
838
|
+
<span class="cstat-no" title="statement not covered" > assignedPorts.push({</span>
|
|
839
|
+
containerPort,
|
|
840
|
+
hostPort: b.HostPort,
|
|
841
|
+
protocol,
|
|
842
|
+
source: 'user',
|
|
843
|
+
});
|
|
769
844
|
});
|
|
770
845
|
});
|
|
771
846
|
}
|
|
772
847
|
|
|
773
848
|
try {
|
|
774
|
-
const container = await withTimeout(
|
|
849
|
+
const container = await withTimeout(
|
|
850
|
+
docker.createContainer(createOpts),
|
|
851
|
+
TIMEOUTS.CONTAINER_OP
|
|
852
|
+
);
|
|
775
853
|
const containerId = container.id || container.Id;
|
|
776
|
-
logger.info(
|
|
854
|
+
logger.info('Created container %s from image %s', containerId, imageName);
|
|
777
855
|
return { id: containerId, ports: assignedPorts };
|
|
778
856
|
} catch (err) {
|
|
779
|
-
<span class="cstat-no" title="statement not covered" > logger.error(
|
|
857
|
+
<span class="cstat-no" title="statement not covered" > logger.error('Failed to create container from image %s', imageName, err);</span>
|
|
780
858
|
<span class="cstat-no" title="statement not covered" > throw new Error('Error creating container: ' + err.message);</span>
|
|
781
859
|
}
|
|
782
860
|
}
|
|
@@ -788,12 +866,12 @@ export async function createContainer(imageName, options = {}, imageProfiles = I
|
|
|
788
866
|
*/
|
|
789
867
|
export async function startContainer(containerId) {
|
|
790
868
|
const container = docker.getContainer(containerId);
|
|
791
|
-
logger.debug(
|
|
869
|
+
logger.debug('Starting container %s', containerId);
|
|
792
870
|
try {
|
|
793
871
|
await withTimeout(container.start(), TIMEOUTS.CONTAINER_OP);
|
|
794
|
-
logger.info(
|
|
872
|
+
logger.info('Started container %s', containerId);
|
|
795
873
|
} catch (err) {
|
|
796
|
-
<span class="cstat-no" title="statement not covered" > logger.error(
|
|
874
|
+
<span class="cstat-no" title="statement not covered" > logger.error('Failed to start container %s', containerId, err);</span>
|
|
797
875
|
<span class="cstat-no" title="statement not covered" > throw err;</span>
|
|
798
876
|
}
|
|
799
877
|
}
|
|
@@ -805,12 +883,12 @@ export async function startContainer(containerId) {
|
|
|
805
883
|
*/
|
|
806
884
|
export async function stopContainer(containerId) {
|
|
807
885
|
const container = docker.getContainer(containerId);
|
|
808
|
-
logger.debug(
|
|
886
|
+
logger.debug('Stopping container %s', containerId);
|
|
809
887
|
try {
|
|
810
888
|
await withTimeout(container.stop(), TIMEOUTS.CONTAINER_OP);
|
|
811
|
-
logger.info(
|
|
889
|
+
logger.info('Stopped container %s', containerId);
|
|
812
890
|
} catch (err) {
|
|
813
|
-
<span class="cstat-no" title="statement not covered" > logger.error(
|
|
891
|
+
<span class="cstat-no" title="statement not covered" > logger.error('Failed to stop container %s', containerId, err);</span>
|
|
814
892
|
<span class="cstat-no" title="statement not covered" > throw err;</span>
|
|
815
893
|
}
|
|
816
894
|
}
|
|
@@ -822,12 +900,12 @@ export async function stopContainer(containerId) {
|
|
|
822
900
|
*/
|
|
823
901
|
export async function restartContainer(containerId) {
|
|
824
902
|
const container = docker.getContainer(containerId);
|
|
825
|
-
logger.debug(
|
|
903
|
+
logger.debug('Restarting container %s', containerId);
|
|
826
904
|
try {
|
|
827
905
|
await withTimeout(container.restart(), TIMEOUTS.CONTAINER_OP);
|
|
828
|
-
logger.info(
|
|
906
|
+
logger.info('Restarted container %s', containerId);
|
|
829
907
|
} catch (err) {
|
|
830
|
-
<span class="cstat-no" title="statement not covered" > logger.error(
|
|
908
|
+
<span class="cstat-no" title="statement not covered" > logger.error('Failed to restart container %s', containerId, err);</span>
|
|
831
909
|
<span class="cstat-no" title="statement not covered" > throw err;</span>
|
|
832
910
|
}
|
|
833
911
|
}
|
|
@@ -838,7 +916,7 @@ export async function restartContainer(containerId) {
|
|
|
838
916
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
839
917
|
Code coverage generated by
|
|
840
918
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
841
|
-
at 2026-04-
|
|
919
|
+
at 2026-04-27T21:36:40.677Z
|
|
842
920
|
</div>
|
|
843
921
|
<script src="../../../prettify.js"></script>
|
|
844
922
|
<script>
|