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,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* macOS Platform Installer
|
|
3
|
+
* Handles macOS-specific installations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { execCommand, execSilent, commandExists } = require('../utils/executor');
|
|
7
|
+
const logger = require('../utils/logger');
|
|
8
|
+
const ora = require('ora');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Install Xcode Command Line Tools
|
|
12
|
+
* @returns {Object} Installation result
|
|
13
|
+
*/
|
|
14
|
+
async function installXcodeCliTools() {
|
|
15
|
+
logger.step('Checking Xcode Command Line Tools...');
|
|
16
|
+
|
|
17
|
+
// Check if already installed
|
|
18
|
+
const check = execSilent('xcode-select -p');
|
|
19
|
+
if (check.success) {
|
|
20
|
+
logger.success('Xcode Command Line Tools already installed');
|
|
21
|
+
return { success: true, skipped: true };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
logger.step('Installing Xcode Command Line Tools...');
|
|
25
|
+
logger.info('This may take several minutes and require user interaction.');
|
|
26
|
+
|
|
27
|
+
const result = execCommand('xcode-select --install', { silent: false });
|
|
28
|
+
|
|
29
|
+
if (result.success) {
|
|
30
|
+
logger.success('Xcode Command Line Tools installed');
|
|
31
|
+
return { success: true, skipped: false };
|
|
32
|
+
} else {
|
|
33
|
+
logger.warn('Xcode CLI Tools installation started. Please complete the installation dialog.');
|
|
34
|
+
logger.info('Run this tool again after installation completes.');
|
|
35
|
+
return { success: false, skipped: false, pending: true };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Install Homebrew
|
|
41
|
+
* @returns {Object} Installation result
|
|
42
|
+
*/
|
|
43
|
+
async function installHomebrew() {
|
|
44
|
+
logger.step('Checking Homebrew...');
|
|
45
|
+
|
|
46
|
+
if (commandExists('brew')) {
|
|
47
|
+
logger.success('Homebrew already installed');
|
|
48
|
+
|
|
49
|
+
// Update Homebrew
|
|
50
|
+
logger.step('Updating Homebrew...');
|
|
51
|
+
execCommand('brew update', { silent: true });
|
|
52
|
+
return { success: true, skipped: true };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
logger.step('Installing Homebrew...');
|
|
56
|
+
const spinner = ora('Downloading and installing Homebrew...').start();
|
|
57
|
+
|
|
58
|
+
const installScript =
|
|
59
|
+
'/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"';
|
|
60
|
+
|
|
61
|
+
const result = execCommand(installScript);
|
|
62
|
+
|
|
63
|
+
if (result.success) {
|
|
64
|
+
spinner.succeed('Homebrew installed successfully');
|
|
65
|
+
|
|
66
|
+
// Add to PATH for Apple Silicon Macs
|
|
67
|
+
const arch = process.arch;
|
|
68
|
+
if (arch === 'arm64') {
|
|
69
|
+
logger.info('Configuring Homebrew for Apple Silicon...');
|
|
70
|
+
const shellConfig = process.env.SHELL?.includes('zsh')
|
|
71
|
+
? '~/.zshrc'
|
|
72
|
+
: '~/.bash_profile';
|
|
73
|
+
execCommand(
|
|
74
|
+
`echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ${shellConfig}`
|
|
75
|
+
);
|
|
76
|
+
execCommand('eval "$(/opt/homebrew/bin/brew shellenv)"');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { success: true, skipped: false };
|
|
80
|
+
} else {
|
|
81
|
+
spinner.fail('Failed to install Homebrew');
|
|
82
|
+
return { success: false, skipped: false };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Install a package using Homebrew
|
|
88
|
+
* @param {string} packageName - Package to install
|
|
89
|
+
* @param {string} caskName - Cask name if it's a cask (optional)
|
|
90
|
+
* @returns {Object} Installation result
|
|
91
|
+
*/
|
|
92
|
+
async function brewInstall(packageName, caskName = null) {
|
|
93
|
+
const isCask = caskName !== null;
|
|
94
|
+
const name = caskName || packageName;
|
|
95
|
+
|
|
96
|
+
logger.step(`Checking ${name}...`);
|
|
97
|
+
|
|
98
|
+
// Check if already installed
|
|
99
|
+
if (isCask) {
|
|
100
|
+
const checkResult = execSilent(`brew list --cask ${name} 2>/dev/null`);
|
|
101
|
+
if (checkResult.success) {
|
|
102
|
+
logger.success(`${name} already installed`);
|
|
103
|
+
return { success: true, skipped: true };
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
if (commandExists(packageName)) {
|
|
107
|
+
logger.success(`${name} already installed`);
|
|
108
|
+
return { success: true, skipped: true };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
logger.step(`Installing ${name}...`);
|
|
113
|
+
|
|
114
|
+
const command = isCask
|
|
115
|
+
? `brew install --cask ${name}`
|
|
116
|
+
: `brew install ${packageName}`;
|
|
117
|
+
|
|
118
|
+
const result = execCommand(command);
|
|
119
|
+
|
|
120
|
+
if (result.success) {
|
|
121
|
+
logger.success(`${name} installed successfully`);
|
|
122
|
+
return { success: true, skipped: false };
|
|
123
|
+
} else {
|
|
124
|
+
logger.error(`Failed to install ${name}`);
|
|
125
|
+
return { success: false, skipped: false };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Install Git via Homebrew
|
|
131
|
+
*/
|
|
132
|
+
async function installGit() {
|
|
133
|
+
return brewInstall('git');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Install curl via Homebrew
|
|
138
|
+
*/
|
|
139
|
+
async function installCurl() {
|
|
140
|
+
return brewInstall('curl');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Install Node.js via Homebrew
|
|
145
|
+
*/
|
|
146
|
+
async function installNode() {
|
|
147
|
+
return brewInstall('node');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Install Visual Studio Code via Homebrew Cask
|
|
152
|
+
*/
|
|
153
|
+
async function installVSCode() {
|
|
154
|
+
return brewInstall('visual-studio-code', 'visual-studio-code');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Enable VS Code command line tool
|
|
159
|
+
*/
|
|
160
|
+
async function enableVSCodeCli() {
|
|
161
|
+
logger.step('Enabling VS Code CLI (code command)...');
|
|
162
|
+
|
|
163
|
+
// On macOS, VS Code CLI should be available after cask install
|
|
164
|
+
// But we can manually add it to PATH if needed
|
|
165
|
+
if (commandExists('code')) {
|
|
166
|
+
logger.success('VS Code CLI already available');
|
|
167
|
+
return { success: true, skipped: true };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Try to create symlink manually
|
|
171
|
+
const vscodePath =
|
|
172
|
+
'/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code';
|
|
173
|
+
const result = execCommand(
|
|
174
|
+
`ln -sf "${vscodePath}" /usr/local/bin/code 2>/dev/null || sudo ln -sf "${vscodePath}" /usr/local/bin/code`
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (result.success || commandExists('code')) {
|
|
178
|
+
logger.success('VS Code CLI enabled');
|
|
179
|
+
return { success: true, skipped: false };
|
|
180
|
+
} else {
|
|
181
|
+
logger.warn('Could not enable VS Code CLI automatically');
|
|
182
|
+
logger.info(
|
|
183
|
+
'You can enable it manually: Open VS Code > Cmd+Shift+P > "Shell Command: Install code"'
|
|
184
|
+
);
|
|
185
|
+
return { success: false, skipped: false };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Install Python via Homebrew
|
|
191
|
+
*/
|
|
192
|
+
async function installPython() {
|
|
193
|
+
// Python is a formula, not a cask
|
|
194
|
+
logger.step('Checking Python...');
|
|
195
|
+
|
|
196
|
+
// Check if python3 is already available
|
|
197
|
+
if (commandExists('python3')) {
|
|
198
|
+
logger.success('Python already installed');
|
|
199
|
+
return { success: true, skipped: true };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
logger.step('Installing Python...');
|
|
203
|
+
const result = execCommand('brew install python');
|
|
204
|
+
|
|
205
|
+
if (result.success) {
|
|
206
|
+
logger.success('Python installed successfully');
|
|
207
|
+
return { success: true, skipped: false };
|
|
208
|
+
} else {
|
|
209
|
+
logger.error('Failed to install Python');
|
|
210
|
+
return { success: false, skipped: false };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Install Java (OpenJDK) via Homebrew
|
|
216
|
+
*/
|
|
217
|
+
async function installJava() {
|
|
218
|
+
// OpenJDK is a formula, not a cask
|
|
219
|
+
logger.step('Checking Java...');
|
|
220
|
+
|
|
221
|
+
// Check if java is already available
|
|
222
|
+
if (commandExists('java')) {
|
|
223
|
+
logger.success('Java already installed');
|
|
224
|
+
|
|
225
|
+
// Still configure JAVA_HOME if not set
|
|
226
|
+
if (!process.env.JAVA_HOME) {
|
|
227
|
+
await configureJavaHome();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return { success: true, skipped: true };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
logger.step('Installing OpenJDK...');
|
|
234
|
+
const result = execCommand('brew install openjdk');
|
|
235
|
+
|
|
236
|
+
if (result.success) {
|
|
237
|
+
logger.success('OpenJDK installed successfully');
|
|
238
|
+
|
|
239
|
+
// Configure Java
|
|
240
|
+
await configureJavaHome();
|
|
241
|
+
|
|
242
|
+
return { success: true, skipped: false };
|
|
243
|
+
} else {
|
|
244
|
+
logger.error('Failed to install OpenJDK');
|
|
245
|
+
return { success: false, skipped: false };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Configure JAVA_HOME for macOS
|
|
251
|
+
*/
|
|
252
|
+
async function configureJavaHome() {
|
|
253
|
+
logger.step('Configuring Java...');
|
|
254
|
+
|
|
255
|
+
// Create symlink for system Java wrappers
|
|
256
|
+
execCommand(
|
|
257
|
+
'sudo ln -sfn $(brew --prefix)/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk 2>/dev/null || true',
|
|
258
|
+
{ silent: true, showCommand: false }
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
// Set JAVA_HOME in shell config
|
|
262
|
+
const shellConfig = process.env.SHELL?.includes('zsh')
|
|
263
|
+
? '~/.zshrc'
|
|
264
|
+
: '~/.bash_profile';
|
|
265
|
+
|
|
266
|
+
// Check if already configured
|
|
267
|
+
const alreadySet = execSilent(`grep -q 'JAVA_HOME' ${shellConfig} 2>/dev/null`);
|
|
268
|
+
|
|
269
|
+
if (!alreadySet.success) {
|
|
270
|
+
execCommand(
|
|
271
|
+
`echo 'export JAVA_HOME="$(/usr/libexec/java_home)"' >> ${shellConfig}`,
|
|
272
|
+
{ silent: true, showCommand: false }
|
|
273
|
+
);
|
|
274
|
+
execCommand(
|
|
275
|
+
`echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> ${shellConfig}`,
|
|
276
|
+
{ silent: true, showCommand: false }
|
|
277
|
+
);
|
|
278
|
+
logger.success('JAVA_HOME configured');
|
|
279
|
+
} else {
|
|
280
|
+
logger.info('JAVA_HOME already configured in shell');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Run base system setup for macOS
|
|
286
|
+
*/
|
|
287
|
+
async function setupBase() {
|
|
288
|
+
const results = {
|
|
289
|
+
installed: [],
|
|
290
|
+
skipped: [],
|
|
291
|
+
failed: []
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// Install Xcode CLI Tools
|
|
295
|
+
const xcodeResult = await installXcodeCliTools();
|
|
296
|
+
if (xcodeResult.pending) {
|
|
297
|
+
return { ...results, pending: true };
|
|
298
|
+
}
|
|
299
|
+
if (xcodeResult.success) {
|
|
300
|
+
(xcodeResult.skipped ? results.skipped : results.installed).push(
|
|
301
|
+
'Xcode CLI Tools'
|
|
302
|
+
);
|
|
303
|
+
} else {
|
|
304
|
+
results.failed.push('Xcode CLI Tools');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Install Homebrew
|
|
308
|
+
const brewResult = await installHomebrew();
|
|
309
|
+
if (brewResult.success) {
|
|
310
|
+
(brewResult.skipped ? results.skipped : results.installed).push('Homebrew');
|
|
311
|
+
} else {
|
|
312
|
+
results.failed.push('Homebrew');
|
|
313
|
+
return results; // Can't continue without Homebrew
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Install Git
|
|
317
|
+
const gitResult = await installGit();
|
|
318
|
+
if (gitResult.success) {
|
|
319
|
+
(gitResult.skipped ? results.skipped : results.installed).push('Git');
|
|
320
|
+
} else {
|
|
321
|
+
results.failed.push('Git');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Install curl
|
|
325
|
+
const curlResult = await installCurl();
|
|
326
|
+
if (curlResult.success) {
|
|
327
|
+
(curlResult.skipped ? results.skipped : results.installed).push('curl');
|
|
328
|
+
} else {
|
|
329
|
+
results.failed.push('curl');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return results;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
module.exports = {
|
|
336
|
+
installXcodeCliTools,
|
|
337
|
+
installHomebrew,
|
|
338
|
+
brewInstall,
|
|
339
|
+
installGit,
|
|
340
|
+
installCurl,
|
|
341
|
+
installNode,
|
|
342
|
+
installVSCode,
|
|
343
|
+
enableVSCodeCli,
|
|
344
|
+
installPython,
|
|
345
|
+
installJava,
|
|
346
|
+
configureJavaHome,
|
|
347
|
+
setupBase
|
|
348
|
+
};
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows Platform Installer
|
|
3
|
+
* Handles Windows-specific installations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { execCommand, execSilent, commandExists } = require('../utils/executor');
|
|
7
|
+
const logger = require('../utils/logger');
|
|
8
|
+
const { isElevated } = require('./detector');
|
|
9
|
+
const ora = require('ora');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if running with Administrator privileges
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function checkAdminPrivileges() {
|
|
16
|
+
return isElevated();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get the available package manager (winget or choco)
|
|
21
|
+
* @returns {string|null}
|
|
22
|
+
*/
|
|
23
|
+
function getPackageManager() {
|
|
24
|
+
if (commandExists('winget')) {
|
|
25
|
+
return 'winget';
|
|
26
|
+
}
|
|
27
|
+
if (commandExists('choco')) {
|
|
28
|
+
return 'choco';
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get install command for package manager
|
|
35
|
+
* @param {string} packageManager - Package manager
|
|
36
|
+
* @param {string} packageId - Package ID
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function getInstallCommand(packageManager, packageId) {
|
|
40
|
+
if (packageManager === 'winget') {
|
|
41
|
+
return `winget install --accept-source-agreements --accept-package-agreements -e --id ${packageId}`;
|
|
42
|
+
}
|
|
43
|
+
if (packageManager === 'choco') {
|
|
44
|
+
return `choco install -y ${packageId}`;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Package name mapping between winget and chocolatey
|
|
51
|
+
*/
|
|
52
|
+
const packageMap = {
|
|
53
|
+
git: {
|
|
54
|
+
winget: 'Git.Git',
|
|
55
|
+
choco: 'git'
|
|
56
|
+
},
|
|
57
|
+
nodejs: {
|
|
58
|
+
winget: 'OpenJS.NodeJS.LTS',
|
|
59
|
+
choco: 'nodejs-lts'
|
|
60
|
+
},
|
|
61
|
+
vscode: {
|
|
62
|
+
winget: 'Microsoft.VisualStudioCode',
|
|
63
|
+
choco: 'vscode'
|
|
64
|
+
},
|
|
65
|
+
python: {
|
|
66
|
+
winget: 'Python.Python.3.12',
|
|
67
|
+
choco: 'python'
|
|
68
|
+
},
|
|
69
|
+
java: {
|
|
70
|
+
winget: 'Microsoft.OpenJDK.17',
|
|
71
|
+
choco: 'openjdk17'
|
|
72
|
+
},
|
|
73
|
+
curl: {
|
|
74
|
+
winget: 'cURL.cURL',
|
|
75
|
+
choco: 'curl'
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get package ID for the package manager
|
|
81
|
+
* @param {string} packageManager - Package manager
|
|
82
|
+
* @param {string} genericName - Generic package name
|
|
83
|
+
* @returns {string}
|
|
84
|
+
*/
|
|
85
|
+
function getPackageId(packageManager, genericName) {
|
|
86
|
+
return packageMap[genericName]?.[packageManager] || genericName;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Install a package using the available package manager
|
|
91
|
+
* @param {string} genericName - Generic package name
|
|
92
|
+
* @param {string} displayName - Display name for logging
|
|
93
|
+
* @param {string} checkCommand - Command to check if installed
|
|
94
|
+
* @returns {Object} Installation result
|
|
95
|
+
*/
|
|
96
|
+
async function installPackage(genericName, displayName, checkCommand = null) {
|
|
97
|
+
const name = displayName || genericName;
|
|
98
|
+
const packageManager = getPackageManager();
|
|
99
|
+
|
|
100
|
+
if (!packageManager) {
|
|
101
|
+
logger.error('No package manager available (winget or chocolatey required)');
|
|
102
|
+
return { success: false, skipped: false };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
logger.step(`Checking ${name}...`);
|
|
106
|
+
|
|
107
|
+
// Check if already installed
|
|
108
|
+
const cmdToCheck = checkCommand || genericName;
|
|
109
|
+
if (commandExists(cmdToCheck)) {
|
|
110
|
+
logger.success(`${name} already installed`);
|
|
111
|
+
return { success: true, skipped: true };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const packageId = getPackageId(packageManager, genericName);
|
|
115
|
+
const command = getInstallCommand(packageManager, packageId);
|
|
116
|
+
|
|
117
|
+
logger.step(`Installing ${name} using ${packageManager}...`);
|
|
118
|
+
const result = execCommand(command);
|
|
119
|
+
|
|
120
|
+
if (result.success) {
|
|
121
|
+
logger.success(`${name} installed successfully`);
|
|
122
|
+
return { success: true, skipped: false };
|
|
123
|
+
} else {
|
|
124
|
+
logger.error(`Failed to install ${name}`);
|
|
125
|
+
return { success: false, skipped: false };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Install Chocolatey package manager
|
|
131
|
+
* @returns {Object} Installation result
|
|
132
|
+
*/
|
|
133
|
+
async function installChocolatey() {
|
|
134
|
+
logger.step('Checking Chocolatey...');
|
|
135
|
+
|
|
136
|
+
if (commandExists('choco')) {
|
|
137
|
+
logger.success('Chocolatey already installed');
|
|
138
|
+
return { success: true, skipped: true };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!checkAdminPrivileges()) {
|
|
142
|
+
logger.error('Administrator privileges required to install Chocolatey');
|
|
143
|
+
logger.info('Please run this tool as Administrator');
|
|
144
|
+
return { success: false, skipped: false, needsAdmin: true };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
logger.step('Installing Chocolatey...');
|
|
148
|
+
|
|
149
|
+
const installScript = `
|
|
150
|
+
Set-ExecutionPolicy Bypass -Scope Process -Force;
|
|
151
|
+
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;
|
|
152
|
+
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
|
153
|
+
`;
|
|
154
|
+
|
|
155
|
+
const result = execCommand(`powershell -Command "${installScript.replace(/\n/g, ' ')}"`);
|
|
156
|
+
|
|
157
|
+
if (result.success) {
|
|
158
|
+
logger.success('Chocolatey installed successfully');
|
|
159
|
+
return { success: true, skipped: false };
|
|
160
|
+
} else {
|
|
161
|
+
logger.error('Failed to install Chocolatey');
|
|
162
|
+
return { success: false, skipped: false };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Install Git
|
|
168
|
+
*/
|
|
169
|
+
async function installGit() {
|
|
170
|
+
return installPackage('git', 'Git', 'git');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Install curl
|
|
175
|
+
*/
|
|
176
|
+
async function installCurl() {
|
|
177
|
+
// curl is included in Windows 10+ by default
|
|
178
|
+
if (commandExists('curl')) {
|
|
179
|
+
logger.success('curl already available (built into Windows)');
|
|
180
|
+
return { success: true, skipped: true };
|
|
181
|
+
}
|
|
182
|
+
return installPackage('curl', 'curl', 'curl');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Install Node.js
|
|
187
|
+
*/
|
|
188
|
+
async function installNode() {
|
|
189
|
+
return installPackage('nodejs', 'Node.js LTS', 'node');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Install Visual Studio Code
|
|
194
|
+
*/
|
|
195
|
+
async function installVSCode() {
|
|
196
|
+
return installPackage('vscode', 'Visual Studio Code', 'code');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Enable VS Code CLI
|
|
201
|
+
*/
|
|
202
|
+
async function enableVSCodeCli() {
|
|
203
|
+
logger.step('Checking VS Code CLI...');
|
|
204
|
+
|
|
205
|
+
if (commandExists('code')) {
|
|
206
|
+
logger.success('VS Code CLI already available');
|
|
207
|
+
return { success: true, skipped: true };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// VS Code installer usually adds to PATH, but may need a terminal restart
|
|
211
|
+
logger.warn('VS Code CLI may require terminal restart to be available');
|
|
212
|
+
return { success: true, skipped: true };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Install Python
|
|
217
|
+
*/
|
|
218
|
+
async function installPython() {
|
|
219
|
+
return installPackage('python', 'Python', 'python');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Install Java (OpenJDK)
|
|
224
|
+
*/
|
|
225
|
+
async function installJava() {
|
|
226
|
+
const result = await installPackage('java', 'OpenJDK 17', 'java');
|
|
227
|
+
|
|
228
|
+
if (result.success && !result.skipped) {
|
|
229
|
+
// Set JAVA_HOME
|
|
230
|
+
logger.step('Configuring JAVA_HOME...');
|
|
231
|
+
|
|
232
|
+
const javaHome = execSilent('where java');
|
|
233
|
+
if (javaHome.success && javaHome.output) {
|
|
234
|
+
const javaPath = javaHome.output.split('\n')[0].trim();
|
|
235
|
+
const javaBinDir = javaPath.replace('\\bin\\java.exe', '');
|
|
236
|
+
|
|
237
|
+
// Set user environment variable
|
|
238
|
+
execCommand(
|
|
239
|
+
`setx JAVA_HOME "${javaBinDir}"`,
|
|
240
|
+
{ silent: true }
|
|
241
|
+
);
|
|
242
|
+
logger.success('JAVA_HOME configured');
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Check PowerShell availability
|
|
251
|
+
*/
|
|
252
|
+
function checkPowerShell() {
|
|
253
|
+
return commandExists('powershell') || commandExists('pwsh');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Run base system setup for Windows
|
|
258
|
+
*/
|
|
259
|
+
async function setupBase() {
|
|
260
|
+
const results = {
|
|
261
|
+
installed: [],
|
|
262
|
+
skipped: [],
|
|
263
|
+
failed: []
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// Check PowerShell
|
|
267
|
+
if (!checkPowerShell()) {
|
|
268
|
+
logger.error('PowerShell is required but not found');
|
|
269
|
+
results.failed.push('PowerShell');
|
|
270
|
+
return results;
|
|
271
|
+
}
|
|
272
|
+
results.skipped.push('PowerShell');
|
|
273
|
+
|
|
274
|
+
// Check admin privileges
|
|
275
|
+
if (!checkAdminPrivileges()) {
|
|
276
|
+
logger.warn('Not running as Administrator');
|
|
277
|
+
logger.info('Some installations may fail. Consider running as Administrator.');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Check/install package manager
|
|
281
|
+
let packageManager = getPackageManager();
|
|
282
|
+
|
|
283
|
+
if (!packageManager) {
|
|
284
|
+
logger.info('No package manager found. Installing Chocolatey...');
|
|
285
|
+
const chocoResult = await installChocolatey();
|
|
286
|
+
if (chocoResult.success) {
|
|
287
|
+
results.installed.push('Chocolatey');
|
|
288
|
+
packageManager = 'choco';
|
|
289
|
+
} else {
|
|
290
|
+
results.failed.push('Chocolatey');
|
|
291
|
+
logger.error('Cannot continue without a package manager');
|
|
292
|
+
return results;
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
logger.success(`Using package manager: ${packageManager}`);
|
|
296
|
+
results.skipped.push(packageManager === 'winget' ? 'winget' : 'Chocolatey');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Install Git
|
|
300
|
+
const gitResult = await installGit();
|
|
301
|
+
if (gitResult.success) {
|
|
302
|
+
(gitResult.skipped ? results.skipped : results.installed).push('Git');
|
|
303
|
+
} else {
|
|
304
|
+
results.failed.push('Git');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Install/check curl
|
|
308
|
+
const curlResult = await installCurl();
|
|
309
|
+
if (curlResult.success) {
|
|
310
|
+
(curlResult.skipped ? results.skipped : results.installed).push('curl');
|
|
311
|
+
} else {
|
|
312
|
+
results.failed.push('curl');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return results;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
module.exports = {
|
|
319
|
+
checkAdminPrivileges,
|
|
320
|
+
getPackageManager,
|
|
321
|
+
getInstallCommand,
|
|
322
|
+
getPackageId,
|
|
323
|
+
installPackage,
|
|
324
|
+
installChocolatey,
|
|
325
|
+
installGit,
|
|
326
|
+
installCurl,
|
|
327
|
+
installNode,
|
|
328
|
+
installVSCode,
|
|
329
|
+
enableVSCodeCli,
|
|
330
|
+
installPython,
|
|
331
|
+
installJava,
|
|
332
|
+
checkPowerShell,
|
|
333
|
+
setupBase
|
|
334
|
+
};
|