@fredlackey/devutils 0.0.1 → 0.0.2
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/README.md +5 -5
- package/package.json +1 -1
- package/src/commands/install.js +374 -36
- package/src/installs/adobe-creative-cloud.js +527 -25
- package/src/installs/adobe-creative-cloud.md +605 -0
- package/src/installs/appcleaner.js +303 -26
- package/src/installs/appcleaner.md +699 -0
- package/src/installs/apt-transport-https.js +390 -0
- package/src/installs/apt-transport-https.md +678 -0
- package/src/installs/atomicparsley.js +624 -26
- package/src/installs/atomicparsley.md +795 -0
- package/src/installs/aws-cli.js +779 -26
- package/src/installs/aws-cli.md +727 -0
- package/src/installs/balena-etcher.js +688 -26
- package/src/installs/balena-etcher.md +761 -0
- package/src/installs/bambu-studio.js +912 -26
- package/src/installs/bambu-studio.md +780 -0
- package/src/installs/bash-completion.js +554 -23
- package/src/installs/bash-completion.md +833 -0
- package/src/installs/bash.js +399 -26
- package/src/installs/bash.md +993 -0
- package/src/installs/beyond-compare.js +585 -26
- package/src/installs/beyond-compare.md +813 -0
- package/src/installs/build-essential.js +511 -26
- package/src/installs/build-essential.md +977 -0
- package/src/installs/ca-certificates.js +618 -0
- package/src/installs/ca-certificates.md +937 -0
- package/src/installs/caffeine.js +490 -26
- package/src/installs/caffeine.md +839 -0
- package/src/installs/camtasia.js +577 -25
- package/src/installs/camtasia.md +762 -0
- package/src/installs/chatgpt.js +458 -26
- package/src/installs/chatgpt.md +814 -0
- package/src/installs/chocolatey.js +447 -0
- package/src/installs/chocolatey.md +661 -0
- package/src/installs/chrome-canary.js +472 -26
- package/src/installs/chrome-canary.md +641 -0
- package/src/installs/chromium.js +645 -26
- package/src/installs/chromium.md +838 -0
- package/src/installs/claude-code.js +558 -26
- package/src/installs/claude-code.md +1173 -0
- package/src/installs/curl.js +361 -26
- package/src/installs/curl.md +714 -0
- package/src/installs/cursor.js +561 -26
- package/src/installs/cursor.md +970 -0
- package/src/installs/dbschema.js +674 -26
- package/src/installs/dbschema.md +925 -0
- package/src/installs/dependencies.md +435 -0
- package/src/installs/development-tools.js +600 -0
- package/src/installs/development-tools.md +977 -0
- package/src/installs/docker.js +1010 -25
- package/src/installs/docker.md +1109 -0
- package/src/installs/drawio.js +1001 -26
- package/src/installs/drawio.md +795 -0
- package/src/installs/elmedia-player.js +328 -25
- package/src/installs/elmedia-player.md +556 -0
- package/src/installs/ffmpeg.js +870 -25
- package/src/installs/ffmpeg.md +852 -0
- package/src/installs/file.js +464 -0
- package/src/installs/file.md +987 -0
- package/src/installs/gemini-cli.js +793 -26
- package/src/installs/gemini-cli.md +1153 -0
- package/src/installs/git.js +382 -26
- package/src/installs/git.md +907 -0
- package/src/installs/gitego.js +931 -26
- package/src/installs/gitego.md +1172 -0
- package/src/installs/go.js +913 -26
- package/src/installs/go.md +958 -0
- package/src/installs/google-chrome.js +801 -25
- package/src/installs/google-chrome.md +862 -0
- package/src/installs/gpg.js +412 -73
- package/src/installs/gpg.md +1056 -0
- package/src/installs/homebrew.js +1015 -26
- package/src/installs/homebrew.md +988 -0
- package/src/installs/imageoptim.js +950 -26
- package/src/installs/imageoptim.md +1119 -0
- package/src/installs/installers.json +2297 -0
- package/src/installs/jq.js +382 -26
- package/src/installs/jq.md +809 -0
- package/src/installs/keyboard-maestro.js +701 -26
- package/src/installs/keyboard-maestro.md +825 -0
- package/src/installs/latex.js +771 -26
- package/src/installs/latex.md +1095 -0
- package/src/installs/lftp.js +338 -26
- package/src/installs/lftp.md +907 -0
- package/src/installs/lsb-release.js +346 -0
- package/src/installs/lsb-release.md +814 -0
- package/src/installs/messenger.js +829 -26
- package/src/installs/messenger.md +900 -0
- package/src/installs/microsoft-office.js +550 -26
- package/src/installs/microsoft-office.md +760 -0
- package/src/installs/microsoft-teams.js +782 -25
- package/src/installs/microsoft-teams.md +886 -0
- package/src/installs/node.js +886 -26
- package/src/installs/node.md +1153 -0
- package/src/installs/nordpass.js +698 -26
- package/src/installs/nordpass.md +921 -0
- package/src/installs/nvm.js +977 -26
- package/src/installs/nvm.md +1057 -0
- package/src/installs/openssh.js +734 -64
- package/src/installs/openssh.md +1056 -0
- package/src/installs/pandoc.js +644 -26
- package/src/installs/pandoc.md +1036 -0
- package/src/installs/pinentry.js +492 -26
- package/src/installs/pinentry.md +1142 -0
- package/src/installs/pngyu.js +851 -26
- package/src/installs/pngyu.md +896 -0
- package/src/installs/postman.js +781 -26
- package/src/installs/postman.md +940 -0
- package/src/installs/procps.js +425 -0
- package/src/installs/procps.md +851 -0
- package/src/installs/safari-tech-preview.js +355 -25
- package/src/installs/safari-tech-preview.md +533 -0
- package/src/installs/sfnt2woff.js +640 -26
- package/src/installs/sfnt2woff.md +795 -0
- package/src/installs/shellcheck.js +463 -26
- package/src/installs/shellcheck.md +1005 -0
- package/src/installs/slack.js +722 -25
- package/src/installs/slack.md +865 -0
- package/src/installs/snagit.js +566 -25
- package/src/installs/snagit.md +844 -0
- package/src/installs/software-properties-common.js +372 -0
- package/src/installs/software-properties-common.md +805 -0
- package/src/installs/spotify.js +858 -25
- package/src/installs/spotify.md +901 -0
- package/src/installs/studio-3t.js +803 -26
- package/src/installs/studio-3t.md +918 -0
- package/src/installs/sublime-text.js +780 -25
- package/src/installs/sublime-text.md +914 -0
- package/src/installs/superwhisper.js +687 -25
- package/src/installs/superwhisper.md +630 -0
- package/src/installs/tailscale.js +727 -26
- package/src/installs/tailscale.md +1100 -0
- package/src/installs/tar.js +389 -0
- package/src/installs/tar.md +946 -0
- package/src/installs/termius.js +780 -26
- package/src/installs/termius.md +844 -0
- package/src/installs/terraform.js +761 -26
- package/src/installs/terraform.md +899 -0
- package/src/installs/tidal.js +752 -25
- package/src/installs/tidal.md +864 -0
- package/src/installs/tmux.js +328 -26
- package/src/installs/tmux.md +1030 -0
- package/src/installs/tree.js +393 -26
- package/src/installs/tree.md +833 -0
- package/src/installs/unzip.js +460 -0
- package/src/installs/unzip.md +879 -0
- package/src/installs/vim.js +403 -26
- package/src/installs/vim.md +1040 -0
- package/src/installs/vlc.js +803 -26
- package/src/installs/vlc.md +927 -0
- package/src/installs/vscode.js +825 -26
- package/src/installs/vscode.md +1002 -0
- package/src/installs/wget.js +415 -0
- package/src/installs/wget.md +791 -0
- package/src/installs/whatsapp.js +710 -25
- package/src/installs/whatsapp.md +854 -0
- package/src/installs/winpty.js +352 -0
- package/src/installs/winpty.md +620 -0
- package/src/installs/woff2.js +535 -26
- package/src/installs/woff2.md +977 -0
- package/src/installs/wsl.js +572 -0
- package/src/installs/wsl.md +699 -0
- package/src/installs/xcode-clt.js +520 -0
- package/src/installs/xcode-clt.md +351 -0
- package/src/installs/xcode.js +542 -26
- package/src/installs/xcode.md +573 -0
- package/src/installs/yarn.js +806 -26
- package/src/installs/yarn.md +1074 -0
- package/src/installs/yq.js +636 -26
- package/src/installs/yq.md +944 -0
- package/src/installs/yt-dlp.js +683 -26
- package/src/installs/yt-dlp.md +946 -0
- package/src/installs/yum-utils.js +297 -0
- package/src/installs/yum-utils.md +648 -0
- package/src/installs/zoom.js +740 -25
- package/src/installs/zoom.md +884 -0
- package/src/scripts/README.md +567 -45
- package/src/scripts/STATUS.md +208 -0
- package/src/scripts/afk.js +395 -7
- package/src/scripts/backup-all.js +731 -9
- package/src/scripts/backup-source.js +711 -8
- package/src/scripts/brewd.js +373 -7
- package/src/scripts/brewi.js +505 -9
- package/src/scripts/brewr.js +512 -9
- package/src/scripts/brews.js +462 -9
- package/src/scripts/brewu.js +488 -7
- package/src/scripts/c.js +185 -7
- package/src/scripts/ccurl.js +325 -8
- package/src/scripts/certbot-crontab-init.js +488 -8
- package/src/scripts/certbot-init.js +641 -9
- package/src/scripts/ch.js +339 -7
- package/src/scripts/claude-danger.js +253 -8
- package/src/scripts/clean-dev.js +419 -8
- package/src/scripts/clear-dns-cache.js +525 -7
- package/src/scripts/clone.js +417 -7
- package/src/scripts/code-all.js +420 -7
- package/src/scripts/count-files.js +195 -8
- package/src/scripts/count-folders.js +195 -8
- package/src/scripts/count.js +248 -8
- package/src/scripts/d.js +203 -7
- package/src/scripts/datauri.js +373 -8
- package/src/scripts/delete-files.js +363 -7
- package/src/scripts/docker-clean.js +410 -8
- package/src/scripts/dp.js +426 -7
- package/src/scripts/e.js +375 -9
- package/src/scripts/empty-trash.js +497 -7
- package/src/scripts/evm.js +428 -9
- package/src/scripts/fetch-github-repos.js +441 -10
- package/src/scripts/get-channel.js +329 -8
- package/src/scripts/get-course.js +384 -11
- package/src/scripts/get-dependencies.js +290 -9
- package/src/scripts/get-folder.js +783 -10
- package/src/scripts/get-tunes.js +411 -10
- package/src/scripts/get-video.js +352 -9
- package/src/scripts/git-backup.js +561 -9
- package/src/scripts/git-clone.js +477 -9
- package/src/scripts/git-pup.js +303 -7
- package/src/scripts/git-push.js +380 -8
- package/src/scripts/h.js +607 -9
- package/src/scripts/hide-desktop-icons.js +483 -7
- package/src/scripts/hide-hidden-files.js +522 -7
- package/src/scripts/install-dependencies-from.js +440 -9
- package/src/scripts/ips.js +647 -10
- package/src/scripts/iso.js +354 -8
- package/src/scripts/killni.js +561 -7
- package/src/scripts/ll.js +451 -8
- package/src/scripts/local-ip.js +310 -8
- package/src/scripts/m.js +508 -8
- package/src/scripts/map.js +293 -8
- package/src/scripts/mkd.js +287 -7
- package/src/scripts/ncu-update-all.js +441 -8
- package/src/scripts/nginx-init.js +702 -12
- package/src/scripts/npmi.js +366 -7
- package/src/scripts/o.js +495 -8
- package/src/scripts/org-by-date.js +321 -7
- package/src/scripts/p.js +208 -7
- package/src/scripts/packages.js +313 -8
- package/src/scripts/path.js +209 -7
- package/src/scripts/ports.js +582 -8
- package/src/scripts/q.js +290 -8
- package/src/scripts/refresh-files.js +378 -10
- package/src/scripts/remove-smaller-files.js +500 -8
- package/src/scripts/rename-files-with-date.js +517 -9
- package/src/scripts/resize-image.js +523 -9
- package/src/scripts/rm-safe.js +653 -8
- package/src/scripts/s.js +525 -9
- package/src/scripts/set-git-public.js +349 -7
- package/src/scripts/show-desktop-icons.js +459 -7
- package/src/scripts/show-hidden-files.js +456 -7
- package/src/scripts/tpa.js +265 -8
- package/src/scripts/tpo.js +264 -7
- package/src/scripts/u.js +489 -7
- package/src/scripts/vpush.js +422 -8
- package/src/scripts/y.js +267 -7
- package/src/utils/common/os.js +94 -2
- package/src/utils/ubuntu/apt.js +13 -7
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ dev install node
|
|
|
60
60
|
dev install vscode
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
DevUtils CLI figures out the right command for macOS, Ubuntu,
|
|
63
|
+
DevUtils CLI figures out the right command for macOS, Ubuntu, Raspberry Pi OS, Amazon Linux, Windows, or Git Bash.
|
|
64
64
|
|
|
65
65
|
### Git Identity Management
|
|
66
66
|
|
|
@@ -112,11 +112,11 @@ Copy this file to a new machine and run `dev identity sync` to regenerate all yo
|
|
|
112
112
|
| Platform | Package Manager |
|
|
113
113
|
|----------|-----------------|
|
|
114
114
|
| macOS | Homebrew |
|
|
115
|
-
| Ubuntu
|
|
116
|
-
| Raspberry Pi OS | APT |
|
|
117
|
-
| Amazon Linux
|
|
115
|
+
| Ubuntu | APT / Snap |
|
|
116
|
+
| Raspberry Pi OS | APT / Snap |
|
|
117
|
+
| Amazon Linux | DNF / YUM |
|
|
118
118
|
| Windows | Chocolatey / winget |
|
|
119
|
-
|
|
|
119
|
+
| Git Bash | Manual / Portable |
|
|
120
120
|
|
|
121
121
|
## Available Commands
|
|
122
122
|
|
package/package.json
CHANGED
package/src/commands/install.js
CHANGED
|
@@ -2,7 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @fileoverview Install command - Platform-agnostic installation of development tools.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
|
+
* This command handles the installation of development tools with automatic
|
|
7
|
+
* dependency resolution. It performs the following steps for each technology:
|
|
8
|
+
*
|
|
9
|
+
* 1. Check if already installed using isInstalled() - skip if true
|
|
10
|
+
* 2. Check if eligible for current platform using isEligible() - fail if false
|
|
11
|
+
* 3. Resolve dependencies from installers.json
|
|
12
|
+
* 4. Recursively check and install dependencies first
|
|
13
|
+
* 5. Install the requested technology
|
|
14
|
+
*
|
|
15
|
+
* @module commands/install
|
|
6
16
|
*/
|
|
7
17
|
|
|
8
18
|
const { Command } = require('commander');
|
|
@@ -11,10 +21,87 @@ const path = require('path');
|
|
|
11
21
|
const readline = require('readline');
|
|
12
22
|
|
|
13
23
|
const INSTALLS_DIR = path.join(__dirname, '..', 'installs');
|
|
24
|
+
const INSTALLERS_JSON = path.join(INSTALLS_DIR, 'installers.json');
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Cache for loaded installer modules to avoid reloading
|
|
28
|
+
* @type {Map<string, object>}
|
|
29
|
+
*/
|
|
30
|
+
const installerCache = new Map();
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Cache for installer metadata from installers.json
|
|
34
|
+
* @type {Array<object>|null}
|
|
35
|
+
*/
|
|
36
|
+
let installersMetadata = null;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Load the installers.json metadata file
|
|
40
|
+
* @returns {Array<object>} Array of installer metadata objects
|
|
41
|
+
*/
|
|
42
|
+
function loadInstallersMetadata() {
|
|
43
|
+
if (installersMetadata !== null) {
|
|
44
|
+
return installersMetadata;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
if (!fs.existsSync(INSTALLERS_JSON)) {
|
|
49
|
+
console.warn('Warning: installers.json not found. Dependency resolution disabled.');
|
|
50
|
+
installersMetadata = [];
|
|
51
|
+
return installersMetadata;
|
|
52
|
+
}
|
|
53
|
+
const content = fs.readFileSync(INSTALLERS_JSON, 'utf8');
|
|
54
|
+
installersMetadata = JSON.parse(content);
|
|
55
|
+
return installersMetadata;
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.warn(`Warning: Failed to load installers.json: ${err.message}`);
|
|
58
|
+
installersMetadata = [];
|
|
59
|
+
return installersMetadata;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get metadata for a specific installer by filename
|
|
65
|
+
* @param {string} filename - The installer filename (e.g., "node.js")
|
|
66
|
+
* @returns {object|null} The installer metadata or null if not found
|
|
67
|
+
*/
|
|
68
|
+
function getInstallerMetadata(filename) {
|
|
69
|
+
const metadata = loadInstallersMetadata();
|
|
70
|
+
return metadata.find(m => m.filename === filename) || null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Load an installer module by name
|
|
75
|
+
* @param {string} name - Name of the installer (without .js extension)
|
|
76
|
+
* @returns {object|null} The installer module or null if not found
|
|
77
|
+
*/
|
|
78
|
+
function loadInstaller(name) {
|
|
79
|
+
const filename = `${name}.js`;
|
|
80
|
+
|
|
81
|
+
// Check cache first
|
|
82
|
+
if (installerCache.has(filename)) {
|
|
83
|
+
return installerCache.get(filename);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const scriptPath = path.join(INSTALLS_DIR, filename);
|
|
87
|
+
|
|
88
|
+
if (!fs.existsSync(scriptPath)) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const installer = require(scriptPath);
|
|
94
|
+
installerCache.set(filename, installer);
|
|
95
|
+
return installer;
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.error(`Error loading installer ${name}: ${err.message}`);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
14
101
|
|
|
15
102
|
/**
|
|
16
103
|
* Get list of available install scripts
|
|
17
|
-
* @returns {string[]} Array of install script names
|
|
104
|
+
* @returns {string[]} Array of install script names (without .js extension)
|
|
18
105
|
*/
|
|
19
106
|
function getAvailableInstalls() {
|
|
20
107
|
try {
|
|
@@ -48,6 +135,194 @@ function confirm(question) {
|
|
|
48
135
|
});
|
|
49
136
|
}
|
|
50
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Check if a technology is installed
|
|
140
|
+
* @param {string} name - Name of the technology (without .js extension)
|
|
141
|
+
* @returns {Promise<boolean>} True if installed, false otherwise
|
|
142
|
+
*/
|
|
143
|
+
async function checkIsInstalled(name) {
|
|
144
|
+
const installer = loadInstaller(name);
|
|
145
|
+
|
|
146
|
+
if (!installer) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Check if installer has isInstalled function
|
|
151
|
+
if (typeof installer.isInstalled !== 'function') {
|
|
152
|
+
// If no isInstalled function, assume not installed (will try to install)
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
// isInstalled may be sync or async
|
|
158
|
+
const result = installer.isInstalled();
|
|
159
|
+
if (result instanceof Promise) {
|
|
160
|
+
return await result;
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
} catch (err) {
|
|
164
|
+
// If check fails, assume not installed
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Check if a technology is eligible for the current platform
|
|
171
|
+
* @param {string} name - Name of the technology (without .js extension)
|
|
172
|
+
* @returns {boolean} True if eligible, false otherwise
|
|
173
|
+
*/
|
|
174
|
+
function checkIsEligible(name) {
|
|
175
|
+
const installer = loadInstaller(name);
|
|
176
|
+
|
|
177
|
+
if (!installer) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Check if installer has isEligible function
|
|
182
|
+
if (typeof installer.isEligible !== 'function') {
|
|
183
|
+
// If no isEligible function, assume eligible (backwards compatibility)
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
return installer.isEligible();
|
|
189
|
+
} catch (err) {
|
|
190
|
+
// If check fails, assume not eligible
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get the display name for a technology from metadata
|
|
197
|
+
* @param {string} filename - The installer filename
|
|
198
|
+
* @returns {string} The display name or the filename without extension
|
|
199
|
+
*/
|
|
200
|
+
function getDisplayName(filename) {
|
|
201
|
+
const metadata = getInstallerMetadata(filename);
|
|
202
|
+
if (metadata && metadata.name) {
|
|
203
|
+
return metadata.name;
|
|
204
|
+
}
|
|
205
|
+
return filename.replace('.js', '');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Resolve dependencies for a technology, filtering by eligibility
|
|
210
|
+
*
|
|
211
|
+
* This function recursively resolves all dependencies for a given technology,
|
|
212
|
+
* returning them in the correct installation order (dependencies first).
|
|
213
|
+
*
|
|
214
|
+
* @param {string} name - Name of the technology (without .js extension)
|
|
215
|
+
* @param {Set<string>} visited - Set of already visited technologies (for cycle detection)
|
|
216
|
+
* @param {Set<string>} installing - Set of technologies currently being resolved (for cycle detection)
|
|
217
|
+
* @param {object} options - Command options
|
|
218
|
+
* @returns {Promise<Array<{name: string, displayName: string}>>} Array of technologies to install in order
|
|
219
|
+
*/
|
|
220
|
+
async function resolveDependencies(name, visited = new Set(), installing = new Set(), options = {}) {
|
|
221
|
+
const filename = `${name}.js`;
|
|
222
|
+
const result = [];
|
|
223
|
+
|
|
224
|
+
// Detect circular dependencies
|
|
225
|
+
if (installing.has(filename)) {
|
|
226
|
+
if (options.verbose) {
|
|
227
|
+
console.log(` [Skipping circular dependency: ${name}]`);
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Skip if already processed
|
|
233
|
+
if (visited.has(filename)) {
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Mark as being processed
|
|
238
|
+
installing.add(filename);
|
|
239
|
+
|
|
240
|
+
// Get metadata for this installer
|
|
241
|
+
const metadata = getInstallerMetadata(filename);
|
|
242
|
+
const dependencies = metadata?.depends_on || [];
|
|
243
|
+
|
|
244
|
+
// Sort dependencies by priority (lower priority = install first)
|
|
245
|
+
const sortedDeps = [...dependencies].sort((a, b) => (a.priority || 0) - (b.priority || 0));
|
|
246
|
+
|
|
247
|
+
// Process each dependency
|
|
248
|
+
for (const dep of sortedDeps) {
|
|
249
|
+
const depName = dep.name.replace('.js', '');
|
|
250
|
+
|
|
251
|
+
// Check if dependency is eligible for this platform
|
|
252
|
+
if (!checkIsEligible(depName)) {
|
|
253
|
+
if (options.verbose) {
|
|
254
|
+
console.log(` [Skipping ineligible dependency: ${depName}]`);
|
|
255
|
+
}
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Check if dependency is already installed
|
|
260
|
+
const isInstalled = await checkIsInstalled(depName);
|
|
261
|
+
if (isInstalled) {
|
|
262
|
+
if (options.verbose) {
|
|
263
|
+
console.log(` [Dependency already installed: ${depName}]`);
|
|
264
|
+
}
|
|
265
|
+
visited.add(dep.name);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Recursively resolve this dependency's dependencies
|
|
270
|
+
const subDeps = await resolveDependencies(depName, visited, installing, options);
|
|
271
|
+
result.push(...subDeps);
|
|
272
|
+
|
|
273
|
+
// Add this dependency if not already visited
|
|
274
|
+
if (!visited.has(dep.name)) {
|
|
275
|
+
result.push({
|
|
276
|
+
name: depName,
|
|
277
|
+
displayName: getDisplayName(dep.name)
|
|
278
|
+
});
|
|
279
|
+
visited.add(dep.name);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Mark as processed (no longer being resolved)
|
|
284
|
+
installing.delete(filename);
|
|
285
|
+
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Install a single technology
|
|
291
|
+
* @param {string} name - Name of the technology to install
|
|
292
|
+
* @param {object} options - Command options
|
|
293
|
+
* @returns {Promise<boolean>} True if installation succeeded
|
|
294
|
+
*/
|
|
295
|
+
async function installSingle(name, options) {
|
|
296
|
+
const installer = loadInstaller(name);
|
|
297
|
+
|
|
298
|
+
if (!installer) {
|
|
299
|
+
console.error(`Error: Installer for "${name}" not found.`);
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (typeof installer.install !== 'function') {
|
|
304
|
+
console.error(`Error: Installer "${name}" does not export an install() function.`);
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
const installOptions = {
|
|
310
|
+
dryRun: options.dryRun || false,
|
|
311
|
+
verbose: options.verbose || false,
|
|
312
|
+
force: options.force || false
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
await installer.install(installOptions);
|
|
316
|
+
return true;
|
|
317
|
+
} catch (err) {
|
|
318
|
+
console.error(`Error installing ${name}: ${err.message}`);
|
|
319
|
+
if (options.verbose) {
|
|
320
|
+
console.error(err.stack);
|
|
321
|
+
}
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
51
326
|
/**
|
|
52
327
|
* List all available install scripts
|
|
53
328
|
*/
|
|
@@ -64,7 +339,11 @@ function listInstalls() {
|
|
|
64
339
|
console.log('─'.repeat(40));
|
|
65
340
|
|
|
66
341
|
for (const name of installs) {
|
|
67
|
-
|
|
342
|
+
const filename = `${name}.js`;
|
|
343
|
+
const displayName = getDisplayName(filename);
|
|
344
|
+
const eligible = checkIsEligible(name);
|
|
345
|
+
const eligibleMark = eligible ? '' : ' (not available on this platform)';
|
|
346
|
+
console.log(` ${name}${eligibleMark}`);
|
|
68
347
|
}
|
|
69
348
|
|
|
70
349
|
console.log(`\nUsage: dev install <name>`);
|
|
@@ -72,7 +351,7 @@ function listInstalls() {
|
|
|
72
351
|
}
|
|
73
352
|
|
|
74
353
|
/**
|
|
75
|
-
* Run an install
|
|
354
|
+
* Run an install with dependency resolution
|
|
76
355
|
* @param {string} name - Name of the install script
|
|
77
356
|
* @param {object} options - Command options
|
|
78
357
|
*/
|
|
@@ -85,6 +364,7 @@ async function runInstall(name, options) {
|
|
|
85
364
|
}
|
|
86
365
|
|
|
87
366
|
const scriptPath = path.join(INSTALLS_DIR, `${name}.js`);
|
|
367
|
+
const filename = `${name}.js`;
|
|
88
368
|
|
|
89
369
|
// Check if install script exists
|
|
90
370
|
if (!fs.existsSync(scriptPath)) {
|
|
@@ -93,14 +373,65 @@ async function runInstall(name, options) {
|
|
|
93
373
|
process.exit(1);
|
|
94
374
|
}
|
|
95
375
|
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
376
|
+
// Get display name for output
|
|
377
|
+
const displayName = getDisplayName(filename);
|
|
378
|
+
|
|
379
|
+
console.log(`\nChecking ${displayName}...`);
|
|
380
|
+
|
|
381
|
+
// Step 1: Check if already installed
|
|
382
|
+
const isInstalled = await checkIsInstalled(name);
|
|
383
|
+
if (isInstalled) {
|
|
384
|
+
console.log(`${displayName} is already installed.`);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Step 2: Check if eligible for this platform
|
|
389
|
+
const isEligible = checkIsEligible(name);
|
|
390
|
+
if (!isEligible) {
|
|
391
|
+
console.log(`${displayName} is not available for this platform.`);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Step 3: Resolve dependencies
|
|
396
|
+
if (options.verbose) {
|
|
397
|
+
console.log(`Resolving dependencies for ${displayName}...`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const dependencies = await resolveDependencies(name, new Set(), new Set(), options);
|
|
401
|
+
|
|
402
|
+
// Build the full installation list (dependencies + target)
|
|
403
|
+
const toInstall = [
|
|
404
|
+
...dependencies,
|
|
405
|
+
{ name, displayName }
|
|
406
|
+
];
|
|
99
407
|
|
|
100
|
-
|
|
101
|
-
|
|
408
|
+
// Remove duplicates while preserving order
|
|
409
|
+
const seen = new Set();
|
|
410
|
+
const uniqueToInstall = toInstall.filter(item => {
|
|
411
|
+
if (seen.has(item.name)) {
|
|
412
|
+
return false;
|
|
102
413
|
}
|
|
414
|
+
seen.add(item.name);
|
|
415
|
+
return true;
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Show what will be installed
|
|
419
|
+
if (uniqueToInstall.length > 1) {
|
|
420
|
+
console.log(`\nThe following will be installed:`);
|
|
421
|
+
for (const item of uniqueToInstall) {
|
|
422
|
+
console.log(` - ${item.displayName}`);
|
|
423
|
+
}
|
|
424
|
+
console.log('');
|
|
425
|
+
} else {
|
|
426
|
+
console.log(`\nPreparing to install: ${displayName}`);
|
|
427
|
+
}
|
|
103
428
|
|
|
429
|
+
if (options.dryRun) {
|
|
430
|
+
console.log('[Dry run mode - no changes will be made]\n');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Confirm installation (unless --force)
|
|
434
|
+
if (!options.force) {
|
|
104
435
|
const shouldProceed = await confirm('Proceed with installation?');
|
|
105
436
|
if (!shouldProceed) {
|
|
106
437
|
console.log('Installation cancelled.');
|
|
@@ -108,42 +439,49 @@ async function runInstall(name, options) {
|
|
|
108
439
|
}
|
|
109
440
|
}
|
|
110
441
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
442
|
+
// Install each technology in order
|
|
443
|
+
let successCount = 0;
|
|
444
|
+
let failCount = 0;
|
|
114
445
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
446
|
+
for (const item of uniqueToInstall) {
|
|
447
|
+
console.log(`\n${'─'.repeat(50)}`);
|
|
448
|
+
console.log(`Installing ${item.displayName}...`);
|
|
449
|
+
console.log('─'.repeat(50));
|
|
120
450
|
|
|
121
|
-
console.log(`\nInstalling ${name}...`);
|
|
122
451
|
if (options.verbose) {
|
|
123
|
-
console.log(`Script: ${
|
|
452
|
+
console.log(`Script: ${path.join(INSTALLS_DIR, `${item.name}.js`)}`);
|
|
124
453
|
}
|
|
125
454
|
|
|
126
|
-
|
|
127
|
-
const installOptions = {
|
|
128
|
-
dryRun: options.dryRun || false,
|
|
129
|
-
verbose: options.verbose || false,
|
|
130
|
-
force: options.force || false
|
|
131
|
-
};
|
|
455
|
+
const success = await installSingle(item.name, options);
|
|
132
456
|
|
|
133
|
-
|
|
134
|
-
|
|
457
|
+
if (success) {
|
|
458
|
+
successCount++;
|
|
459
|
+
if (!options.dryRun) {
|
|
460
|
+
console.log(`${item.displayName} installed successfully.`);
|
|
461
|
+
}
|
|
462
|
+
} else {
|
|
463
|
+
failCount++;
|
|
464
|
+
console.error(`Failed to install ${item.displayName}.`);
|
|
135
465
|
|
|
136
|
-
|
|
137
|
-
|
|
466
|
+
// Ask if user wants to continue on failure (unless --force)
|
|
467
|
+
if (!options.force && uniqueToInstall.indexOf(item) < uniqueToInstall.length - 1) {
|
|
468
|
+
const shouldContinue = await confirm('Continue with remaining installations?');
|
|
469
|
+
if (!shouldContinue) {
|
|
470
|
+
console.log('Installation cancelled.');
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
138
474
|
}
|
|
475
|
+
}
|
|
139
476
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
477
|
+
// Summary
|
|
478
|
+
console.log(`\n${'─'.repeat(50)}`);
|
|
479
|
+
console.log('Installation Summary:');
|
|
480
|
+
console.log(` Successful: ${successCount}`);
|
|
481
|
+
if (failCount > 0) {
|
|
482
|
+
console.log(` Failed: ${failCount}`);
|
|
146
483
|
}
|
|
484
|
+
console.log('');
|
|
147
485
|
}
|
|
148
486
|
|
|
149
487
|
/**
|
|
@@ -162,7 +500,7 @@ async function handleInstall(name, options) {
|
|
|
162
500
|
|
|
163
501
|
// Create and configure the command
|
|
164
502
|
const install = new Command('install')
|
|
165
|
-
.description('Platform-agnostic installation of development tools')
|
|
503
|
+
.description('Platform-agnostic installation of development tools with automatic dependency resolution')
|
|
166
504
|
.argument('[name]', 'Name of the package to install')
|
|
167
505
|
.option('--list', 'List all available install scripts')
|
|
168
506
|
.option('--dry-run', 'Show what would be executed without running')
|