cdd-cli 3.2.2 → 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/.~lock.ROADMAP.md# +1 -0
- package/CHANGELOG.md +51 -0
- package/README.es.md +198 -0
- package/README.md +81 -23
- package/ROADMAP.md +647 -0
- package/coverage/clover.xml +400 -519
- package/coverage/coverage-final.json +16 -20
- 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 +31 -13
- package/coverage/lcov-report/components/PromptField.jsx.html +4 -4
- package/coverage/lcov-report/components/SuggestionPanel.jsx.html +37 -13
- package/coverage/lcov-report/components/index.html +14 -14
- package/coverage/lcov-report/helpers/constants.js.html +87 -33
- package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +40 -40
- package/coverage/lcov-report/helpers/dockerHubService.js.html +310 -0
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +1 -1
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +1 -1
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +1 -1
- package/coverage/lcov-report/helpers/imageNameUtils.js.html +18 -18
- package/coverage/lcov-report/helpers/index.html +77 -62
- package/coverage/lcov-report/helpers/logger.js.html +60 -60
- package/coverage/lcov-report/helpers/safeCall.js.html +14 -14
- package/coverage/lcov-report/helpers/validationHelpers.js.html +52 -52
- package/coverage/lcov-report/hooks/creation/index.html +19 -19
- package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +4 -4
- package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +225 -48
- package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +5 -5
- package/coverage/lcov-report/hooks/debug/index.html +1 -1
- package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +7 -7
- package/coverage/lcov-report/hooks/index.html +15 -15
- package/coverage/lcov-report/hooks/navigation/index.html +1 -1
- package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +6 -6
- package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +13 -13
- package/coverage/lcov-report/hooks/useControls.js.html +82 -40
- package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +5 -5
- package/coverage/lcov-report/hooks/useExitHandler.js.html +6 -6
- package/coverage/lcov-report/index.html +38 -68
- package/coverage/lcov.info +742 -981
- package/dist/App.js +7 -1
- package/dist/components/ContainerCreationPrompt.js +26 -10
- package/dist/components/ControlsHUD.js +106 -0
- package/dist/components/SuggestionPanel.js +16 -7
- package/dist/helpers/constants.js +60 -21
- package/dist/helpers/dockerHubService.js +111 -0
- package/dist/helpers/logger.js +1 -1
- package/dist/hooks/creation/useContainerActions.js +22 -5
- package/dist/hooks/creation/useContainerCreation.js +395 -107
- package/dist/hooks/useContainerCommandRouter.js +1 -1
- package/dist/hooks/useControls.js +23 -7
- package/dist/index.js +7 -0
- package/package.json +1 -1
- package/src/App.jsx +6 -0
- package/src/components/ContainerCreationPrompt.jsx +15 -5
- package/src/components/ControlsHUD.jsx +66 -0
- package/src/components/SuggestionPanel.jsx +10 -2
- package/src/helpers/constants.js +38 -20
- package/src/helpers/dockerHubService.js +75 -0
- package/src/helpers/logger.js +1 -2
- package/src/hooks/creation/useContainerActions.js +19 -6
- package/src/hooks/creation/useContainerCreation.js +224 -87
- package/src/hooks/useContainerCommandRouter.js +1 -1
- package/src/hooks/useControls.js +24 -8
- package/src/index.js +4 -0
- package/test/ContainerCreationPrompt.dom.test.js +245 -0
- package/test/ControlsHUD.dom.test.js +125 -0
- package/test/SuggestionPanel.dom.test.js +30 -0
- package/test/constants.test.js +7 -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 +306 -3
- package/test/useContainerCreationHub.dom.test.js +184 -0
- package/test/useControls.dom.test.js +212 -6
package/dist/App.js
CHANGED
|
@@ -32,7 +32,13 @@ export default function App() {
|
|
|
32
32
|
portInput: controls.portInput,
|
|
33
33
|
envInput: controls.envInput,
|
|
34
34
|
message: controls.message,
|
|
35
|
-
messageColor: controls.messageColor
|
|
35
|
+
messageColor: controls.messageColor,
|
|
36
|
+
suggestions: controls.creation.suggestions,
|
|
37
|
+
selectedSuggestionIndex: controls.creation.selectedSuggestionIndex,
|
|
38
|
+
visibleOffset: controls.creation.visibleOffset,
|
|
39
|
+
isSearchingHub: controls.creation.isSearchingHub,
|
|
40
|
+
hubResults: controls.creation.hubResults,
|
|
41
|
+
hasSuggestedEnv: controls.creation.hasSuggestedEnv
|
|
36
42
|
});
|
|
37
43
|
}
|
|
38
44
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Box
|
|
2
|
+
import { Box } from 'ink';
|
|
3
3
|
import { PromptField, PromptMessage } from './PromptField.js';
|
|
4
4
|
import { SuggestionPanel } from './SuggestionPanel.js';
|
|
5
|
+
import { ControlsHUD } from './ControlsHUD.js';
|
|
5
6
|
import PropTypes from 'prop-types';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -35,9 +36,15 @@ export default function ContainerCreationPrompt(props) {
|
|
|
35
36
|
_props$selectedSugges = props.selectedSuggestionIndex,
|
|
36
37
|
selectedSuggestionIndex = _props$selectedSugges === void 0 ? -1 : _props$selectedSugges,
|
|
37
38
|
_props$visibleOffset = props.visibleOffset,
|
|
38
|
-
visibleOffset = _props$visibleOffset === void 0 ? 0 : _props$visibleOffset
|
|
39
|
+
visibleOffset = _props$visibleOffset === void 0 ? 0 : _props$visibleOffset,
|
|
40
|
+
_props$hubResults = props.hubResults,
|
|
41
|
+
hubResults = _props$hubResults === void 0 ? null : _props$hubResults,
|
|
42
|
+
_props$isSearchingHub = props.isSearchingHub,
|
|
43
|
+
isSearchingHub = _props$isSearchingHub === void 0 ? false : _props$isSearchingHub,
|
|
44
|
+
_props$hasSuggestedEn = props.hasSuggestedEnv,
|
|
45
|
+
hasSuggestedEnv = _props$hasSuggestedEn === void 0 ? false : _props$hasSuggestedEn;
|
|
39
46
|
var prompts = [{
|
|
40
|
-
label: 'Name of the image to create (e.g., nginx:
|
|
47
|
+
label: 'Name of the image to create (e.g., nginx:1.27-alpine):',
|
|
41
48
|
value: imageName,
|
|
42
49
|
required: true
|
|
43
50
|
}, {
|
|
@@ -57,7 +64,9 @@ export default function ContainerCreationPrompt(props) {
|
|
|
57
64
|
label = _ref.label,
|
|
58
65
|
value = _ref.value,
|
|
59
66
|
required = _ref.required;
|
|
60
|
-
var
|
|
67
|
+
var activeItems = hubResults !== null && hubResults !== void 0 ? hubResults : suggestions;
|
|
68
|
+
var showSuggestions = step === 0 && (isSearchingHub || activeItems.length > 0);
|
|
69
|
+
var hasSuggestions = (suggestions === null || suggestions === void 0 ? void 0 : suggestions.length) > 0 || (hubResults === null || hubResults === void 0 ? void 0 : hubResults.length) > 0;
|
|
61
70
|
return /*#__PURE__*/React.createElement(Box, {
|
|
62
71
|
flexDirection: "column",
|
|
63
72
|
borderStyle: "round",
|
|
@@ -68,15 +77,19 @@ export default function ContainerCreationPrompt(props) {
|
|
|
68
77
|
value: value,
|
|
69
78
|
required: required
|
|
70
79
|
}), showSuggestions && /*#__PURE__*/React.createElement(SuggestionPanel, {
|
|
71
|
-
items:
|
|
80
|
+
items: activeItems,
|
|
72
81
|
selectedIndex: selectedSuggestionIndex,
|
|
73
|
-
visibleOffset: visibleOffset
|
|
82
|
+
visibleOffset: visibleOffset,
|
|
83
|
+
isLoading: isSearchingHub
|
|
74
84
|
}), /*#__PURE__*/React.createElement(PromptMessage, {
|
|
75
85
|
message: message,
|
|
76
86
|
color: messageColor
|
|
77
|
-
}), /*#__PURE__*/React.createElement(
|
|
78
|
-
|
|
79
|
-
|
|
87
|
+
}), /*#__PURE__*/React.createElement(ControlsHUD, {
|
|
88
|
+
step: step,
|
|
89
|
+
hasSuggestions: hasSuggestions,
|
|
90
|
+
isSearchingHub: isSearchingHub,
|
|
91
|
+
hasSuggestedEnv: hasSuggestedEnv
|
|
92
|
+
}));
|
|
80
93
|
}
|
|
81
94
|
ContainerCreationPrompt.propTypes = {
|
|
82
95
|
step: PropTypes.number.isRequired,
|
|
@@ -88,7 +101,10 @@ ContainerCreationPrompt.propTypes = {
|
|
|
88
101
|
messageColor: PropTypes.string,
|
|
89
102
|
suggestions: PropTypes.arrayOf(PropTypes.string),
|
|
90
103
|
selectedSuggestionIndex: PropTypes.number,
|
|
91
|
-
visibleOffset: PropTypes.number
|
|
104
|
+
visibleOffset: PropTypes.number,
|
|
105
|
+
hubResults: PropTypes.arrayOf(PropTypes.string),
|
|
106
|
+
isSearchingHub: PropTypes.bool,
|
|
107
|
+
hasSuggestedEnv: PropTypes.bool
|
|
92
108
|
};
|
|
93
109
|
|
|
94
110
|
// Named export for test compatibility with jest ESM interop
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the contextual keyboard hints for the current wizard state.
|
|
6
|
+
*
|
|
7
|
+
* @param {number} step - Current wizard step (0-3)
|
|
8
|
+
* @param {boolean} hasSuggestions - Whether suggestions/hub results are visible
|
|
9
|
+
* @param {boolean} isSearchingHub - Whether a Hub search is in progress
|
|
10
|
+
* @param {boolean} [hasSuggestedEnv=false] - Whether the image has suggestedEnv entries
|
|
11
|
+
* @returns {{ key: string, label: string }[]}
|
|
12
|
+
*/
|
|
13
|
+
function getHints(step, hasSuggestions, isSearchingHub) {
|
|
14
|
+
var hasSuggestedEnv = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
15
|
+
if (step === 0) {
|
|
16
|
+
if (hasSuggestions) {
|
|
17
|
+
return [{
|
|
18
|
+
key: '↑↓',
|
|
19
|
+
label: 'Navigate'
|
|
20
|
+
}, {
|
|
21
|
+
key: 'Enter',
|
|
22
|
+
label: 'Select'
|
|
23
|
+
}, {
|
|
24
|
+
key: 'Esc',
|
|
25
|
+
label: 'Cancel'
|
|
26
|
+
}];
|
|
27
|
+
}
|
|
28
|
+
var hints = [];
|
|
29
|
+
if (!isSearchingHub) hints.push({
|
|
30
|
+
key: 'Tab',
|
|
31
|
+
label: 'Search Hub'
|
|
32
|
+
});
|
|
33
|
+
hints.push({
|
|
34
|
+
key: '↑↓',
|
|
35
|
+
label: 'Browse'
|
|
36
|
+
});
|
|
37
|
+
hints.push({
|
|
38
|
+
key: 'Enter',
|
|
39
|
+
label: 'Confirm'
|
|
40
|
+
});
|
|
41
|
+
hints.push({
|
|
42
|
+
key: 'Esc',
|
|
43
|
+
label: 'Cancel'
|
|
44
|
+
});
|
|
45
|
+
return hints;
|
|
46
|
+
}
|
|
47
|
+
if (step === 3) {
|
|
48
|
+
var _hints = [];
|
|
49
|
+
if (hasSuggestedEnv) _hints.push({
|
|
50
|
+
key: 'Tab',
|
|
51
|
+
label: 'Insert next env'
|
|
52
|
+
});
|
|
53
|
+
_hints.push({
|
|
54
|
+
key: 'Enter',
|
|
55
|
+
label: 'Continue'
|
|
56
|
+
});
|
|
57
|
+
_hints.push({
|
|
58
|
+
key: 'Esc',
|
|
59
|
+
label: 'Cancel'
|
|
60
|
+
});
|
|
61
|
+
return _hints;
|
|
62
|
+
}
|
|
63
|
+
return [{
|
|
64
|
+
key: 'Enter',
|
|
65
|
+
label: 'Continue'
|
|
66
|
+
}, {
|
|
67
|
+
key: 'Esc',
|
|
68
|
+
label: 'Cancel'
|
|
69
|
+
}];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Displays a row of contextual keyboard shortcut hints for the active wizard step.
|
|
74
|
+
*
|
|
75
|
+
* @param {Object} props
|
|
76
|
+
* @param {number} props.step - Current wizard step
|
|
77
|
+
* @param {boolean} [props.hasSuggestions=false] - Whether suggestions/hub results are visible
|
|
78
|
+
* @param {boolean} [props.isSearchingHub=false] - Whether a Hub search is in progress
|
|
79
|
+
* @param {boolean} [props.hasSuggestedEnv=false] - Whether the image has suggestedEnv entries
|
|
80
|
+
*/
|
|
81
|
+
function ControlsHUD(_ref) {
|
|
82
|
+
var step = _ref.step,
|
|
83
|
+
_ref$hasSuggestions = _ref.hasSuggestions,
|
|
84
|
+
hasSuggestions = _ref$hasSuggestions === void 0 ? false : _ref$hasSuggestions,
|
|
85
|
+
_ref$isSearchingHub = _ref.isSearchingHub,
|
|
86
|
+
isSearchingHub = _ref$isSearchingHub === void 0 ? false : _ref$isSearchingHub,
|
|
87
|
+
_ref$hasSuggestedEnv = _ref.hasSuggestedEnv,
|
|
88
|
+
hasSuggestedEnv = _ref$hasSuggestedEnv === void 0 ? false : _ref$hasSuggestedEnv;
|
|
89
|
+
var hints = getHints(step, hasSuggestions, isSearchingHub, hasSuggestedEnv);
|
|
90
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
91
|
+
flexDirection: "row",
|
|
92
|
+
columnGap: 2
|
|
93
|
+
}, hints.map(function (_ref2) {
|
|
94
|
+
var key = _ref2.key,
|
|
95
|
+
label = _ref2.label;
|
|
96
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
97
|
+
key: key
|
|
98
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
99
|
+
color: "cyan"
|
|
100
|
+
}, "[", key, "]"), /*#__PURE__*/React.createElement(Text, {
|
|
101
|
+
dimColor: true
|
|
102
|
+
}, " ", label));
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
export default ControlsHUD;
|
|
106
|
+
export { getHints, ControlsHUD };
|
|
@@ -19,26 +19,35 @@ export function SuggestionPanel(_ref) {
|
|
|
19
19
|
selectedIndex = _ref.selectedIndex,
|
|
20
20
|
visibleOffset = _ref.visibleOffset,
|
|
21
21
|
_ref$maxVisible = _ref.maxVisible,
|
|
22
|
-
maxVisible = _ref$maxVisible === void 0 ? 6 : _ref$maxVisible
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
maxVisible = _ref$maxVisible === void 0 ? 6 : _ref$maxVisible,
|
|
23
|
+
_ref$isLoading = _ref.isLoading,
|
|
24
|
+
isLoading = _ref$isLoading === void 0 ? false : _ref$isLoading;
|
|
25
|
+
var hasItems = items && items.length > 0;
|
|
26
|
+
if (!hasItems && !isLoading) return null;
|
|
27
|
+
var visible = hasItems ? items.slice(visibleOffset, visibleOffset + maxVisible) : [];
|
|
25
28
|
return /*#__PURE__*/React.createElement(Box, {
|
|
26
29
|
flexDirection: "column",
|
|
27
30
|
borderStyle: "single",
|
|
28
31
|
borderColor: "cyan",
|
|
29
|
-
paddingX: 1
|
|
32
|
+
paddingX: 1,
|
|
33
|
+
"data-testid": "suggestion-panel"
|
|
30
34
|
}, visible.map(function (item, localIdx) {
|
|
31
35
|
var globalIdx = visibleOffset + localIdx;
|
|
32
36
|
var isFocused = globalIdx === selectedIndex;
|
|
33
37
|
return /*#__PURE__*/React.createElement(Text, {
|
|
34
38
|
key: item,
|
|
35
|
-
color: isFocused ? 'cyan' : undefined
|
|
39
|
+
color: isFocused ? 'cyan' : undefined,
|
|
40
|
+
dimColor: !isFocused
|
|
36
41
|
}, isFocused ? "\u203A ".concat(item) : " ".concat(item));
|
|
37
|
-
})
|
|
42
|
+
}), isLoading && /*#__PURE__*/React.createElement(Text, {
|
|
43
|
+
dimColor: true,
|
|
44
|
+
color: "gray"
|
|
45
|
+
}, ' [searching Docker Hub...]'));
|
|
38
46
|
}
|
|
39
47
|
SuggestionPanel.propTypes = {
|
|
40
48
|
items: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
41
49
|
selectedIndex: PropTypes.number.isRequired,
|
|
42
50
|
visibleOffset: PropTypes.number.isRequired,
|
|
43
|
-
maxVisible: PropTypes.number
|
|
51
|
+
maxVisible: PropTypes.number,
|
|
52
|
+
isLoading: PropTypes.bool
|
|
44
53
|
};
|
|
@@ -44,101 +44,140 @@ export var IMAGE_PROFILES = {
|
|
|
44
44
|
nginx: {
|
|
45
45
|
requiredEnv: [],
|
|
46
46
|
defaultPort: '80',
|
|
47
|
-
suggestedEnv: []
|
|
47
|
+
suggestedEnv: [],
|
|
48
|
+
defaultTag: '1.27-alpine'
|
|
48
49
|
},
|
|
49
50
|
httpd: {
|
|
50
51
|
requiredEnv: [],
|
|
51
52
|
defaultPort: '80',
|
|
52
|
-
suggestedEnv: []
|
|
53
|
+
suggestedEnv: [],
|
|
54
|
+
defaultTag: '2.4-alpine'
|
|
53
55
|
},
|
|
54
56
|
caddy: {
|
|
55
57
|
requiredEnv: [],
|
|
56
58
|
defaultPort: '80',
|
|
57
|
-
suggestedEnv: []
|
|
59
|
+
suggestedEnv: [],
|
|
60
|
+
defaultTag: '2-alpine'
|
|
58
61
|
},
|
|
59
62
|
node: {
|
|
60
63
|
requiredEnv: [],
|
|
61
64
|
defaultPort: '3000',
|
|
62
|
-
suggestedEnv: ['NODE_ENV=production']
|
|
65
|
+
suggestedEnv: ['NODE_ENV=production'],
|
|
66
|
+
defaultTag: '22-alpine'
|
|
63
67
|
},
|
|
64
68
|
python: {
|
|
65
69
|
requiredEnv: [],
|
|
66
70
|
defaultPort: '8000',
|
|
67
|
-
suggestedEnv: ['PYTHONUNBUFFERED=1']
|
|
71
|
+
suggestedEnv: ['PYTHONUNBUFFERED=1'],
|
|
72
|
+
defaultTag: '3.12-slim'
|
|
68
73
|
},
|
|
69
74
|
openjdk: {
|
|
70
75
|
requiredEnv: [],
|
|
71
76
|
defaultPort: '8080',
|
|
72
|
-
suggestedEnv: ['JAVA_OPTS=-Xms256m -Xmx512m']
|
|
77
|
+
suggestedEnv: ['JAVA_OPTS=-Xms256m -Xmx512m'],
|
|
78
|
+
defaultTag: '21-slim'
|
|
73
79
|
},
|
|
74
80
|
golang: {
|
|
75
81
|
requiredEnv: [],
|
|
76
82
|
defaultPort: '8080',
|
|
77
|
-
suggestedEnv: ['GOMAXPROCS=2']
|
|
83
|
+
suggestedEnv: ['GOMAXPROCS=2'],
|
|
84
|
+
defaultTag: '1.22-alpine'
|
|
78
85
|
},
|
|
79
86
|
redis: {
|
|
80
87
|
requiredEnv: [],
|
|
81
88
|
defaultPort: '6379',
|
|
82
|
-
suggestedEnv: ['REDIS_PASSWORD=change-me']
|
|
89
|
+
suggestedEnv: ['REDIS_PASSWORD=change-me'],
|
|
90
|
+
defaultTag: '7-alpine'
|
|
83
91
|
},
|
|
84
92
|
memcached: {
|
|
85
93
|
requiredEnv: [],
|
|
86
94
|
defaultPort: '11211',
|
|
87
|
-
suggestedEnv: []
|
|
95
|
+
suggestedEnv: [],
|
|
96
|
+
defaultTag: '1.6-alpine'
|
|
88
97
|
},
|
|
89
98
|
postgres: {
|
|
90
99
|
requiredEnv: ['POSTGRES_PASSWORD'],
|
|
91
100
|
defaultPort: '5432',
|
|
92
|
-
suggestedEnv: ['POSTGRES_USER=postgres', 'POSTGRES_DB=app']
|
|
101
|
+
suggestedEnv: ['POSTGRES_PASSWORD=secret', 'POSTGRES_USER=postgres', 'POSTGRES_DB=app'],
|
|
102
|
+
defaultTag: '17-alpine'
|
|
93
103
|
},
|
|
94
104
|
mysql: {
|
|
95
105
|
requiredEnv: ['MYSQL_ROOT_PASSWORD'],
|
|
96
106
|
defaultPort: '3306',
|
|
97
|
-
suggestedEnv: ['MYSQL_DATABASE=app', 'MYSQL_USER=app', 'MYSQL_PASSWORD=app123']
|
|
107
|
+
suggestedEnv: ['MYSQL_ROOT_PASSWORD=secret', 'MYSQL_DATABASE=app', 'MYSQL_USER=app', 'MYSQL_PASSWORD=app123'],
|
|
108
|
+
defaultTag: '8.0'
|
|
98
109
|
},
|
|
99
110
|
mariadb: {
|
|
100
111
|
requiredEnv: ['MARIADB_ROOT_PASSWORD'],
|
|
101
112
|
defaultPort: '3306',
|
|
102
|
-
suggestedEnv: ['MARIADB_DATABASE=app', 'MARIADB_USER=app', 'MARIADB_PASSWORD=app123']
|
|
113
|
+
suggestedEnv: ['MARIADB_ROOT_PASSWORD=secret', 'MARIADB_DATABASE=app', 'MARIADB_USER=app', 'MARIADB_PASSWORD=app123'],
|
|
114
|
+
defaultTag: '11-alpine'
|
|
103
115
|
},
|
|
104
116
|
mongo: {
|
|
105
117
|
requiredEnv: [],
|
|
106
118
|
defaultPort: '27017',
|
|
107
|
-
suggestedEnv: ['MONGO_INITDB_ROOT_USERNAME=admin', 'MONGO_INITDB_ROOT_PASSWORD=secret']
|
|
119
|
+
suggestedEnv: ['MONGO_INITDB_ROOT_USERNAME=admin', 'MONGO_INITDB_ROOT_PASSWORD=secret'],
|
|
120
|
+
defaultTag: '7.0'
|
|
108
121
|
},
|
|
109
122
|
mssql: {
|
|
110
123
|
requiredEnv: ['ACCEPT_EULA', 'SA_PASSWORD'],
|
|
111
124
|
defaultPort: '1433',
|
|
112
|
-
suggestedEnv: ['ACCEPT_EULA=Y', 'MSSQL_PID=Developer']
|
|
125
|
+
suggestedEnv: ['ACCEPT_EULA=Y', 'SA_PASSWORD=Strong!Passw0rd', 'MSSQL_PID=Developer'],
|
|
126
|
+
defaultTag: '2022-latest'
|
|
113
127
|
},
|
|
114
128
|
rabbitmq: {
|
|
115
129
|
requiredEnv: [],
|
|
116
130
|
defaultPort: '5672',
|
|
117
|
-
suggestedEnv: ['RABBITMQ_DEFAULT_USER=guest', 'RABBITMQ_DEFAULT_PASS=guest']
|
|
131
|
+
suggestedEnv: ['RABBITMQ_DEFAULT_USER=guest', 'RABBITMQ_DEFAULT_PASS=guest'],
|
|
132
|
+
defaultTag: '3-management-alpine'
|
|
118
133
|
},
|
|
119
134
|
kafka: {
|
|
120
135
|
requiredEnv: [],
|
|
121
136
|
defaultPort: '9092',
|
|
122
|
-
suggestedEnv: ['KAFKA_CFG_LISTENERS=PLAINTEXT://:9092']
|
|
137
|
+
suggestedEnv: ['KAFKA_CFG_LISTENERS=PLAINTEXT://:9092', 'KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092'],
|
|
138
|
+
defaultTag: '3.7'
|
|
123
139
|
},
|
|
124
140
|
zookeeper: {
|
|
125
141
|
requiredEnv: [],
|
|
126
142
|
defaultPort: '2181',
|
|
127
|
-
suggestedEnv: ['ALLOW_ANONYMOUS_LOGIN=yes']
|
|
143
|
+
suggestedEnv: ['ALLOW_ANONYMOUS_LOGIN=yes'],
|
|
144
|
+
defaultTag: '3.9'
|
|
128
145
|
},
|
|
129
146
|
elasticsearch: {
|
|
130
147
|
requiredEnv: [],
|
|
131
148
|
defaultPort: '9200',
|
|
132
|
-
suggestedEnv: ['discovery.type=single-node', 'xpack.security.enabled=false']
|
|
149
|
+
suggestedEnv: ['discovery.type=single-node', 'xpack.security.enabled=false'],
|
|
150
|
+
defaultTag: '8.13.0'
|
|
133
151
|
},
|
|
134
152
|
minio: {
|
|
135
153
|
requiredEnv: ['MINIO_ROOT_USER', 'MINIO_ROOT_PASSWORD'],
|
|
136
154
|
defaultPort: '9000',
|
|
137
|
-
suggestedEnv: ['MINIO_CONSOLE_ADDRESS=:9001']
|
|
155
|
+
suggestedEnv: ['MINIO_ROOT_USER=admin', 'MINIO_ROOT_PASSWORD=secret', 'MINIO_CONSOLE_ADDRESS=:9001'],
|
|
156
|
+
defaultTag: 'latest'
|
|
138
157
|
},
|
|
139
158
|
wordpress: {
|
|
140
159
|
requiredEnv: ['WORDPRESS_DB_HOST', 'WORDPRESS_DB_USER', 'WORDPRESS_DB_PASSWORD', 'WORDPRESS_DB_NAME'],
|
|
141
160
|
defaultPort: '8080',
|
|
142
|
-
suggestedEnv: ['WORDPRESS_TABLE_PREFIX=wp_']
|
|
161
|
+
suggestedEnv: ['WORDPRESS_DB_HOST=db', 'WORDPRESS_DB_USER=wp', 'WORDPRESS_DB_PASSWORD=secret', 'WORDPRESS_DB_NAME=wordpress', 'WORDPRESS_TABLE_PREFIX=wp_'],
|
|
162
|
+
defaultTag: '6.5-apache'
|
|
143
163
|
}
|
|
144
|
-
};
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Given an image name (with or without tag), returns the image name with a
|
|
168
|
+
* default tag appended when the image matches a known profile and has no
|
|
169
|
+
* explicit tag. Returns the input unchanged otherwise.
|
|
170
|
+
*
|
|
171
|
+
* @param {string} imageName - e.g. 'postgres', 'postgres:15', 'unknownimage'
|
|
172
|
+
* @param {Record<string, { defaultTag: string }>} profiles
|
|
173
|
+
* @returns {string}
|
|
174
|
+
*/
|
|
175
|
+
export function resolveImageTag(imageName) {
|
|
176
|
+
var profiles = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : IMAGE_PROFILES;
|
|
177
|
+
if (!imageName) return imageName;
|
|
178
|
+
if (imageName.includes(':')) return imageName;
|
|
179
|
+
var baseName = imageName.trim().toLowerCase().split('/').pop();
|
|
180
|
+
var profile = profiles[baseName];
|
|
181
|
+
if (!(profile !== null && profile !== void 0 && profile.defaultTag)) return imageName;
|
|
182
|
+
return "".concat(imageName, ":").concat(profile.defaultTag);
|
|
183
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
2
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
3
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
4
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
5
|
+
/**
|
|
6
|
+
* Docker Hub search helper.
|
|
7
|
+
* Pure function — no side effects beyond HTTP. All state management
|
|
8
|
+
* lives in the calling hook (useContainerCreation).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
var DOCKER_HUB_SEARCH_URL = 'https://hub.docker.com/v2/search/repositories/';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Format a Docker Hub search result object into a display string.
|
|
15
|
+
*
|
|
16
|
+
* Format rules:
|
|
17
|
+
* - Official: "name [★ official] [Npulls+]" (if pullCount > 0)
|
|
18
|
+
* - Non-official with pulls: "name [Npulls+]"
|
|
19
|
+
* - Otherwise: "name"
|
|
20
|
+
*
|
|
21
|
+
* Pull count suffixes: ≥1B → "1B+", ≥1M → "1M+", ≥1K → "1K+", else raw number
|
|
22
|
+
*
|
|
23
|
+
* @param {{ name: string, isOfficial: boolean, pullCount: number }} result
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
export function formatHubResult(result) {
|
|
27
|
+
var name = result.name,
|
|
28
|
+
isOfficial = result.isOfficial,
|
|
29
|
+
pullCount = result.pullCount;
|
|
30
|
+
var pullSuffix = '';
|
|
31
|
+
if (pullCount >= 1e9) {
|
|
32
|
+
pullSuffix = " [1B+ pulls]";
|
|
33
|
+
} else if (pullCount >= 1e6) {
|
|
34
|
+
pullSuffix = " [".concat(Math.floor(pullCount / 1e6), "M+ pulls]");
|
|
35
|
+
} else if (pullCount >= 1e3) {
|
|
36
|
+
pullSuffix = " [".concat(Math.floor(pullCount / 1e3), "K+ pulls]");
|
|
37
|
+
} else if (pullCount > 0) {
|
|
38
|
+
pullSuffix = " [".concat(pullCount, " pulls]");
|
|
39
|
+
}
|
|
40
|
+
if (isOfficial) {
|
|
41
|
+
return "".concat(name, " [\u2605 official]").concat(pullSuffix);
|
|
42
|
+
}
|
|
43
|
+
return "".concat(name).concat(pullSuffix);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Search Docker Hub for images matching the given query.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} query - Search term (will be trimmed and encoded)
|
|
50
|
+
* @param {Object} [options]
|
|
51
|
+
* @param {AbortSignal} [options.signal] - Abort signal; defaults to AbortSignal.timeout(5000)
|
|
52
|
+
* @returns {Promise<Array<{ name: string, isOfficial: boolean, pullCount: number, description: string }>>}
|
|
53
|
+
* @throws {Error} If response.ok is false or the network call rejects
|
|
54
|
+
*/
|
|
55
|
+
export function searchDockerHub(_x) {
|
|
56
|
+
return _searchDockerHub.apply(this, arguments);
|
|
57
|
+
}
|
|
58
|
+
function _searchDockerHub() {
|
|
59
|
+
_searchDockerHub = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(query) {
|
|
60
|
+
var _options$signal, _data$results;
|
|
61
|
+
var options,
|
|
62
|
+
q,
|
|
63
|
+
url,
|
|
64
|
+
signal,
|
|
65
|
+
response,
|
|
66
|
+
data,
|
|
67
|
+
results,
|
|
68
|
+
_args = arguments;
|
|
69
|
+
return _regenerator().w(function (_context) {
|
|
70
|
+
while (1) switch (_context.n) {
|
|
71
|
+
case 0:
|
|
72
|
+
options = _args.length > 1 && _args[1] !== undefined ? _args[1] : {};
|
|
73
|
+
q = query.trim();
|
|
74
|
+
if (q) {
|
|
75
|
+
_context.n = 1;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
return _context.a(2, []);
|
|
79
|
+
case 1:
|
|
80
|
+
url = "".concat(DOCKER_HUB_SEARCH_URL, "?query=").concat(encodeURIComponent(q), "&page_size=10");
|
|
81
|
+
signal = (_options$signal = options.signal) !== null && _options$signal !== void 0 ? _options$signal : AbortSignal.timeout(5000);
|
|
82
|
+
_context.n = 2;
|
|
83
|
+
return fetch(url, {
|
|
84
|
+
signal: signal
|
|
85
|
+
});
|
|
86
|
+
case 2:
|
|
87
|
+
response = _context.v;
|
|
88
|
+
if (response.ok) {
|
|
89
|
+
_context.n = 3;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
throw new Error("Docker Hub search failed: ".concat(response.status));
|
|
93
|
+
case 3:
|
|
94
|
+
_context.n = 4;
|
|
95
|
+
return response.json();
|
|
96
|
+
case 4:
|
|
97
|
+
data = _context.v;
|
|
98
|
+
results = (_data$results = data.results) !== null && _data$results !== void 0 ? _data$results : [];
|
|
99
|
+
return _context.a(2, results.map(function (item) {
|
|
100
|
+
return {
|
|
101
|
+
name: item.repo_name,
|
|
102
|
+
isOfficial: item.is_official,
|
|
103
|
+
pullCount: item.pull_count,
|
|
104
|
+
description: item.short_description
|
|
105
|
+
};
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
}, _callee);
|
|
109
|
+
}));
|
|
110
|
+
return _searchDockerHub.apply(this, arguments);
|
|
111
|
+
}
|
package/dist/helpers/logger.js
CHANGED
|
@@ -58,7 +58,7 @@ function log(level, message) {
|
|
|
58
58
|
timestamp: timestamp,
|
|
59
59
|
formatted: formatted
|
|
60
60
|
};
|
|
61
|
-
var shouldWriteToConsole = listeners.size === 0
|
|
61
|
+
var shouldWriteToConsole = listeners.size === 0;
|
|
62
62
|
if (shouldWriteToConsole) {
|
|
63
63
|
var method = level === 'debug' ? 'log' : level;
|
|
64
64
|
if (typeof console[method] === 'function') {
|
|
@@ -8,7 +8,7 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
|
|
|
8
8
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
9
9
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
10
10
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
11
|
-
import { useState } from 'react';
|
|
11
|
+
import { useState, useRef, useEffect } from 'react';
|
|
12
12
|
import { safeCall } from '../../helpers/safeCall.js';
|
|
13
13
|
import { startContainer as svcStartContainer, stopContainer as svcStopContainer, restartContainer as svcRestartContainer, removeContainer as svcRemoveContainer } from '../../helpers/dockerService/serviceComponents/containerActions.js';
|
|
14
14
|
|
|
@@ -32,6 +32,24 @@ export function useContainerActions(_ref) {
|
|
|
32
32
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
33
33
|
messageColor = _useState4[0],
|
|
34
34
|
setMessageColor = _useState4[1];
|
|
35
|
+
var messageTimerRef = useRef(null);
|
|
36
|
+
function setTimedMessage(msg) {
|
|
37
|
+
var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'yellow';
|
|
38
|
+
var ms = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 4000;
|
|
39
|
+
clearTimeout(messageTimerRef.current);
|
|
40
|
+
setMessage(msg);
|
|
41
|
+
if (color !== undefined) setMessageColor(color);
|
|
42
|
+
if (msg) {
|
|
43
|
+
messageTimerRef.current = setTimeout(function () {
|
|
44
|
+
return setMessage('');
|
|
45
|
+
}, ms);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
useEffect(function () {
|
|
49
|
+
return function () {
|
|
50
|
+
return clearTimeout(messageTimerRef.current);
|
|
51
|
+
};
|
|
52
|
+
}, []);
|
|
35
53
|
|
|
36
54
|
/**
|
|
37
55
|
* Handles a generic container action and sets feedback.
|
|
@@ -76,16 +94,14 @@ export function useContainerActions(_ref) {
|
|
|
76
94
|
_context5.n = 4;
|
|
77
95
|
return actionFn(container.id);
|
|
78
96
|
case 4:
|
|
79
|
-
|
|
80
|
-
setMessageColor('green');
|
|
97
|
+
setTimedMessage("".concat(actionLabel, " container successful."), 'green');
|
|
81
98
|
safeCall(onAction);
|
|
82
99
|
_context5.n = 6;
|
|
83
100
|
break;
|
|
84
101
|
case 5:
|
|
85
102
|
_context5.p = 5;
|
|
86
103
|
_t = _context5.v;
|
|
87
|
-
|
|
88
|
-
setMessageColor('red');
|
|
104
|
+
setTimedMessage("Failed to ".concat(actionLabel.toLowerCase(), " container: ").concat(_t.message), 'red');
|
|
89
105
|
case 6:
|
|
90
106
|
return _context5.a(2);
|
|
91
107
|
}
|
|
@@ -96,6 +112,7 @@ export function useContainerActions(_ref) {
|
|
|
96
112
|
return {
|
|
97
113
|
message: message,
|
|
98
114
|
setMessage: setMessage,
|
|
115
|
+
setTimedMessage: setTimedMessage,
|
|
99
116
|
messageColor: messageColor,
|
|
100
117
|
setMessageColor: setMessageColor,
|
|
101
118
|
handleAction: handleAction,
|