auto-dev-setup 0.1.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/LICENSE +21 -0
- package/README.md +191 -0
- package/bin/auto-setup.js +13 -0
- package/package.json +58 -0
- package/src/cli.js +295 -0
- package/src/config/extensions.js +51 -0
- package/src/index.js +16 -0
- package/src/platform/detector.js +240 -0
- package/src/platform/index.js +36 -0
- package/src/platform/linux.js +412 -0
- package/src/platform/macos.js +348 -0
- package/src/platform/windows.js +334 -0
- package/src/stacks/base.js +47 -0
- package/src/stacks/index.js +16 -0
- package/src/stacks/java.js +232 -0
- package/src/stacks/python.js +433 -0
- package/src/stacks/web.js +208 -0
- package/src/utils/executor.js +227 -0
- package/src/utils/index.js +18 -0
- package/src/utils/logger.js +235 -0
- package/src/utils/postinstall.js +295 -0
- package/src/utils/prompts.js +168 -0
- package/src/utils/verifier.js +204 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-Installation Setup
|
|
3
|
+
* Ensures all installed tools are properly configured and accessible
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { execCommand, execSilent, commandExists } = require('./executor');
|
|
7
|
+
const logger = require('./logger');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get all paths that should be in PATH for the tools to work
|
|
11
|
+
* @returns {Array} Array of paths to add
|
|
12
|
+
*/
|
|
13
|
+
function getRequiredPaths() {
|
|
14
|
+
const platform = process.platform;
|
|
15
|
+
const paths = [];
|
|
16
|
+
|
|
17
|
+
if (platform === 'win32') {
|
|
18
|
+
// Only check Python paths if Python is installed
|
|
19
|
+
if (commandExists('python') || commandExists('python3')) {
|
|
20
|
+
// Python user scripts - use sysconfig for correct versioned path
|
|
21
|
+
const pythonScripts = execSilent(
|
|
22
|
+
'python -c "import sysconfig; print(sysconfig.get_path(\'scripts\', \'nt_user\'))"'
|
|
23
|
+
);
|
|
24
|
+
if (pythonScripts.success && pythonScripts.output) {
|
|
25
|
+
const scriptsPath = pythonScripts.output.trim();
|
|
26
|
+
if (scriptsPath && scriptsPath !== 'None') {
|
|
27
|
+
paths.push({
|
|
28
|
+
path: scriptsPath,
|
|
29
|
+
name: 'Python Scripts',
|
|
30
|
+
tools: ['jupyter', 'pipx', 'virtualenv']
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// npm global on Windows - only if npm is installed
|
|
37
|
+
if (commandExists('npm')) {
|
|
38
|
+
const npmPrefix = execSilent('npm config get prefix');
|
|
39
|
+
if (npmPrefix.success && npmPrefix.output) {
|
|
40
|
+
const prefix = npmPrefix.output.trim();
|
|
41
|
+
// On Windows, npm global binaries are in the prefix folder directly
|
|
42
|
+
paths.push({
|
|
43
|
+
path: prefix,
|
|
44
|
+
name: 'npm global',
|
|
45
|
+
tools: ['npm global packages']
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Also check for AppData npm location (common on Windows)
|
|
51
|
+
const appData = process.env.APPDATA;
|
|
52
|
+
if (appData) {
|
|
53
|
+
const npmPath = `${appData}\\npm`;
|
|
54
|
+
paths.push({
|
|
55
|
+
path: npmPath,
|
|
56
|
+
name: 'npm AppData',
|
|
57
|
+
tools: ['npm global packages']
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
// Only check Python paths if Python is installed
|
|
62
|
+
if (commandExists('python3') || commandExists('python')) {
|
|
63
|
+
// Unix: Python user bin - use sysconfig for correct path
|
|
64
|
+
const pythonUserBin = execSilent(
|
|
65
|
+
'python3 -c "import sysconfig; print(sysconfig.get_path(\'scripts\', scheme=\'posix_user\'))" 2>/dev/null || python -c "import sysconfig; print(sysconfig.get_path(\'scripts\', scheme=\'posix_user\'))"'
|
|
66
|
+
);
|
|
67
|
+
if (pythonUserBin.success && pythonUserBin.output) {
|
|
68
|
+
const binPath = pythonUserBin.output.trim();
|
|
69
|
+
if (binPath && binPath !== 'None') {
|
|
70
|
+
paths.push({
|
|
71
|
+
path: binPath,
|
|
72
|
+
name: 'Python user bin',
|
|
73
|
+
tools: ['jupyter', 'pipx', 'virtualenv']
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Fallback: try site --user-base
|
|
79
|
+
if (!pythonUserBin.success) {
|
|
80
|
+
const pythonUserBase = execSilent('python3 -m site --user-base 2>/dev/null || python -m site --user-base');
|
|
81
|
+
if (pythonUserBase.success && pythonUserBase.output) {
|
|
82
|
+
paths.push({
|
|
83
|
+
path: `${pythonUserBase.output.trim()}/bin`,
|
|
84
|
+
name: 'Python user bin',
|
|
85
|
+
tools: ['jupyter', 'pipx', 'virtualenv']
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// npm global (if npm is installed and configured to user space)
|
|
92
|
+
if (commandExists('npm')) {
|
|
93
|
+
const npmPrefix = execSilent('npm config get prefix');
|
|
94
|
+
if (npmPrefix.success && npmPrefix.output) {
|
|
95
|
+
const prefix = npmPrefix.output.trim();
|
|
96
|
+
// Only add if it's in user space (not /usr/local or /usr)
|
|
97
|
+
if (prefix.includes(process.env.HOME) || prefix.includes('.npm-global')) {
|
|
98
|
+
paths.push({
|
|
99
|
+
path: `${prefix}/bin`,
|
|
100
|
+
name: 'npm global',
|
|
101
|
+
tools: ['npm global packages']
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// pipx bin directory - always check this on Unix
|
|
108
|
+
const pipxBin = `${process.env.HOME}/.local/bin`;
|
|
109
|
+
paths.push({
|
|
110
|
+
path: pipxBin,
|
|
111
|
+
name: 'pipx/local bin',
|
|
112
|
+
tools: ['pipx installed tools']
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Homebrew on Apple Silicon
|
|
116
|
+
if (platform === 'darwin' && process.arch === 'arm64') {
|
|
117
|
+
paths.push({
|
|
118
|
+
path: '/opt/homebrew/bin',
|
|
119
|
+
name: 'Homebrew',
|
|
120
|
+
tools: ['brew packages']
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Homebrew on Intel Mac
|
|
125
|
+
if (platform === 'darwin' && process.arch === 'x64') {
|
|
126
|
+
paths.push({
|
|
127
|
+
path: '/usr/local/bin',
|
|
128
|
+
name: 'Homebrew',
|
|
129
|
+
tools: ['brew packages']
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return paths;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Check which required paths are missing from current PATH
|
|
139
|
+
* @returns {Array} Missing paths
|
|
140
|
+
*/
|
|
141
|
+
function getMissingPaths() {
|
|
142
|
+
const requiredPaths = getRequiredPaths();
|
|
143
|
+
const currentPath = process.env.PATH || '';
|
|
144
|
+
const missing = [];
|
|
145
|
+
|
|
146
|
+
for (const pathInfo of requiredPaths) {
|
|
147
|
+
if (!currentPath.toLowerCase().includes(pathInfo.path.toLowerCase())) {
|
|
148
|
+
missing.push(pathInfo);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return missing;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Add missing paths to user's shell configuration (Unix) or environment (Windows)
|
|
157
|
+
* @returns {Object} Result with paths added
|
|
158
|
+
*/
|
|
159
|
+
async function ensurePathsConfigured() {
|
|
160
|
+
const platform = process.platform;
|
|
161
|
+
const requiredPaths = getRequiredPaths();
|
|
162
|
+
const added = [];
|
|
163
|
+
|
|
164
|
+
if (platform === 'win32') {
|
|
165
|
+
// Windows: Add to user PATH environment variable
|
|
166
|
+
for (const pathInfo of requiredPaths) {
|
|
167
|
+
const checkResult = execSilent(
|
|
168
|
+
`powershell -Command "$p = [Environment]::GetEnvironmentVariable('Path', 'User'); $p -like '*${pathInfo.path.replace(/\\/g, '\\\\')}*'"`
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
if (!checkResult.output?.includes('True')) {
|
|
172
|
+
const addResult = execCommand(
|
|
173
|
+
`powershell -Command "$p = [Environment]::GetEnvironmentVariable('Path', 'User'); [Environment]::SetEnvironmentVariable('Path', \\"$p;${pathInfo.path}\\", 'User')"`,
|
|
174
|
+
{ silent: true, showCommand: false }
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (addResult.success) {
|
|
178
|
+
added.push(pathInfo);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
// Unix: Add to shell config
|
|
184
|
+
const shellConfig = process.env.SHELL?.includes('zsh')
|
|
185
|
+
? `${process.env.HOME}/.zshrc`
|
|
186
|
+
: `${process.env.HOME}/.bashrc`;
|
|
187
|
+
|
|
188
|
+
const fs = require('fs');
|
|
189
|
+
|
|
190
|
+
for (const pathInfo of requiredPaths) {
|
|
191
|
+
// Check if already in config
|
|
192
|
+
const checkResult = execSilent(`grep -q '${pathInfo.path}' "${shellConfig}" 2>/dev/null`);
|
|
193
|
+
|
|
194
|
+
if (!checkResult.success) {
|
|
195
|
+
try {
|
|
196
|
+
const exportLine = `\n# ${pathInfo.name} (added by auto-dev-setup)\nexport PATH="${pathInfo.path}:$PATH"\n`;
|
|
197
|
+
fs.appendFileSync(shellConfig, exportLine);
|
|
198
|
+
added.push(pathInfo);
|
|
199
|
+
} catch (err) {
|
|
200
|
+
// Ignore errors
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return { added };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Verify all installed tools are accessible
|
|
211
|
+
* @returns {Object} Verification results
|
|
212
|
+
*/
|
|
213
|
+
function verifyToolsAccessible() {
|
|
214
|
+
const tools = [
|
|
215
|
+
{ cmd: 'git', name: 'Git', required: true },
|
|
216
|
+
{ cmd: 'node', name: 'Node.js', required: true },
|
|
217
|
+
{ cmd: 'npm', name: 'npm', required: true },
|
|
218
|
+
{ cmd: 'python', name: 'Python', altCmd: 'python3', required: false },
|
|
219
|
+
{ cmd: 'pip', name: 'pip', altCmd: 'pip3', required: false },
|
|
220
|
+
{ cmd: 'pipx', name: 'pipx', required: false },
|
|
221
|
+
{ cmd: 'virtualenv', name: 'virtualenv', required: false },
|
|
222
|
+
{ cmd: 'jupyter', name: 'Jupyter', required: false },
|
|
223
|
+
{ cmd: 'java', name: 'Java', required: false },
|
|
224
|
+
{ cmd: 'code', name: 'VS Code', required: false }
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
const results = {
|
|
228
|
+
accessible: [],
|
|
229
|
+
notAccessible: [],
|
|
230
|
+
notInstalled: []
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
for (const tool of tools) {
|
|
234
|
+
if (commandExists(tool.cmd) || (tool.altCmd && commandExists(tool.altCmd))) {
|
|
235
|
+
results.accessible.push(tool.name);
|
|
236
|
+
} else {
|
|
237
|
+
results.notAccessible.push(tool.name);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return results;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Display post-installation instructions
|
|
246
|
+
* @param {Object} options - Display options
|
|
247
|
+
*/
|
|
248
|
+
function displayPostInstallInstructions(options = {}) {
|
|
249
|
+
const platform = process.platform;
|
|
250
|
+
const missingPaths = getMissingPaths();
|
|
251
|
+
|
|
252
|
+
if (missingPaths.length === 0 && !options.forceShow) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
logger.newline();
|
|
257
|
+
logger.warn('Action Required: Restart your terminal');
|
|
258
|
+
logger.newline();
|
|
259
|
+
|
|
260
|
+
if (platform === 'win32') {
|
|
261
|
+
logger.info('To use all installed tools, please:');
|
|
262
|
+
logger.listItem('Close this PowerShell/Terminal window completely');
|
|
263
|
+
logger.listItem('Open a new PowerShell/Terminal window');
|
|
264
|
+
logger.newline();
|
|
265
|
+
logger.info('Then verify with:');
|
|
266
|
+
logger.command('jupyter --version');
|
|
267
|
+
logger.command('pipx --version');
|
|
268
|
+
logger.command('java --version');
|
|
269
|
+
} else if (platform === 'darwin') {
|
|
270
|
+
logger.info('To use all installed tools, either:');
|
|
271
|
+
logger.listItem('Restart your terminal, OR');
|
|
272
|
+
logger.listItem('Run: source ~/.zshrc');
|
|
273
|
+
logger.newline();
|
|
274
|
+
logger.info('Then verify with:');
|
|
275
|
+
logger.command('jupyter --version');
|
|
276
|
+
logger.command('pipx --version');
|
|
277
|
+
} else {
|
|
278
|
+
logger.info('To use all installed tools, either:');
|
|
279
|
+
logger.listItem('Restart your terminal, OR');
|
|
280
|
+
const shellConfig = process.env.SHELL?.includes('zsh') ? '~/.zshrc' : '~/.bashrc';
|
|
281
|
+
logger.listItem(`Run: source ${shellConfig}`);
|
|
282
|
+
logger.newline();
|
|
283
|
+
logger.info('Then verify with:');
|
|
284
|
+
logger.command('jupyter --version');
|
|
285
|
+
logger.command('pipx --version');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
module.exports = {
|
|
290
|
+
getRequiredPaths,
|
|
291
|
+
getMissingPaths,
|
|
292
|
+
ensurePathsConfigured,
|
|
293
|
+
verifyToolsAccessible,
|
|
294
|
+
displayPostInstallInstructions
|
|
295
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Prompt Utility
|
|
3
|
+
* Handles interactive prompts and user input
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
|
+
const logger = require('./logger');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Ask a yes/no question
|
|
11
|
+
* @param {string} message - Question to ask
|
|
12
|
+
* @param {boolean} defaultValue - Default value
|
|
13
|
+
* @returns {Promise<boolean>} User's answer
|
|
14
|
+
*/
|
|
15
|
+
async function confirm(message, defaultValue = true) {
|
|
16
|
+
const { answer } = await inquirer.prompt([
|
|
17
|
+
{
|
|
18
|
+
type: 'confirm',
|
|
19
|
+
name: 'answer',
|
|
20
|
+
message,
|
|
21
|
+
default: defaultValue
|
|
22
|
+
}
|
|
23
|
+
]);
|
|
24
|
+
return answer;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Ask a multiple choice question
|
|
29
|
+
* @param {string} message - Question to ask
|
|
30
|
+
* @param {Array} choices - Array of choices
|
|
31
|
+
* @returns {Promise<string>} Selected choice
|
|
32
|
+
*/
|
|
33
|
+
async function select(message, choices) {
|
|
34
|
+
const { answer } = await inquirer.prompt([
|
|
35
|
+
{
|
|
36
|
+
type: 'list',
|
|
37
|
+
name: 'answer',
|
|
38
|
+
message,
|
|
39
|
+
choices
|
|
40
|
+
}
|
|
41
|
+
]);
|
|
42
|
+
return answer;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Ask for text input
|
|
47
|
+
* @param {string} message - Question to ask
|
|
48
|
+
* @param {string} defaultValue - Default value
|
|
49
|
+
* @returns {Promise<string>} User's input
|
|
50
|
+
*/
|
|
51
|
+
async function input(message, defaultValue = '') {
|
|
52
|
+
const { answer } = await inquirer.prompt([
|
|
53
|
+
{
|
|
54
|
+
type: 'input',
|
|
55
|
+
name: 'answer',
|
|
56
|
+
message,
|
|
57
|
+
default: defaultValue
|
|
58
|
+
}
|
|
59
|
+
]);
|
|
60
|
+
return answer;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Ask for multiple selections
|
|
65
|
+
* @param {string} message - Question to ask
|
|
66
|
+
* @param {Array} choices - Array of choices
|
|
67
|
+
* @returns {Promise<Array>} Selected choices
|
|
68
|
+
*/
|
|
69
|
+
async function multiSelect(message, choices) {
|
|
70
|
+
const { answer } = await inquirer.prompt([
|
|
71
|
+
{
|
|
72
|
+
type: 'checkbox',
|
|
73
|
+
name: 'answer',
|
|
74
|
+
message,
|
|
75
|
+
choices
|
|
76
|
+
}
|
|
77
|
+
]);
|
|
78
|
+
return answer;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Ask for password input
|
|
83
|
+
* @param {string} message - Question to ask
|
|
84
|
+
* @returns {Promise<string>} User's password
|
|
85
|
+
*/
|
|
86
|
+
async function password(message) {
|
|
87
|
+
const { answer } = await inquirer.prompt([
|
|
88
|
+
{
|
|
89
|
+
type: 'password',
|
|
90
|
+
name: 'answer',
|
|
91
|
+
message,
|
|
92
|
+
mask: '*'
|
|
93
|
+
}
|
|
94
|
+
]);
|
|
95
|
+
return answer;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Prompt for Python stack installation
|
|
100
|
+
* @returns {Promise<boolean>}
|
|
101
|
+
*/
|
|
102
|
+
async function promptPythonStack() {
|
|
103
|
+
logger.newline();
|
|
104
|
+
return confirm(
|
|
105
|
+
'Do you want to install the full Python development environment?',
|
|
106
|
+
true
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Prompt for Java stack installation
|
|
112
|
+
* @returns {Promise<boolean>}
|
|
113
|
+
*/
|
|
114
|
+
async function promptJavaStack() {
|
|
115
|
+
logger.newline();
|
|
116
|
+
return confirm(
|
|
117
|
+
'Do you want to install the Java development environment?',
|
|
118
|
+
true
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Prompt to continue after warning
|
|
124
|
+
* @param {string} warning - Warning message
|
|
125
|
+
* @returns {Promise<boolean>}
|
|
126
|
+
*/
|
|
127
|
+
async function continueAfterWarning(warning) {
|
|
128
|
+
logger.warn(warning);
|
|
129
|
+
return confirm('Do you want to continue anyway?', false);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Prompt for admin/sudo action
|
|
134
|
+
* @param {string} action - Action description
|
|
135
|
+
* @returns {Promise<boolean>}
|
|
136
|
+
*/
|
|
137
|
+
async function promptForElevation(action) {
|
|
138
|
+
logger.newline();
|
|
139
|
+
logger.warn(`The following action requires administrator privileges:`);
|
|
140
|
+
logger.info(action);
|
|
141
|
+
return confirm('Do you want to proceed?', true);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Press enter to continue
|
|
146
|
+
*/
|
|
147
|
+
async function pressEnterToContinue() {
|
|
148
|
+
await inquirer.prompt([
|
|
149
|
+
{
|
|
150
|
+
type: 'input',
|
|
151
|
+
name: 'continue',
|
|
152
|
+
message: 'Press Enter to continue...'
|
|
153
|
+
}
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = {
|
|
158
|
+
confirm,
|
|
159
|
+
select,
|
|
160
|
+
input,
|
|
161
|
+
multiSelect,
|
|
162
|
+
password,
|
|
163
|
+
promptPythonStack,
|
|
164
|
+
promptJavaStack,
|
|
165
|
+
continueAfterWarning,
|
|
166
|
+
promptForElevation,
|
|
167
|
+
pressEnterToContinue
|
|
168
|
+
};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifier Utility
|
|
3
|
+
* Verifies installations and checks tool availability
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { commandExists, getVersion, execSilent } = require('./executor');
|
|
7
|
+
const logger = require('./logger');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Verify a tool is installed
|
|
11
|
+
* @param {string} command - Command to check
|
|
12
|
+
* @param {string} name - Display name
|
|
13
|
+
* @param {string} versionFlag - Version flag
|
|
14
|
+
* @returns {Object} Verification result
|
|
15
|
+
*/
|
|
16
|
+
function verifyTool(command, name, versionFlag = '--version') {
|
|
17
|
+
const exists = commandExists(command);
|
|
18
|
+
const version = exists ? getVersion(command, versionFlag) : null;
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
name,
|
|
22
|
+
command,
|
|
23
|
+
installed: exists,
|
|
24
|
+
version
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Verify multiple tools
|
|
30
|
+
* @param {Array} tools - Array of tool objects {command, name, versionFlag}
|
|
31
|
+
* @returns {Array} Verification results
|
|
32
|
+
*/
|
|
33
|
+
function verifyTools(tools) {
|
|
34
|
+
return tools.map((tool) =>
|
|
35
|
+
verifyTool(tool.command, tool.name, tool.versionFlag || '--version')
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Display verification results
|
|
41
|
+
* @param {Array} results - Verification results
|
|
42
|
+
*/
|
|
43
|
+
function displayVerification(results) {
|
|
44
|
+
logger.subsection('Verification Results:');
|
|
45
|
+
|
|
46
|
+
for (const result of results) {
|
|
47
|
+
if (result.installed) {
|
|
48
|
+
logger.success(`${result.name}: ${result.version || 'installed'}`);
|
|
49
|
+
} else {
|
|
50
|
+
logger.error(`${result.name}: not installed`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Verify base tools (git, curl/wget)
|
|
57
|
+
* @returns {Object} Verification results
|
|
58
|
+
*/
|
|
59
|
+
function verifyBaseTools() {
|
|
60
|
+
const tools = [
|
|
61
|
+
{ command: 'git', name: 'Git' },
|
|
62
|
+
{ command: 'curl', name: 'curl' }
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
return verifyTools(tools);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Verify web stack (Node, npm, VS Code)
|
|
70
|
+
* @returns {Object} Verification results
|
|
71
|
+
*/
|
|
72
|
+
function verifyWebStack() {
|
|
73
|
+
const tools = [
|
|
74
|
+
{ command: 'node', name: 'Node.js', versionFlag: '-v' },
|
|
75
|
+
{ command: 'npm', name: 'npm', versionFlag: '-v' },
|
|
76
|
+
{ command: 'git', name: 'Git' },
|
|
77
|
+
{ command: 'code', name: 'VS Code' }
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
return verifyTools(tools);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Verify Python stack
|
|
85
|
+
* @returns {Object} Verification results
|
|
86
|
+
*/
|
|
87
|
+
function verifyPythonStack() {
|
|
88
|
+
const platform = process.platform;
|
|
89
|
+
const pythonCmd = platform === 'win32' ? 'python' : 'python3';
|
|
90
|
+
const pipCmd = platform === 'win32' ? 'pip' : 'pip3';
|
|
91
|
+
|
|
92
|
+
const tools = [
|
|
93
|
+
{ command: pythonCmd, name: 'Python' },
|
|
94
|
+
{ command: pipCmd, name: 'pip' },
|
|
95
|
+
{ command: 'pipx', name: 'pipx' },
|
|
96
|
+
{ command: 'virtualenv', name: 'virtualenv' },
|
|
97
|
+
{ command: 'jupyter', name: 'Jupyter' }
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
return verifyTools(tools);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Verify Java stack
|
|
105
|
+
* @returns {Object} Verification results
|
|
106
|
+
*/
|
|
107
|
+
function verifyJavaStack() {
|
|
108
|
+
const tools = [{ command: 'java', name: 'Java' }];
|
|
109
|
+
|
|
110
|
+
const results = verifyTools(tools);
|
|
111
|
+
|
|
112
|
+
// Check JAVA_HOME
|
|
113
|
+
const javaHome = process.env.JAVA_HOME;
|
|
114
|
+
results.push({
|
|
115
|
+
name: 'JAVA_HOME',
|
|
116
|
+
command: 'JAVA_HOME',
|
|
117
|
+
installed: !!javaHome,
|
|
118
|
+
version: javaHome || 'not set'
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return results;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Verify VS Code extensions
|
|
126
|
+
* @param {Array} extensions - Extension IDs to check
|
|
127
|
+
* @returns {Array} Installed extensions
|
|
128
|
+
*/
|
|
129
|
+
function verifyVSCodeExtensions(extensions) {
|
|
130
|
+
const result = execSilent('code --list-extensions');
|
|
131
|
+
|
|
132
|
+
if (!result.success) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const installedExtensions = result.output.toLowerCase().split('\n');
|
|
137
|
+
|
|
138
|
+
return extensions.map((ext) => ({
|
|
139
|
+
id: ext,
|
|
140
|
+
installed: installedExtensions.includes(ext.toLowerCase())
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Check if Homebrew is installed (macOS)
|
|
146
|
+
* @returns {boolean}
|
|
147
|
+
*/
|
|
148
|
+
function isHomebrewInstalled() {
|
|
149
|
+
return commandExists('brew');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Check if Xcode CLI tools are installed (macOS)
|
|
154
|
+
* @returns {boolean}
|
|
155
|
+
*/
|
|
156
|
+
function isXcodeCliInstalled() {
|
|
157
|
+
const result = execSilent('xcode-select -p');
|
|
158
|
+
return result.success;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check if winget is available (Windows)
|
|
163
|
+
* @returns {boolean}
|
|
164
|
+
*/
|
|
165
|
+
function isWingetAvailable() {
|
|
166
|
+
return commandExists('winget');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Check if chocolatey is available (Windows)
|
|
171
|
+
* @returns {boolean}
|
|
172
|
+
*/
|
|
173
|
+
function isChocolateyAvailable() {
|
|
174
|
+
return commandExists('choco');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Run all verifications and return summary
|
|
179
|
+
* @returns {Object} Complete verification summary
|
|
180
|
+
*/
|
|
181
|
+
function runAllVerifications() {
|
|
182
|
+
return {
|
|
183
|
+
base: verifyBaseTools(),
|
|
184
|
+
web: verifyWebStack(),
|
|
185
|
+
python: verifyPythonStack(),
|
|
186
|
+
java: verifyJavaStack()
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = {
|
|
191
|
+
verifyTool,
|
|
192
|
+
verifyTools,
|
|
193
|
+
displayVerification,
|
|
194
|
+
verifyBaseTools,
|
|
195
|
+
verifyWebStack,
|
|
196
|
+
verifyPythonStack,
|
|
197
|
+
verifyJavaStack,
|
|
198
|
+
verifyVSCodeExtensions,
|
|
199
|
+
isHomebrewInstalled,
|
|
200
|
+
isXcodeCliInstalled,
|
|
201
|
+
isWingetAvailable,
|
|
202
|
+
isChocolateyAvailable,
|
|
203
|
+
runAllVerifications
|
|
204
|
+
};
|