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.
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Platform Detection Module
3
+ * Detects OS, CPU architecture, and default shell
4
+ */
5
+
6
+ const os = require('os');
7
+ const { execSync } = require('child_process');
8
+
9
+ /**
10
+ * Detect the operating system
11
+ * @returns {Object} OS information
12
+ */
13
+ function detectOS() {
14
+ const platform = process.platform;
15
+
16
+ const osMap = {
17
+ darwin: {
18
+ name: 'macOS',
19
+ platform: 'darwin',
20
+ packageManager: 'brew',
21
+ icon: '🍎'
22
+ },
23
+ linux: {
24
+ name: 'Linux',
25
+ platform: 'linux',
26
+ packageManager: detectLinuxPackageManager(),
27
+ distro: detectLinuxDistro(),
28
+ icon: '🐧'
29
+ },
30
+ win32: {
31
+ name: 'Windows',
32
+ platform: 'win32',
33
+ packageManager: detectWindowsPackageManager(),
34
+ icon: '🪟'
35
+ }
36
+ };
37
+
38
+ return osMap[platform] || {
39
+ name: 'Unknown',
40
+ platform: platform,
41
+ packageManager: null,
42
+ icon: '❓'
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Detect Linux distribution
48
+ * @returns {string} Distribution name
49
+ */
50
+ function detectLinuxDistro() {
51
+ if (process.platform !== 'linux') return null;
52
+
53
+ try {
54
+ // Try to read /etc/os-release
55
+ const osRelease = require('fs').readFileSync('/etc/os-release', 'utf8');
56
+ const idMatch = osRelease.match(/^ID=(.*)$/m);
57
+ if (idMatch) {
58
+ return idMatch[1].replace(/"/g, '').toLowerCase();
59
+ }
60
+ } catch {
61
+ // Fallback to checking package managers
62
+ try {
63
+ execSync('which apt', { stdio: 'pipe' });
64
+ return 'debian';
65
+ } catch {
66
+ try {
67
+ execSync('which dnf', { stdio: 'pipe' });
68
+ return 'fedora';
69
+ } catch {
70
+ try {
71
+ execSync('which pacman', { stdio: 'pipe' });
72
+ return 'arch';
73
+ } catch {
74
+ return 'unknown';
75
+ }
76
+ }
77
+ }
78
+ }
79
+ return 'unknown';
80
+ }
81
+
82
+ /**
83
+ * Detect Linux package manager
84
+ * @returns {string} Package manager command
85
+ */
86
+ function detectLinuxPackageManager() {
87
+ if (process.platform !== 'linux') return null;
88
+
89
+ const managers = [
90
+ { cmd: 'apt', name: 'apt' },
91
+ { cmd: 'dnf', name: 'dnf' },
92
+ { cmd: 'yum', name: 'yum' },
93
+ { cmd: 'pacman', name: 'pacman' },
94
+ { cmd: 'zypper', name: 'zypper' },
95
+ { cmd: 'apk', name: 'apk' }
96
+ ];
97
+
98
+ for (const manager of managers) {
99
+ try {
100
+ execSync(`which ${manager.cmd}`, { stdio: 'pipe' });
101
+ return manager.name;
102
+ } catch {
103
+ continue;
104
+ }
105
+ }
106
+ return null;
107
+ }
108
+
109
+ /**
110
+ * Detect Windows package manager
111
+ * @returns {string} Package manager command
112
+ */
113
+ function detectWindowsPackageManager() {
114
+ if (process.platform !== 'win32') return null;
115
+
116
+ try {
117
+ execSync('winget --version', { stdio: 'pipe' });
118
+ return 'winget';
119
+ } catch {
120
+ try {
121
+ execSync('choco --version', { stdio: 'pipe' });
122
+ return 'choco';
123
+ } catch {
124
+ return null;
125
+ }
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Detect CPU architecture
131
+ * @returns {Object} Architecture information
132
+ */
133
+ function detectArchitecture() {
134
+ const arch = process.arch;
135
+ const cpus = os.cpus();
136
+
137
+ const archMap = {
138
+ x64: 'x86_64 (64-bit)',
139
+ arm64: 'ARM64 (Apple Silicon / ARM)',
140
+ arm: 'ARM (32-bit)',
141
+ ia32: 'x86 (32-bit)'
142
+ };
143
+
144
+ return {
145
+ arch: arch,
146
+ display: archMap[arch] || arch,
147
+ cpuModel: cpus.length > 0 ? cpus[0].model : 'Unknown',
148
+ cores: cpus.length
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Detect default shell
154
+ * @returns {Object} Shell information
155
+ */
156
+ function detectShell() {
157
+ const platform = process.platform;
158
+ let shell = process.env.SHELL || '';
159
+ let configFile = '';
160
+
161
+ if (platform === 'win32') {
162
+ // Windows shell detection
163
+ if (process.env.PSModulePath) {
164
+ shell = 'powershell';
165
+ configFile = '$PROFILE';
166
+ } else {
167
+ shell = 'cmd';
168
+ configFile = 'N/A';
169
+ }
170
+ } else {
171
+ // Unix-like shell detection
172
+ const shellName = shell.split('/').pop();
173
+
174
+ const configFiles = {
175
+ zsh: '~/.zshrc',
176
+ bash: '~/.bashrc',
177
+ fish: '~/.config/fish/config.fish',
178
+ sh: '~/.profile'
179
+ };
180
+
181
+ configFile = configFiles[shellName] || '~/.profile';
182
+ shell = shellName;
183
+ }
184
+
185
+ return {
186
+ name: shell,
187
+ path: process.env.SHELL || 'N/A',
188
+ configFile: configFile
189
+ };
190
+ }
191
+
192
+ /**
193
+ * Check if running with elevated privileges
194
+ * @returns {boolean} True if admin/root
195
+ */
196
+ function isElevated() {
197
+ if (process.platform === 'win32') {
198
+ try {
199
+ execSync('net session', { stdio: 'pipe' });
200
+ return true;
201
+ } catch {
202
+ return false;
203
+ }
204
+ } else {
205
+ return process.getuid && process.getuid() === 0;
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Get complete platform information
211
+ * @returns {Object} Complete platform details
212
+ */
213
+ function getPlatformInfo() {
214
+ const osInfo = detectOS();
215
+ const archInfo = detectArchitecture();
216
+ const shellInfo = detectShell();
217
+
218
+ return {
219
+ os: osInfo,
220
+ architecture: archInfo,
221
+ shell: shellInfo,
222
+ hostname: os.hostname(),
223
+ username: os.userInfo().username,
224
+ homedir: os.homedir(),
225
+ isElevated: isElevated(),
226
+ nodeVersion: process.version,
227
+ platform: process.platform
228
+ };
229
+ }
230
+
231
+ module.exports = {
232
+ detectOS,
233
+ detectArchitecture,
234
+ detectShell,
235
+ detectLinuxDistro,
236
+ detectLinuxPackageManager,
237
+ detectWindowsPackageManager,
238
+ isElevated,
239
+ getPlatformInfo
240
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Platform Index
3
+ * Central export for platform modules
4
+ */
5
+
6
+ const detector = require('./detector');
7
+ const macos = require('./macos');
8
+ const linux = require('./linux');
9
+ const windows = require('./windows');
10
+
11
+ /**
12
+ * Get the appropriate platform module
13
+ * @returns {Object} Platform-specific module
14
+ */
15
+ function getPlatformModule() {
16
+ const platform = process.platform;
17
+
18
+ switch (platform) {
19
+ case 'darwin':
20
+ return macos;
21
+ case 'linux':
22
+ return linux;
23
+ case 'win32':
24
+ return windows;
25
+ default:
26
+ throw new Error(`Unsupported platform: ${platform}`);
27
+ }
28
+ }
29
+
30
+ module.exports = {
31
+ detector,
32
+ macos,
33
+ linux,
34
+ windows,
35
+ getPlatformModule
36
+ };
@@ -0,0 +1,412 @@
1
+ /**
2
+ * Linux Platform Installer
3
+ * Handles Linux-specific installations
4
+ */
5
+
6
+ const { execCommand, execSilent, commandExists } = require('../utils/executor');
7
+ const logger = require('../utils/logger');
8
+ const { detectLinuxDistro, detectLinuxPackageManager } = require('./detector');
9
+ const ora = require('ora');
10
+
11
+ /**
12
+ * Get the appropriate install command for the detected package manager
13
+ * @param {string} packageManager - Package manager name
14
+ * @param {string} packageName - Package to install
15
+ * @returns {string} Install command
16
+ */
17
+ function getInstallCommand(packageManager, packageName) {
18
+ const commands = {
19
+ apt: `sudo apt-get install -y ${packageName}`,
20
+ dnf: `sudo dnf install -y ${packageName}`,
21
+ yum: `sudo yum install -y ${packageName}`,
22
+ pacman: `sudo pacman -S --noconfirm ${packageName}`,
23
+ zypper: `sudo zypper install -y ${packageName}`,
24
+ apk: `sudo apk add ${packageName}`
25
+ };
26
+
27
+ return commands[packageManager] || null;
28
+ }
29
+
30
+ /**
31
+ * Get package name mapping for different distros
32
+ * @param {string} packageManager - Package manager
33
+ * @param {string} genericName - Generic package name
34
+ * @returns {string} Distro-specific package name
35
+ */
36
+ function getPackageName(packageManager, genericName) {
37
+ const packageMap = {
38
+ nodejs: {
39
+ apt: 'nodejs',
40
+ dnf: 'nodejs',
41
+ pacman: 'nodejs',
42
+ zypper: 'nodejs',
43
+ apk: 'nodejs'
44
+ },
45
+ npm: {
46
+ apt: 'npm',
47
+ dnf: 'npm',
48
+ pacman: 'npm',
49
+ zypper: 'npm',
50
+ apk: 'npm'
51
+ },
52
+ python: {
53
+ apt: 'python3',
54
+ dnf: 'python3',
55
+ pacman: 'python',
56
+ zypper: 'python3',
57
+ apk: 'python3'
58
+ },
59
+ pip: {
60
+ apt: 'python3-pip',
61
+ dnf: 'python3-pip',
62
+ pacman: 'python-pip',
63
+ zypper: 'python3-pip',
64
+ apk: 'py3-pip'
65
+ },
66
+ java: {
67
+ apt: 'openjdk-17-jdk',
68
+ dnf: 'java-17-openjdk-devel',
69
+ pacman: 'jdk-openjdk',
70
+ zypper: 'java-17-openjdk-devel',
71
+ apk: 'openjdk17'
72
+ },
73
+ vscode: {
74
+ apt: 'code',
75
+ dnf: 'code',
76
+ pacman: 'code',
77
+ zypper: 'code',
78
+ apk: 'code'
79
+ }
80
+ };
81
+
82
+ return packageMap[genericName]?.[packageManager] || genericName;
83
+ }
84
+
85
+ /**
86
+ * Update package manager cache
87
+ * @param {string} packageManager - Package manager
88
+ * @returns {Object} Result
89
+ */
90
+ async function updatePackageCache(packageManager) {
91
+ logger.step('Updating package cache...');
92
+
93
+ const commands = {
94
+ apt: 'sudo apt-get update',
95
+ dnf: 'sudo dnf check-update || true',
96
+ yum: 'sudo yum check-update || true',
97
+ pacman: 'sudo pacman -Sy',
98
+ zypper: 'sudo zypper refresh',
99
+ apk: 'sudo apk update'
100
+ };
101
+
102
+ const command = commands[packageManager];
103
+ if (!command) {
104
+ logger.warn(`Unknown package manager: ${packageManager}`);
105
+ return { success: false };
106
+ }
107
+
108
+ const result = execCommand(command, { silent: true });
109
+ return result;
110
+ }
111
+
112
+ /**
113
+ * Install a package using the system package manager
114
+ * @param {string} packageName - Package to install
115
+ * @param {string} displayName - Display name for logging
116
+ * @returns {Object} Installation result
117
+ */
118
+ async function installPackage(packageName, displayName = null) {
119
+ const name = displayName || packageName;
120
+ const packageManager = detectLinuxPackageManager();
121
+
122
+ if (!packageManager) {
123
+ logger.error('No supported package manager found');
124
+ return { success: false, skipped: false };
125
+ }
126
+
127
+ logger.step(`Checking ${name}...`);
128
+
129
+ // Check if already installed
130
+ if (commandExists(packageName)) {
131
+ logger.success(`${name} already installed`);
132
+ return { success: true, skipped: true };
133
+ }
134
+
135
+ const distroPackage = getPackageName(packageManager, packageName);
136
+ const command = getInstallCommand(packageManager, distroPackage);
137
+
138
+ if (!command) {
139
+ logger.error(`Unsupported package manager: ${packageManager}`);
140
+ return { success: false, skipped: false };
141
+ }
142
+
143
+ logger.step(`Installing ${name}...`);
144
+ const result = execCommand(command);
145
+
146
+ if (result.success) {
147
+ logger.success(`${name} installed successfully`);
148
+ return { success: true, skipped: false };
149
+ } else {
150
+ logger.error(`Failed to install ${name}`);
151
+ return { success: false, skipped: false };
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Install Git
157
+ */
158
+ async function installGit() {
159
+ return installPackage('git', 'Git');
160
+ }
161
+
162
+ /**
163
+ * Install curl
164
+ */
165
+ async function installCurl() {
166
+ return installPackage('curl', 'curl');
167
+ }
168
+
169
+ /**
170
+ * Install Node.js and npm
171
+ */
172
+ async function installNode() {
173
+ const packageManager = detectLinuxPackageManager();
174
+
175
+ // For apt-based systems, use NodeSource repository for latest LTS
176
+ if (packageManager === 'apt') {
177
+ logger.step('Setting up NodeSource repository for Node.js LTS...');
178
+
179
+ // Check if Node.js is already installed
180
+ if (commandExists('node')) {
181
+ logger.success('Node.js already installed');
182
+ return { success: true, skipped: true };
183
+ }
184
+
185
+ // Install curl first if needed
186
+ if (!commandExists('curl')) {
187
+ await installCurl();
188
+ }
189
+
190
+ // Add NodeSource repository
191
+ const setupResult = execCommand(
192
+ 'curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -'
193
+ );
194
+
195
+ if (!setupResult.success) {
196
+ logger.warn('Failed to set up NodeSource repository, using system Node.js');
197
+ }
198
+
199
+ return installPackage('nodejs', 'Node.js');
200
+ }
201
+
202
+ // For other package managers, install directly
203
+ const nodeResult = await installPackage('nodejs', 'Node.js');
204
+ const npmResult = await installPackage('npm', 'npm');
205
+
206
+ return {
207
+ success: nodeResult.success && npmResult.success,
208
+ skipped: nodeResult.skipped && npmResult.skipped
209
+ };
210
+ }
211
+
212
+ /**
213
+ * Install Visual Studio Code
214
+ */
215
+ async function installVSCode() {
216
+ const packageManager = detectLinuxPackageManager();
217
+
218
+ logger.step('Checking Visual Studio Code...');
219
+
220
+ if (commandExists('code')) {
221
+ logger.success('Visual Studio Code already installed');
222
+ return { success: true, skipped: true };
223
+ }
224
+
225
+ logger.step('Installing Visual Studio Code...');
226
+
227
+ if (packageManager === 'apt') {
228
+ // Add Microsoft repository for Debian/Ubuntu
229
+ execCommand(
230
+ 'wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/packages.microsoft.gpg'
231
+ );
232
+ execCommand(
233
+ 'sudo install -D -o root -g root -m 644 /tmp/packages.microsoft.gpg /etc/apt/keyrings/packages.microsoft.gpg'
234
+ );
235
+ execCommand(
236
+ 'sudo sh -c \'echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list\''
237
+ );
238
+ execCommand('sudo apt-get update');
239
+ return installPackage('code', 'Visual Studio Code');
240
+ } else if (packageManager === 'dnf' || packageManager === 'yum') {
241
+ // Add Microsoft repository for Fedora/RHEL
242
+ execCommand('sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc');
243
+ execCommand(
244
+ 'sudo sh -c \'echo -e "[code]\\nname=Visual Studio Code\\nbaseurl=https://packages.microsoft.com/yumrepos/vscode\\nenabled=1\\ngpgcheck=1\\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" > /etc/yum.repos.d/vscode.repo\''
245
+ );
246
+ return installPackage('code', 'Visual Studio Code');
247
+ } else if (packageManager === 'pacman') {
248
+ // For Arch, use yay or install from AUR
249
+ if (commandExists('yay')) {
250
+ const result = execCommand('yay -S --noconfirm visual-studio-code-bin');
251
+ if (result.success) {
252
+ logger.success('Visual Studio Code installed successfully');
253
+ return { success: true, skipped: false };
254
+ }
255
+ }
256
+ logger.warn(
257
+ 'Please install Visual Studio Code from AUR: yay -S visual-studio-code-bin'
258
+ );
259
+ return { success: false, skipped: false };
260
+ }
261
+
262
+ logger.warn('Please install Visual Studio Code manually from https://code.visualstudio.com/');
263
+ return { success: false, skipped: false };
264
+ }
265
+
266
+ /**
267
+ * Enable VS Code CLI
268
+ */
269
+ async function enableVSCodeCli() {
270
+ // On Linux, VS Code CLI should be available after installation
271
+ if (commandExists('code')) {
272
+ logger.success('VS Code CLI already available');
273
+ return { success: true, skipped: true };
274
+ }
275
+ return { success: false, skipped: false };
276
+ }
277
+
278
+ /**
279
+ * Install Python
280
+ */
281
+ async function installPython() {
282
+ const pythonResult = await installPackage('python3', 'Python');
283
+ const pipResult = await installPackage('python3-pip', 'pip');
284
+
285
+ return {
286
+ success: pythonResult.success && pipResult.success,
287
+ skipped: pythonResult.skipped && pipResult.skipped
288
+ };
289
+ }
290
+
291
+ /**
292
+ * Install Java (OpenJDK)
293
+ */
294
+ async function installJava() {
295
+ const packageManager = detectLinuxPackageManager();
296
+ const javaPackage = getPackageName(packageManager, 'java');
297
+
298
+ const result = await installPackage(javaPackage, 'OpenJDK');
299
+
300
+ if (result.success && !result.skipped) {
301
+ // Set JAVA_HOME
302
+ logger.step('Configuring JAVA_HOME...');
303
+
304
+ const javaPath = execSilent('dirname $(dirname $(readlink -f $(which java)))');
305
+ if (javaPath.success && javaPath.output) {
306
+ const shellConfig = process.env.SHELL?.includes('zsh')
307
+ ? '~/.zshrc'
308
+ : '~/.bashrc';
309
+ execCommand(
310
+ `echo 'export JAVA_HOME="${javaPath.output}"' >> ${shellConfig}`
311
+ );
312
+ logger.success('JAVA_HOME configured');
313
+ }
314
+ }
315
+
316
+ return result;
317
+ }
318
+
319
+ /**
320
+ * Install core build tools
321
+ */
322
+ async function installBuildTools() {
323
+ const packageManager = detectLinuxPackageManager();
324
+
325
+ logger.step('Installing core build tools...');
326
+
327
+ const buildPackages = {
328
+ apt: 'build-essential',
329
+ dnf: '@development-tools',
330
+ pacman: 'base-devel',
331
+ zypper: '-t pattern devel_basis',
332
+ apk: 'build-base'
333
+ };
334
+
335
+ const pkg = buildPackages[packageManager];
336
+ if (!pkg) return { success: true, skipped: true };
337
+
338
+ const command = getInstallCommand(packageManager, pkg);
339
+ const result = execCommand(command);
340
+
341
+ return result;
342
+ }
343
+
344
+ /**
345
+ * Run base system setup for Linux
346
+ */
347
+ async function setupBase() {
348
+ const results = {
349
+ installed: [],
350
+ skipped: [],
351
+ failed: []
352
+ };
353
+
354
+ const packageManager = detectLinuxPackageManager();
355
+ if (!packageManager) {
356
+ logger.error('No supported package manager detected');
357
+ return results;
358
+ }
359
+
360
+ logger.info(`Detected package manager: ${packageManager}`);
361
+
362
+ // Update package cache
363
+ await updatePackageCache(packageManager);
364
+
365
+ // Install build tools
366
+ const buildResult = await installBuildTools();
367
+ if (buildResult.success) {
368
+ results.installed.push('Build Tools');
369
+ }
370
+
371
+ // Install Git
372
+ const gitResult = await installGit();
373
+ if (gitResult.success) {
374
+ (gitResult.skipped ? results.skipped : results.installed).push('Git');
375
+ } else {
376
+ results.failed.push('Git');
377
+ }
378
+
379
+ // Install curl
380
+ const curlResult = await installCurl();
381
+ if (curlResult.success) {
382
+ (curlResult.skipped ? results.skipped : results.installed).push('curl');
383
+ } else {
384
+ results.failed.push('curl');
385
+ }
386
+
387
+ // Install wget
388
+ const wgetResult = await installPackage('wget', 'wget');
389
+ if (wgetResult.success) {
390
+ (wgetResult.skipped ? results.skipped : results.installed).push('wget');
391
+ } else {
392
+ results.failed.push('wget');
393
+ }
394
+
395
+ return results;
396
+ }
397
+
398
+ module.exports = {
399
+ getInstallCommand,
400
+ getPackageName,
401
+ updatePackageCache,
402
+ installPackage,
403
+ installGit,
404
+ installCurl,
405
+ installNode,
406
+ installVSCode,
407
+ enableVSCodeCli,
408
+ installPython,
409
+ installJava,
410
+ installBuildTools,
411
+ setupBase
412
+ };