@fredlackey/devutils 0.0.11 → 0.0.13

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.
@@ -255,9 +255,10 @@ async function install_ubuntu() {
255
255
  }
256
256
 
257
257
  // Step 3: Run the installer in quiet/non-interactive mode
258
+ // Note: The -dir parameter is required to specify installation directory
258
259
  console.log('Running installer (this may take a moment)...');
259
260
  const installResult = await shell.exec(
260
- 'sudo /tmp/studio-3t-linux-x64.sh -q'
261
+ `sudo /tmp/studio-3t-linux-x64.sh -q -dir ${LINUX_INSTALL_DIR}`
261
262
  );
262
263
 
263
264
  if (installResult.code !== 0) {
@@ -266,7 +267,7 @@ async function install_ubuntu() {
266
267
  `Error: ${installResult.stderr}\n\n` +
267
268
  `Troubleshooting:\n` +
268
269
  ` 1. Ensure you have sudo privileges\n` +
269
- ` 2. Try running manually: sudo /tmp/studio-3t-linux-x64.sh -q`
270
+ ` 2. Try running manually: sudo /tmp/studio-3t-linux-x64.sh -q -dir ${LINUX_INSTALL_DIR}`
270
271
  );
271
272
  }
272
273
 
@@ -410,7 +411,7 @@ async function install_amazon_linux() {
410
411
  `Error: ${installResult.stderr}\n\n` +
411
412
  `Troubleshooting:\n` +
412
413
  ` 1. Ensure you have sudo privileges\n` +
413
- ` 2. Try running manually: sudo /tmp/studio-3t-linux-x64.sh -q`
414
+ ` 2. Try running manually: sudo /tmp/studio-3t-linux-x64.sh -q -dir ${LINUX_INSTALL_DIR}`
414
415
  );
415
416
  }
416
417
 
@@ -597,9 +598,10 @@ async function install_ubuntu_wsl() {
597
598
  }
598
599
 
599
600
  // Step 3: Run the installer in quiet/non-interactive mode
601
+ // Note: The -dir parameter is required to specify installation directory
600
602
  console.log('Running installer (this may take a moment)...');
601
603
  const installResult = await shell.exec(
602
- 'sudo /tmp/studio-3t-linux-x64.sh -q'
604
+ `sudo /tmp/studio-3t-linux-x64.sh -q -dir ${LINUX_INSTALL_DIR}`
603
605
  );
604
606
 
605
607
  if (installResult.code !== 0) {
@@ -608,7 +610,7 @@ async function install_ubuntu_wsl() {
608
610
  `Error: ${installResult.stderr}\n\n` +
609
611
  `Troubleshooting:\n` +
610
612
  ` 1. Ensure you have sudo privileges\n` +
611
- ` 2. Try running manually: sudo /tmp/studio-3t-linux-x64.sh -q`
613
+ ` 2. Try running manually: sudo /tmp/studio-3t-linux-x64.sh -q -dir ${LINUX_INSTALL_DIR}`
612
614
  );
613
615
  }
614
616
 
@@ -128,9 +128,14 @@ async function setupSublimeAptRepository() {
128
128
 
129
129
  // Add the Sublime Text repository using the modern DEB822 format
130
130
  console.log('Adding Sublime Text repository...');
131
- const repoContent = "Types: deb\\nURIs: https://download.sublimetext.com/\\nSuites: apt/stable/\\nSigned-By: /etc/apt/keyrings/sublimehq-pub.asc";
131
+ // Use cat with heredoc to create the DEB822 sources file for better reliability
132
132
  const repoResult = await shell.exec(
133
- `echo -e '${repoContent}' | sudo tee /etc/apt/sources.list.d/sublime-text.sources`
133
+ `cat <<'SUBLREPO' | sudo tee /etc/apt/sources.list.d/sublime-text.sources
134
+ Types: deb
135
+ URIs: https://download.sublimetext.com/
136
+ Suites: apt/stable/
137
+ Signed-By: /etc/apt/keyrings/sublimehq-pub.asc
138
+ SUBLREPO`
134
139
  );
135
140
  if (repoResult.code !== 0) {
136
141
  throw new Error(`Failed to add Sublime Text repository: ${repoResult.stderr}`);
@@ -0,0 +1,455 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @fileoverview Install Zsh (Z Shell) across supported platforms.
5
+ *
6
+ * Zsh is a powerful Unix shell and command interpreter designed for interactive
7
+ * use and scripting. It combines features from Bash, ksh, and tcsh while adding
8
+ * many original features like advanced tab completion, spelling correction,
9
+ * themeable prompts, and an extensive plugin ecosystem.
10
+ *
11
+ * Key features of Zsh:
12
+ * - Advanced context-aware tab completion
13
+ * - Spelling correction for commands and paths
14
+ * - Highly customizable, themeable prompts
15
+ * - Extended pattern matching and recursive globbing
16
+ * - Plugin ecosystem (Oh My Zsh, etc.)
17
+ * - Shared command history across sessions
18
+ *
19
+ * Since macOS Catalina (10.15), Zsh is the default shell on macOS. On Linux
20
+ * systems, Bash typically remains the default, but Zsh is readily available
21
+ * through standard package managers.
22
+ *
23
+ * IMPORTANT: This installer does NOT automatically change the user's default
24
+ * shell. Changing the default shell should be a deliberate user action.
25
+ *
26
+ * @module installs/zsh
27
+ */
28
+
29
+ const os = require('../utils/common/os');
30
+ const shell = require('../utils/common/shell');
31
+ const brew = require('../utils/macos/brew');
32
+ const apt = require('../utils/ubuntu/apt');
33
+
34
+ /**
35
+ * Retrieves the currently installed Zsh version by running `zsh --version`.
36
+ *
37
+ * The version output format is: "zsh 5.9 (x86_64-apple-darwin23.0)"
38
+ * This function extracts just the numeric version (e.g., "5.9").
39
+ *
40
+ * @returns {Promise<string|null>} The version string (e.g., "5.9") or null if not found
41
+ */
42
+ async function getZshVersion() {
43
+ const result = await shell.exec('zsh --version');
44
+ if (result.code === 0 && result.stdout) {
45
+ // Output format: "zsh 5.9 (x86_64-apple-darwin23.0)"
46
+ // Extract just the version number (e.g., "5.9")
47
+ const versionMatch = result.stdout.match(/zsh\s+(\d+\.\d+\.?\d*)/);
48
+ if (versionMatch) {
49
+ return versionMatch[1];
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+
55
+ /**
56
+ * Checks if Zsh is already installed on the system.
57
+ *
58
+ * Uses the shell utility to check if the 'zsh' command exists in PATH.
59
+ * This is a synchronous check that returns immediately.
60
+ *
61
+ * @returns {boolean} True if zsh command exists in PATH
62
+ */
63
+ function isZshCommandAvailable() {
64
+ return shell.commandExists('zsh');
65
+ }
66
+
67
+ /**
68
+ * Install Zsh on macOS using Homebrew.
69
+ *
70
+ * macOS Catalina (10.15) and later include Zsh pre-installed as the default
71
+ * shell. This function can install a newer version via Homebrew if desired.
72
+ * The system Zsh at /bin/zsh will always remain available.
73
+ *
74
+ * Note: This installer checks if Zsh is installed via Homebrew specifically,
75
+ * not just if Zsh exists on the system (since macOS always has system Zsh).
76
+ *
77
+ * @returns {Promise<void>}
78
+ */
79
+ async function install_macos() {
80
+ // Check if Homebrew is available (required for installing Homebrew Zsh)
81
+ if (!brew.isInstalled()) {
82
+ console.log('Homebrew is required to install Zsh via Homebrew on macOS.');
83
+ console.log('Please install Homebrew first: dev install homebrew');
84
+ console.log('');
85
+ console.log('Note: macOS already includes Zsh pre-installed at /bin/zsh.');
86
+ console.log('Homebrew is only needed if you want a newer version.');
87
+ return;
88
+ }
89
+
90
+ // Check if Zsh is already installed via Homebrew
91
+ // Note: We check the Homebrew formula, not the command, because macOS has
92
+ // a system Zsh that will always exist at /bin/zsh
93
+ const isBrewZshInstalled = await brew.isFormulaInstalled('zsh');
94
+ if (isBrewZshInstalled) {
95
+ const version = await getZshVersion();
96
+ console.log(`Zsh is already installed via Homebrew (version ${version || 'unknown'}).`);
97
+ return;
98
+ }
99
+
100
+ // Check if user has system Zsh and inform them before proceeding
101
+ if (isZshCommandAvailable()) {
102
+ const systemVersion = await getZshVersion();
103
+ console.log(`System Zsh is available (version ${systemVersion || 'unknown'}).`);
104
+ console.log('Installing Homebrew Zsh for a potentially newer version...');
105
+ console.log('');
106
+ }
107
+
108
+ // Proceed with Homebrew Zsh installation
109
+ console.log('Installing Zsh via Homebrew...');
110
+ const installResult = await brew.install('zsh');
111
+
112
+ if (!installResult.success) {
113
+ console.log('Failed to install Zsh via Homebrew.');
114
+ console.log(installResult.output);
115
+ return;
116
+ }
117
+
118
+ // Verify installation succeeded
119
+ const verifyResult = await brew.isFormulaInstalled('zsh');
120
+ if (!verifyResult) {
121
+ console.log('Installation verification failed: Zsh was not found after install.');
122
+ return;
123
+ }
124
+
125
+ // Get the Homebrew prefix to determine the correct zsh path
126
+ // On Apple Silicon: /opt/homebrew/bin/zsh
127
+ // On Intel Macs: /usr/local/bin/zsh
128
+ const prefixResult = await shell.exec('brew --prefix');
129
+ const brewPrefix = prefixResult.code === 0 ? prefixResult.stdout.trim() : '/opt/homebrew';
130
+ const brewZshPath = `${brewPrefix}/bin/zsh`;
131
+
132
+ console.log('Zsh installed successfully via Homebrew.');
133
+ console.log(`Homebrew Zsh path: ${brewZshPath}`);
134
+ console.log('');
135
+ console.log('To use the Homebrew Zsh as your default shell:');
136
+ console.log(` 1. Add it to allowed shells: echo "${brewZshPath}" | sudo tee -a /etc/shells`);
137
+ console.log(` 2. Change your default shell: chsh -s "${brewZshPath}"`);
138
+ console.log('');
139
+ console.log('Or continue using the system Zsh at /bin/zsh');
140
+
141
+ // Display the installed version
142
+ const version = await getZshVersion();
143
+ if (version) {
144
+ console.log(`\nInstalled Zsh version: ${version}`);
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Install Zsh on Ubuntu/Debian using APT.
150
+ *
151
+ * Ubuntu and Debian use Bash as the default shell, but Zsh is available in
152
+ * the standard repositories. This function installs Zsh via APT and provides
153
+ * instructions for setting it as the default shell.
154
+ *
155
+ * @returns {Promise<void>}
156
+ */
157
+ async function install_ubuntu() {
158
+ // Check if Zsh is already installed by looking for the command
159
+ if (isZshCommandAvailable()) {
160
+ const version = await getZshVersion();
161
+ console.log(`Zsh is already installed (version ${version || 'unknown'}).`);
162
+ return;
163
+ }
164
+
165
+ // Update package lists before installing to ensure we get the latest version
166
+ console.log('Updating package lists...');
167
+ const updateResult = await apt.update();
168
+ if (!updateResult.success) {
169
+ console.log('Warning: Failed to update package lists. Continuing with installation...');
170
+ }
171
+
172
+ // Install Zsh using APT
173
+ console.log('Installing Zsh via APT...');
174
+ const installResult = await apt.install('zsh');
175
+
176
+ if (!installResult.success) {
177
+ console.log('Failed to install Zsh via APT.');
178
+ console.log(installResult.output);
179
+ return;
180
+ }
181
+
182
+ // Verify the installation succeeded by checking if the command exists
183
+ if (!isZshCommandAvailable()) {
184
+ console.log('Installation verification failed: zsh command not found after install.');
185
+ return;
186
+ }
187
+
188
+ console.log('Zsh installed successfully.');
189
+
190
+ // Display the installed version
191
+ const version = await getZshVersion();
192
+ if (version) {
193
+ console.log(`Installed Zsh version: ${version}`);
194
+ }
195
+
196
+ // Provide instructions for setting Zsh as the default shell
197
+ console.log('');
198
+ console.log('To set Zsh as your default shell, run:');
199
+ console.log(' chsh -s $(which zsh)');
200
+ console.log('');
201
+ console.log('Then log out and log back in for the change to take effect.');
202
+ }
203
+
204
+ /**
205
+ * Install Zsh on Ubuntu running in WSL (Windows Subsystem for Linux).
206
+ *
207
+ * WSL provides a full Linux environment on Windows. The installation process
208
+ * is identical to native Ubuntu using APT. After installation, users may need
209
+ * to configure their WSL session to start Zsh automatically.
210
+ *
211
+ * @returns {Promise<void>}
212
+ */
213
+ async function install_ubuntu_wsl() {
214
+ console.log('Installing Zsh in WSL Ubuntu environment...');
215
+
216
+ // WSL Ubuntu uses the same APT-based installation as native Ubuntu
217
+ await install_ubuntu();
218
+
219
+ // Provide WSL-specific guidance for auto-launching Zsh
220
+ if (isZshCommandAvailable()) {
221
+ console.log('');
222
+ console.log('WSL Tip: If changing the default shell does not work, add this to ~/.bashrc:');
223
+ console.log('');
224
+ console.log(' if [ -t 1 ] && [ -x /usr/bin/zsh ]; then');
225
+ console.log(' exec /usr/bin/zsh');
226
+ console.log(' fi');
227
+ console.log('');
228
+ console.log('This will automatically launch Zsh when you open a WSL terminal.');
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Install Zsh on Raspberry Pi OS using APT.
234
+ *
235
+ * Raspberry Pi OS is based on Debian, so Zsh installation follows the same
236
+ * APT-based process. Zsh works well on all Raspberry Pi models, though
237
+ * complex Zsh prompts (with git status, etc.) may be slower on older models.
238
+ *
239
+ * @returns {Promise<void>}
240
+ */
241
+ async function install_raspbian() {
242
+ console.log('Installing Zsh on Raspberry Pi OS...');
243
+
244
+ // Raspberry Pi OS uses APT just like Ubuntu/Debian
245
+ // The installation process is identical
246
+ await install_ubuntu();
247
+
248
+ // Provide Raspberry Pi-specific tips
249
+ if (isZshCommandAvailable()) {
250
+ console.log('');
251
+ console.log('Raspberry Pi Tip: If you experience slow prompts, use a simple prompt:');
252
+ console.log(" Add to ~/.zshrc: PROMPT='%n@%m:%~%# '");
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Install Zsh on Amazon Linux or RHEL using DNF/YUM.
258
+ *
259
+ * Amazon Linux and RHEL use Bash as the default shell. Zsh is available in
260
+ * the standard repositories. This function detects whether to use DNF (modern)
261
+ * or YUM (legacy) based on what is available.
262
+ *
263
+ * @returns {Promise<void>}
264
+ */
265
+ async function install_amazon_linux() {
266
+ // Check if Zsh is already installed by looking for the command
267
+ if (isZshCommandAvailable()) {
268
+ const version = await getZshVersion();
269
+ console.log(`Zsh is already installed (version ${version || 'unknown'}).`);
270
+ return;
271
+ }
272
+
273
+ // Determine whether to use DNF or YUM
274
+ // DNF is available on Amazon Linux 2023 and RHEL 8/9
275
+ // YUM is used on Amazon Linux 2 and older RHEL
276
+ const useDnf = shell.commandExists('dnf');
277
+ const packageManager = useDnf ? 'dnf' : 'yum';
278
+
279
+ console.log(`Installing Zsh via ${packageManager.toUpperCase()}...`);
280
+
281
+ // Install Zsh using the appropriate package manager
282
+ // The -y flag automatically confirms all prompts for non-interactive installation
283
+ const installCommand = `sudo ${packageManager} install -y zsh`;
284
+ const installResult = await shell.exec(installCommand);
285
+
286
+ if (installResult.code !== 0) {
287
+ console.log(`Failed to install Zsh via ${packageManager.toUpperCase()}.`);
288
+ console.log(installResult.stderr || installResult.stdout);
289
+ return;
290
+ }
291
+
292
+ // Verify installation succeeded
293
+ if (!isZshCommandAvailable()) {
294
+ console.log('Installation verification failed: zsh command not found after install.');
295
+ return;
296
+ }
297
+
298
+ console.log('Zsh installed successfully.');
299
+
300
+ // Display the installed version
301
+ const version = await getZshVersion();
302
+ if (version) {
303
+ console.log(`Installed Zsh version: ${version}`);
304
+ }
305
+
306
+ // Provide instructions for setting Zsh as the default shell
307
+ console.log('');
308
+ console.log('To set Zsh as your default shell, run:');
309
+ console.log(' sudo usermod --shell $(which zsh) $USER');
310
+ console.log('');
311
+ console.log('Then log out and log back in for the change to take effect.');
312
+ }
313
+
314
+ /**
315
+ * Handle Zsh installation on native Windows.
316
+ *
317
+ * Zsh does not run on native Windows (PowerShell, Command Prompt).
318
+ * Zsh is a Unix shell that requires a POSIX-compatible environment.
319
+ * This function informs the user gracefully without throwing an error.
320
+ *
321
+ * @returns {Promise<void>}
322
+ */
323
+ async function install_windows() {
324
+ console.log('Zsh is not available for native Windows.');
325
+ return;
326
+ }
327
+
328
+ /**
329
+ * Handle Zsh installation in Git Bash on Windows.
330
+ *
331
+ * Zsh does not run in Git Bash. Git Bash uses a MinGW-based environment
332
+ * that provides Bash, not Zsh. While Git Bash includes many Unix utilities,
333
+ * it does not support alternative shells like Zsh.
334
+ *
335
+ * @returns {Promise<void>}
336
+ */
337
+ async function install_gitbash() {
338
+ console.log('Zsh is not available for Git Bash.');
339
+ return;
340
+ }
341
+
342
+ /**
343
+ * Check if Zsh is installed on the current platform.
344
+ *
345
+ * This function checks whether Zsh is available, using platform-appropriate
346
+ * verification methods:
347
+ * - macOS: Checks Homebrew formula (system Zsh always exists)
348
+ * - Linux/WSL: Checks if zsh command exists
349
+ * - Windows/Git Bash: Always returns false (not supported)
350
+ *
351
+ * @returns {Promise<boolean>} True if Zsh is installed
352
+ */
353
+ async function isInstalled() {
354
+ const platform = os.detect();
355
+
356
+ // Windows and Git Bash don't support Zsh
357
+ if (platform.type === 'windows' || platform.type === 'gitbash') {
358
+ return false;
359
+ }
360
+
361
+ // macOS: Check Homebrew formula (system Zsh is always present at /bin/zsh)
362
+ // For macOS, we consider Zsh "installed" if either Homebrew Zsh or system Zsh exists
363
+ if (platform.type === 'macos') {
364
+ // System Zsh is always available on macOS Catalina+
365
+ return isZshCommandAvailable();
366
+ }
367
+
368
+ // Linux platforms (including WSL): Check if zsh command exists
369
+ return isZshCommandAvailable();
370
+ }
371
+
372
+ /**
373
+ * Check if this installer is supported on the current platform.
374
+ *
375
+ * Zsh can be installed on:
376
+ * - macOS (pre-installed, can update via Homebrew)
377
+ * - Ubuntu/Debian (via APT)
378
+ * - Ubuntu on WSL (via APT)
379
+ * - Raspberry Pi OS (via APT)
380
+ * - Amazon Linux/RHEL/Fedora (via DNF/YUM)
381
+ *
382
+ * Zsh is NOT supported on:
383
+ * - Windows (native) - requires POSIX environment
384
+ * - Git Bash - uses MinGW, not a full Unix environment
385
+ *
386
+ * @returns {boolean} True if installation is supported on this platform
387
+ */
388
+ function isEligible() {
389
+ const platform = os.detect();
390
+ // Supported platforms for Zsh installation
391
+ return ['macos', 'ubuntu', 'debian', 'wsl', 'raspbian', 'amazon_linux', 'rhel', 'fedora'].includes(platform.type);
392
+ }
393
+
394
+ /**
395
+ * Main installation entry point - detects platform and runs appropriate installer.
396
+ *
397
+ * This function automatically detects the current operating system and invokes
398
+ * the corresponding platform-specific installation function. For unsupported
399
+ * platforms (Windows native, Git Bash), a friendly message is displayed and
400
+ * the script exits gracefully without errors.
401
+ *
402
+ * IMPORTANT: This installer does NOT change the user's default shell.
403
+ * Setting the default shell is a deliberate user action that should be
404
+ * performed separately after installation.
405
+ *
406
+ * @returns {Promise<void>}
407
+ */
408
+ async function install() {
409
+ const platform = os.detect();
410
+
411
+ // Map platform types to their installation functions
412
+ // Multiple platform types may map to the same installer (e.g., debian -> ubuntu installer)
413
+ const installers = {
414
+ 'macos': install_macos,
415
+ 'ubuntu': install_ubuntu,
416
+ 'debian': install_ubuntu,
417
+ 'wsl': install_ubuntu_wsl,
418
+ 'raspbian': install_raspbian,
419
+ 'amazon_linux': install_amazon_linux,
420
+ 'rhel': install_amazon_linux,
421
+ 'fedora': install_amazon_linux,
422
+ 'windows': install_windows,
423
+ 'gitbash': install_gitbash,
424
+ };
425
+
426
+ const installer = installers[platform.type];
427
+
428
+ // Handle unsupported platforms gracefully without throwing errors
429
+ if (!installer) {
430
+ console.log(`Zsh is not available for ${platform.type}.`);
431
+ return;
432
+ }
433
+
434
+ await installer();
435
+ }
436
+
437
+ module.exports = {
438
+ install,
439
+ isInstalled,
440
+ isEligible,
441
+ install_macos,
442
+ install_ubuntu,
443
+ install_ubuntu_wsl,
444
+ install_raspbian,
445
+ install_amazon_linux,
446
+ install_windows,
447
+ install_gitbash,
448
+ };
449
+
450
+ if (require.main === module) {
451
+ install().catch(err => {
452
+ console.error(err.message);
453
+ process.exit(1);
454
+ });
455
+ }