@fredlackey/devutils 0.0.1
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 +156 -0
- package/bin/dev.js +16 -0
- package/files/README.md +0 -0
- package/files/claude/.claude/commands/setup-context.md +3 -0
- package/files/monorepos/_archive/README.md +36 -0
- package/files/monorepos/_legacy/README.md +36 -0
- package/files/monorepos/ai-docs/README.md +33 -0
- package/files/monorepos/apps/README.md +24 -0
- package/files/monorepos/docs/README.md +40 -0
- package/files/monorepos/packages/README.md +25 -0
- package/files/monorepos/research/README.md +29 -0
- package/files/monorepos/scripts/README.md +24 -0
- package/package.json +39 -0
- package/src/cli.js +68 -0
- package/src/commands/README.md +41 -0
- package/src/commands/configure.js +199 -0
- package/src/commands/identity.js +1630 -0
- package/src/commands/ignore.js +247 -0
- package/src/commands/install.js +173 -0
- package/src/commands/setup.js +212 -0
- package/src/commands/status.js +223 -0
- package/src/completion.js +284 -0
- package/src/constants.js +45 -0
- package/src/ignore/claude-code.txt +10 -0
- package/src/ignore/docker.txt +18 -0
- package/src/ignore/linux.txt +23 -0
- package/src/ignore/macos.txt +36 -0
- package/src/ignore/node.txt +55 -0
- package/src/ignore/terraform.txt +37 -0
- package/src/ignore/vscode.txt +18 -0
- package/src/ignore/windows.txt +35 -0
- package/src/index.js +0 -0
- package/src/installs/README.md +399 -0
- package/src/installs/adobe-creative-cloud.js +44 -0
- package/src/installs/appcleaner.js +44 -0
- package/src/installs/atomicparsley.js +44 -0
- package/src/installs/aws-cli.js +44 -0
- package/src/installs/balena-etcher.js +44 -0
- package/src/installs/bambu-studio.js +44 -0
- package/src/installs/bash-completion.js +44 -0
- package/src/installs/bash.js +44 -0
- package/src/installs/beyond-compare.js +44 -0
- package/src/installs/build-essential.js +44 -0
- package/src/installs/caffeine.js +44 -0
- package/src/installs/camtasia.js +44 -0
- package/src/installs/chatgpt.js +44 -0
- package/src/installs/chrome-canary.js +44 -0
- package/src/installs/chromium.js +44 -0
- package/src/installs/claude-code.js +44 -0
- package/src/installs/curl.js +44 -0
- package/src/installs/cursor.js +44 -0
- package/src/installs/dbschema.js +44 -0
- package/src/installs/docker.js +44 -0
- package/src/installs/drawio.js +44 -0
- package/src/installs/elmedia-player.js +44 -0
- package/src/installs/ffmpeg.js +44 -0
- package/src/installs/gemini-cli.js +44 -0
- package/src/installs/git.js +44 -0
- package/src/installs/gitego.js +44 -0
- package/src/installs/go.js +44 -0
- package/src/installs/google-chrome.js +44 -0
- package/src/installs/gpg.js +141 -0
- package/src/installs/homebrew.js +44 -0
- package/src/installs/imageoptim.js +44 -0
- package/src/installs/jq.js +44 -0
- package/src/installs/keyboard-maestro.js +44 -0
- package/src/installs/latex.js +44 -0
- package/src/installs/lftp.js +44 -0
- package/src/installs/messenger.js +44 -0
- package/src/installs/microsoft-office.js +44 -0
- package/src/installs/microsoft-teams.js +44 -0
- package/src/installs/node.js +44 -0
- package/src/installs/nordpass.js +44 -0
- package/src/installs/nvm.js +44 -0
- package/src/installs/openssh.js +134 -0
- package/src/installs/pandoc.js +44 -0
- package/src/installs/pinentry.js +44 -0
- package/src/installs/pngyu.js +44 -0
- package/src/installs/postman.js +44 -0
- package/src/installs/safari-tech-preview.js +44 -0
- package/src/installs/sfnt2woff.js +44 -0
- package/src/installs/shellcheck.js +44 -0
- package/src/installs/slack.js +44 -0
- package/src/installs/snagit.js +44 -0
- package/src/installs/spotify.js +44 -0
- package/src/installs/studio-3t.js +44 -0
- package/src/installs/sublime-text.js +44 -0
- package/src/installs/superwhisper.js +44 -0
- package/src/installs/tailscale.js +44 -0
- package/src/installs/termius.js +44 -0
- package/src/installs/terraform.js +44 -0
- package/src/installs/tidal.js +44 -0
- package/src/installs/tmux.js +44 -0
- package/src/installs/tree.js +44 -0
- package/src/installs/vim.js +44 -0
- package/src/installs/vlc.js +44 -0
- package/src/installs/vscode.js +44 -0
- package/src/installs/whatsapp.js +44 -0
- package/src/installs/woff2.js +44 -0
- package/src/installs/xcode.js +44 -0
- package/src/installs/yarn.js +44 -0
- package/src/installs/yq.js +44 -0
- package/src/installs/yt-dlp.js +44 -0
- package/src/installs/zoom.js +44 -0
- package/src/scripts/README.md +95 -0
- package/src/scripts/afk.js +23 -0
- package/src/scripts/backup-all.js +24 -0
- package/src/scripts/backup-source.js +24 -0
- package/src/scripts/brewd.js +23 -0
- package/src/scripts/brewi.js +24 -0
- package/src/scripts/brewr.js +24 -0
- package/src/scripts/brews.js +24 -0
- package/src/scripts/brewu.js +23 -0
- package/src/scripts/c.js +23 -0
- package/src/scripts/ccurl.js +24 -0
- package/src/scripts/certbot-crontab-init.js +24 -0
- package/src/scripts/certbot-init.js +25 -0
- package/src/scripts/ch.js +23 -0
- package/src/scripts/claude-danger.js +23 -0
- package/src/scripts/clean-dev.js +24 -0
- package/src/scripts/clear-dns-cache.js +23 -0
- package/src/scripts/clone.js +25 -0
- package/src/scripts/code-all.js +24 -0
- package/src/scripts/count-files.js +24 -0
- package/src/scripts/count-folders.js +24 -0
- package/src/scripts/count.js +24 -0
- package/src/scripts/d.js +23 -0
- package/src/scripts/datauri.js +24 -0
- package/src/scripts/delete-files.js +24 -0
- package/src/scripts/docker-clean.js +24 -0
- package/src/scripts/dp.js +23 -0
- package/src/scripts/e.js +24 -0
- package/src/scripts/empty-trash.js +23 -0
- package/src/scripts/evm.js +25 -0
- package/src/scripts/fetch-github-repos.js +25 -0
- package/src/scripts/get-channel.js +24 -0
- package/src/scripts/get-course.js +26 -0
- package/src/scripts/get-dependencies.js +25 -0
- package/src/scripts/get-folder.js +26 -0
- package/src/scripts/get-tunes.js +25 -0
- package/src/scripts/get-video.js +24 -0
- package/src/scripts/git-backup.js +25 -0
- package/src/scripts/git-clone.js +25 -0
- package/src/scripts/git-pup.js +23 -0
- package/src/scripts/git-push.js +24 -0
- package/src/scripts/h.js +24 -0
- package/src/scripts/hide-desktop-icons.js +23 -0
- package/src/scripts/hide-hidden-files.js +23 -0
- package/src/scripts/install-dependencies-from.js +25 -0
- package/src/scripts/ips.js +26 -0
- package/src/scripts/iso.js +24 -0
- package/src/scripts/killni.js +23 -0
- package/src/scripts/ll.js +24 -0
- package/src/scripts/local-ip.js +23 -0
- package/src/scripts/m.js +24 -0
- package/src/scripts/map.js +24 -0
- package/src/scripts/mkd.js +24 -0
- package/src/scripts/ncu-update-all.js +24 -0
- package/src/scripts/nginx-init.js +28 -0
- package/src/scripts/npmi.js +23 -0
- package/src/scripts/o.js +24 -0
- package/src/scripts/org-by-date.js +24 -0
- package/src/scripts/p.js +23 -0
- package/src/scripts/packages.js +25 -0
- package/src/scripts/path.js +23 -0
- package/src/scripts/ports.js +23 -0
- package/src/scripts/q.js +23 -0
- package/src/scripts/refresh-files.js +26 -0
- package/src/scripts/remove-smaller-files.js +24 -0
- package/src/scripts/rename-files-with-date.js +25 -0
- package/src/scripts/resize-image.js +25 -0
- package/src/scripts/rm-safe.js +24 -0
- package/src/scripts/s.js +24 -0
- package/src/scripts/set-git-public.js +23 -0
- package/src/scripts/show-desktop-icons.js +23 -0
- package/src/scripts/show-hidden-files.js +23 -0
- package/src/scripts/tpa.js +23 -0
- package/src/scripts/tpo.js +23 -0
- package/src/scripts/u.js +23 -0
- package/src/scripts/vpush.js +23 -0
- package/src/scripts/y.js +23 -0
- package/src/utils/README.md +95 -0
- package/src/utils/common/apps.js +143 -0
- package/src/utils/common/display.js +157 -0
- package/src/utils/common/network.js +185 -0
- package/src/utils/common/os.js +202 -0
- package/src/utils/common/package-manager.js +301 -0
- package/src/utils/common/privileges.js +138 -0
- package/src/utils/common/shell.js +195 -0
- package/src/utils/macos/apps.js +228 -0
- package/src/utils/macos/brew.js +315 -0
- package/src/utils/ubuntu/apt.js +301 -0
- package/src/utils/ubuntu/desktop.js +292 -0
- package/src/utils/ubuntu/snap.js +302 -0
- package/src/utils/ubuntu/systemd.js +286 -0
- package/src/utils/windows/choco.js +327 -0
- package/src/utils/windows/env.js +246 -0
- package/src/utils/windows/registry.js +269 -0
- package/src/utils/windows/shell.js +240 -0
- package/src/utils/windows/winget.js +378 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Ignore command - Append technology-specific patterns to .gitignore.
|
|
5
|
+
* Reads patterns from src/ignore/*.txt and adds them to the repository's .gitignore.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { Command } = require('commander');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const IGNORE_DIR = path.join(__dirname, '..', 'ignore');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get list of available ignore technologies
|
|
16
|
+
* @returns {string[]} Array of technology names
|
|
17
|
+
*/
|
|
18
|
+
function getAvailableTechnologies() {
|
|
19
|
+
try {
|
|
20
|
+
if (!fs.existsSync(IGNORE_DIR)) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
return fs.readdirSync(IGNORE_DIR)
|
|
24
|
+
.filter(file => file.endsWith('.txt'))
|
|
25
|
+
.map(file => file.replace('.txt', ''))
|
|
26
|
+
.sort();
|
|
27
|
+
} catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Find the git root directory by walking up the tree
|
|
34
|
+
* @param {string} startDir - Starting directory
|
|
35
|
+
* @returns {string|null} Path to git root or null
|
|
36
|
+
*/
|
|
37
|
+
function findGitRoot(startDir) {
|
|
38
|
+
let currentDir = path.resolve(startDir);
|
|
39
|
+
const root = path.parse(currentDir).root;
|
|
40
|
+
|
|
41
|
+
while (currentDir !== root) {
|
|
42
|
+
const gitDir = path.join(currentDir, '.git');
|
|
43
|
+
if (fs.existsSync(gitDir)) {
|
|
44
|
+
return currentDir;
|
|
45
|
+
}
|
|
46
|
+
currentDir = path.dirname(currentDir);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generate section markers for a technology
|
|
54
|
+
* @param {string} technology - Technology name
|
|
55
|
+
* @returns {{ start: string, end: string }}
|
|
56
|
+
*/
|
|
57
|
+
function getSectionMarkers(technology) {
|
|
58
|
+
return {
|
|
59
|
+
start: `# === @fredlackey/devutils: ${technology} ===`,
|
|
60
|
+
end: `# === end: ${technology} ===`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check if patterns for a technology are already in .gitignore
|
|
66
|
+
* @param {string} gitignoreContent - Content of .gitignore
|
|
67
|
+
* @param {string} technology - Technology name
|
|
68
|
+
* @returns {boolean}
|
|
69
|
+
*/
|
|
70
|
+
function hasPatterns(gitignoreContent, technology) {
|
|
71
|
+
const markers = getSectionMarkers(technology);
|
|
72
|
+
return gitignoreContent.includes(markers.start);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Remove existing patterns for a technology from .gitignore
|
|
77
|
+
* @param {string} gitignoreContent - Content of .gitignore
|
|
78
|
+
* @param {string} technology - Technology name
|
|
79
|
+
* @returns {string} Updated content
|
|
80
|
+
*/
|
|
81
|
+
function removePatterns(gitignoreContent, technology) {
|
|
82
|
+
const markers = getSectionMarkers(technology);
|
|
83
|
+
const lines = gitignoreContent.split('\n');
|
|
84
|
+
const result = [];
|
|
85
|
+
let inSection = false;
|
|
86
|
+
|
|
87
|
+
for (const line of lines) {
|
|
88
|
+
if (line.trim() === markers.start) {
|
|
89
|
+
inSection = true;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (line.trim() === markers.end) {
|
|
93
|
+
inSection = false;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (!inSection) {
|
|
97
|
+
result.push(line);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Remove trailing empty lines that were before the section
|
|
102
|
+
while (result.length > 0 && result[result.length - 1] === '') {
|
|
103
|
+
result.pop();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return result.join('\n');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* List all available technologies
|
|
111
|
+
*/
|
|
112
|
+
function listTechnologies() {
|
|
113
|
+
const technologies = getAvailableTechnologies();
|
|
114
|
+
|
|
115
|
+
if (technologies.length === 0) {
|
|
116
|
+
console.log('\nNo ignore patterns available.');
|
|
117
|
+
console.log(`Pattern files should be placed in: ${IGNORE_DIR}\n`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log('\nAvailable technologies:');
|
|
122
|
+
console.log('─'.repeat(40));
|
|
123
|
+
|
|
124
|
+
for (const tech of technologies) {
|
|
125
|
+
console.log(` ${tech}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log(`\nUsage: dev ignore <technology> [folder]`);
|
|
129
|
+
console.log('');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Run the ignore command
|
|
134
|
+
* @param {string} technology - Technology name
|
|
135
|
+
* @param {string} folder - Optional folder path for .gitignore
|
|
136
|
+
* @param {object} options - Command options
|
|
137
|
+
*/
|
|
138
|
+
async function runIgnore(technology, folder, options) {
|
|
139
|
+
// Handle --list option
|
|
140
|
+
if (options.list) {
|
|
141
|
+
listTechnologies();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Validate technology argument
|
|
146
|
+
if (!technology) {
|
|
147
|
+
console.error('\nError: No technology specified.');
|
|
148
|
+
console.log('Usage: dev ignore <technology> [folder]');
|
|
149
|
+
console.log('Run `dev ignore --list` to see available options.\n');
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Check if technology pattern file exists
|
|
154
|
+
const patternFile = path.join(IGNORE_DIR, `${technology}.txt`);
|
|
155
|
+
if (!fs.existsSync(patternFile)) {
|
|
156
|
+
console.error(`\nError: Unknown technology "${technology}".`);
|
|
157
|
+
console.log('Run `dev ignore --list` to see available options.\n');
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Determine target directory for .gitignore
|
|
162
|
+
let targetDir;
|
|
163
|
+
if (folder) {
|
|
164
|
+
// Use specified folder path
|
|
165
|
+
targetDir = path.resolve(folder);
|
|
166
|
+
if (!fs.existsSync(targetDir)) {
|
|
167
|
+
console.error(`\nError: Folder "${folder}" does not exist.\n`);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
if (!fs.statSync(targetDir).isDirectory()) {
|
|
171
|
+
console.error(`\nError: "${folder}" is not a directory.\n`);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
// Find git root (default behavior)
|
|
176
|
+
targetDir = findGitRoot(process.cwd());
|
|
177
|
+
if (!targetDir) {
|
|
178
|
+
console.error('\nError: No git repository found.');
|
|
179
|
+
console.log('Initialize with `git init` or specify a folder path.\n');
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
185
|
+
|
|
186
|
+
// Read existing .gitignore content
|
|
187
|
+
let gitignoreContent = '';
|
|
188
|
+
if (fs.existsSync(gitignorePath)) {
|
|
189
|
+
gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Check for existing patterns
|
|
193
|
+
if (hasPatterns(gitignoreContent, technology)) {
|
|
194
|
+
if (options.force) {
|
|
195
|
+
// Remove existing patterns to replace them
|
|
196
|
+
gitignoreContent = removePatterns(gitignoreContent, technology);
|
|
197
|
+
console.log(`Replacing existing patterns for ${technology}...`);
|
|
198
|
+
} else {
|
|
199
|
+
console.log(`\nPatterns for ${technology} already present in .gitignore`);
|
|
200
|
+
console.log('Use --force to replace existing patterns.\n');
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Read pattern file
|
|
206
|
+
const patterns = fs.readFileSync(patternFile, 'utf8');
|
|
207
|
+
const markers = getSectionMarkers(technology);
|
|
208
|
+
|
|
209
|
+
// Build the section to add
|
|
210
|
+
const section = `\n${markers.start}\n${patterns.trim()}\n${markers.end}\n`;
|
|
211
|
+
|
|
212
|
+
// Show dry run output
|
|
213
|
+
if (options.dryRun) {
|
|
214
|
+
console.log(`\n[Dry run] Would append to ${gitignorePath}:`);
|
|
215
|
+
console.log('─'.repeat(40));
|
|
216
|
+
console.log(section);
|
|
217
|
+
console.log('─'.repeat(40));
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Append to .gitignore
|
|
222
|
+
const newContent = gitignoreContent.trimEnd() + section;
|
|
223
|
+
fs.writeFileSync(gitignorePath, newContent);
|
|
224
|
+
|
|
225
|
+
console.log(`\nAdded ${technology} patterns to ${gitignorePath}`);
|
|
226
|
+
|
|
227
|
+
// Show what was added (if verbose)
|
|
228
|
+
if (options.verbose) {
|
|
229
|
+
console.log('\nPatterns added:');
|
|
230
|
+
console.log('─'.repeat(40));
|
|
231
|
+
console.log(patterns.trim());
|
|
232
|
+
console.log('');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Create and configure the command
|
|
237
|
+
const ignore = new Command('ignore')
|
|
238
|
+
.description('Append technology-specific patterns to .gitignore')
|
|
239
|
+
.argument('[technology]', 'Technology name (e.g., node, python, macos)')
|
|
240
|
+
.argument('[folder]', 'Target folder for .gitignore (defaults to git root)')
|
|
241
|
+
.option('--list', 'List all available technologies')
|
|
242
|
+
.option('--dry-run', 'Show what would be added without modifying files')
|
|
243
|
+
.option('--force', 'Re-add patterns even if section already exists')
|
|
244
|
+
.option('--verbose', 'Show detailed output')
|
|
245
|
+
.action(runIgnore);
|
|
246
|
+
|
|
247
|
+
module.exports = ignore;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Install command - Platform-agnostic installation of development tools.
|
|
5
|
+
* Locates and executes install scripts from src/installs/.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { Command } = require('commander');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const readline = require('readline');
|
|
12
|
+
|
|
13
|
+
const INSTALLS_DIR = path.join(__dirname, '..', 'installs');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get list of available install scripts
|
|
17
|
+
* @returns {string[]} Array of install script names
|
|
18
|
+
*/
|
|
19
|
+
function getAvailableInstalls() {
|
|
20
|
+
try {
|
|
21
|
+
if (!fs.existsSync(INSTALLS_DIR)) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
return fs.readdirSync(INSTALLS_DIR)
|
|
25
|
+
.filter(file => file.endsWith('.js'))
|
|
26
|
+
.map(file => file.replace('.js', ''))
|
|
27
|
+
.sort();
|
|
28
|
+
} catch {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Prompt user for confirmation
|
|
35
|
+
* @param {string} question - Question to ask
|
|
36
|
+
* @returns {Promise<boolean>}
|
|
37
|
+
*/
|
|
38
|
+
function confirm(question) {
|
|
39
|
+
return new Promise(resolve => {
|
|
40
|
+
const rl = readline.createInterface({
|
|
41
|
+
input: process.stdin,
|
|
42
|
+
output: process.stdout
|
|
43
|
+
});
|
|
44
|
+
rl.question(`${question} (y/n): `, answer => {
|
|
45
|
+
rl.close();
|
|
46
|
+
resolve(answer.toLowerCase().startsWith('y'));
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* List all available install scripts
|
|
53
|
+
*/
|
|
54
|
+
function listInstalls() {
|
|
55
|
+
const installs = getAvailableInstalls();
|
|
56
|
+
|
|
57
|
+
if (installs.length === 0) {
|
|
58
|
+
console.log('\nNo install scripts available.');
|
|
59
|
+
console.log(`Install scripts should be placed in: ${INSTALLS_DIR}\n`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log('\nAvailable install scripts:');
|
|
64
|
+
console.log('─'.repeat(40));
|
|
65
|
+
|
|
66
|
+
for (const name of installs) {
|
|
67
|
+
console.log(` ${name}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log(`\nUsage: dev install <name>`);
|
|
71
|
+
console.log('');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Run an install script
|
|
76
|
+
* @param {string} name - Name of the install script
|
|
77
|
+
* @param {object} options - Command options
|
|
78
|
+
*/
|
|
79
|
+
async function runInstall(name, options) {
|
|
80
|
+
if (!name) {
|
|
81
|
+
console.error('\nError: No package specified.');
|
|
82
|
+
console.log('Usage: dev install <name>');
|
|
83
|
+
console.log('Run `dev install --list` to see available options.\n');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const scriptPath = path.join(INSTALLS_DIR, `${name}.js`);
|
|
88
|
+
|
|
89
|
+
// Check if install script exists
|
|
90
|
+
if (!fs.existsSync(scriptPath)) {
|
|
91
|
+
console.error(`\nError: Unknown package "${name}".`);
|
|
92
|
+
console.log('Run `dev install --list` to see available options.\n');
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Confirm installation (unless --force)
|
|
97
|
+
if (!options.force) {
|
|
98
|
+
console.log(`\nPreparing to install: ${name}`);
|
|
99
|
+
|
|
100
|
+
if (options.dryRun) {
|
|
101
|
+
console.log('[Dry run mode - no changes will be made]\n');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const shouldProceed = await confirm('Proceed with installation?');
|
|
105
|
+
if (!shouldProceed) {
|
|
106
|
+
console.log('Installation cancelled.');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
// Load the install script
|
|
113
|
+
const installScript = require(scriptPath);
|
|
114
|
+
|
|
115
|
+
// Check for install function
|
|
116
|
+
if (typeof installScript.install !== 'function') {
|
|
117
|
+
console.error(`\nError: Install script "${name}" does not export an install() function.`);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log(`\nInstalling ${name}...`);
|
|
122
|
+
if (options.verbose) {
|
|
123
|
+
console.log(`Script: ${scriptPath}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Pass options to the install function
|
|
127
|
+
const installOptions = {
|
|
128
|
+
dryRun: options.dryRun || false,
|
|
129
|
+
verbose: options.verbose || false,
|
|
130
|
+
force: options.force || false
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Run the install
|
|
134
|
+
await installScript.install(installOptions);
|
|
135
|
+
|
|
136
|
+
if (!options.dryRun) {
|
|
137
|
+
console.log(`\n${name} installation complete.`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.error(`\nError installing ${name}:`, err.message);
|
|
142
|
+
if (options.verbose) {
|
|
143
|
+
console.error(err.stack);
|
|
144
|
+
}
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Handle the install command
|
|
151
|
+
* @param {string} name - Package name to install
|
|
152
|
+
* @param {object} options - Command options
|
|
153
|
+
*/
|
|
154
|
+
async function handleInstall(name, options) {
|
|
155
|
+
if (options.list) {
|
|
156
|
+
listInstalls();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
await runInstall(name, options);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Create and configure the command
|
|
164
|
+
const install = new Command('install')
|
|
165
|
+
.description('Platform-agnostic installation of development tools')
|
|
166
|
+
.argument('[name]', 'Name of the package to install')
|
|
167
|
+
.option('--list', 'List all available install scripts')
|
|
168
|
+
.option('--dry-run', 'Show what would be executed without running')
|
|
169
|
+
.option('--force', 'Skip confirmation prompts')
|
|
170
|
+
.option('--verbose', 'Show detailed output during installation')
|
|
171
|
+
.action(handleInstall);
|
|
172
|
+
|
|
173
|
+
module.exports = install;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Setup command - Install essential tools required for DevUtils CLI to function.
|
|
5
|
+
* This includes git, ssh-keygen, gpg, and other core dependencies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { Command } = require('commander');
|
|
9
|
+
const readline = require('readline');
|
|
10
|
+
|
|
11
|
+
const shell = require('../utils/common/shell');
|
|
12
|
+
const osUtils = require('../utils/common/os');
|
|
13
|
+
|
|
14
|
+
// Essential tools that DevUtils CLI requires
|
|
15
|
+
const ESSENTIAL_TOOLS = [
|
|
16
|
+
{
|
|
17
|
+
name: 'git',
|
|
18
|
+
command: 'git',
|
|
19
|
+
description: 'Version control system',
|
|
20
|
+
install: 'git'
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'ssh-keygen',
|
|
24
|
+
command: 'ssh-keygen',
|
|
25
|
+
description: 'SSH key generation',
|
|
26
|
+
install: 'openssh'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'gpg',
|
|
30
|
+
command: 'gpg',
|
|
31
|
+
description: 'GPG encryption and signing',
|
|
32
|
+
install: 'gpg'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'curl',
|
|
36
|
+
command: 'curl',
|
|
37
|
+
description: 'Data transfer tool',
|
|
38
|
+
install: 'curl'
|
|
39
|
+
}
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create readline interface for prompts
|
|
44
|
+
* @returns {readline.Interface}
|
|
45
|
+
*/
|
|
46
|
+
function createPrompt() {
|
|
47
|
+
return readline.createInterface({
|
|
48
|
+
input: process.stdin,
|
|
49
|
+
output: process.stdout
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Prompt user for confirmation
|
|
55
|
+
* @param {readline.Interface} rl - Readline interface
|
|
56
|
+
* @param {string} question - Question to ask
|
|
57
|
+
* @returns {Promise<boolean>}
|
|
58
|
+
*/
|
|
59
|
+
function confirm(rl, question) {
|
|
60
|
+
return new Promise(resolve => {
|
|
61
|
+
rl.question(`${question} (y/n): `, answer => {
|
|
62
|
+
resolve(answer.toLowerCase().startsWith('y'));
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Check which essential tools are missing
|
|
69
|
+
* @returns {Array<object>} Array of missing tools
|
|
70
|
+
*/
|
|
71
|
+
function checkMissingTools() {
|
|
72
|
+
const missing = [];
|
|
73
|
+
for (const tool of ESSENTIAL_TOOLS) {
|
|
74
|
+
if (!shell.commandExists(tool.command)) {
|
|
75
|
+
missing.push(tool);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return missing;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get all tool statuses
|
|
83
|
+
* @returns {Array<{ tool: object, installed: boolean }>}
|
|
84
|
+
*/
|
|
85
|
+
function getToolStatuses() {
|
|
86
|
+
return ESSENTIAL_TOOLS.map(tool => ({
|
|
87
|
+
tool,
|
|
88
|
+
installed: shell.commandExists(tool.command)
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Install a tool using the appropriate installer
|
|
94
|
+
* @param {object} tool - Tool definition
|
|
95
|
+
* @returns {Promise<boolean>} True if installation succeeded
|
|
96
|
+
*/
|
|
97
|
+
async function installTool(tool) {
|
|
98
|
+
try {
|
|
99
|
+
const installer = require(`../installs/${tool.install}`);
|
|
100
|
+
if (typeof installer.install === 'function') {
|
|
101
|
+
return await installer.install();
|
|
102
|
+
}
|
|
103
|
+
console.error(` No install function found for ${tool.name}`);
|
|
104
|
+
return false;
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.error(` Failed to install ${tool.name}: ${err.message}`);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Display tool status table
|
|
113
|
+
* @param {Array<{ tool: object, installed: boolean }>} statuses
|
|
114
|
+
*/
|
|
115
|
+
function displayToolStatus(statuses) {
|
|
116
|
+
console.log('\nEssential Tools:');
|
|
117
|
+
console.log('─'.repeat(50));
|
|
118
|
+
|
|
119
|
+
for (const { tool, installed } of statuses) {
|
|
120
|
+
const status = installed ? '✓ Installed' : '✗ Missing';
|
|
121
|
+
const statusColor = installed ? status : status;
|
|
122
|
+
console.log(` ${tool.name.padEnd(15)} ${statusColor.padEnd(15)} ${tool.description}`);
|
|
123
|
+
}
|
|
124
|
+
console.log('');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Run the setup command
|
|
129
|
+
* @param {object} options - Command options
|
|
130
|
+
*/
|
|
131
|
+
async function runSetup(options) {
|
|
132
|
+
const platform = osUtils.detect();
|
|
133
|
+
|
|
134
|
+
console.log('\n=== DevUtils CLI Setup ===\n');
|
|
135
|
+
console.log(`Platform: ${platform.type}`);
|
|
136
|
+
console.log(`Package Manager: ${platform.packageManager || 'unknown'}`);
|
|
137
|
+
|
|
138
|
+
// Check tool statuses
|
|
139
|
+
const statuses = getToolStatuses();
|
|
140
|
+
displayToolStatus(statuses);
|
|
141
|
+
|
|
142
|
+
// Find missing tools
|
|
143
|
+
const missing = statuses.filter(s => !s.installed).map(s => s.tool);
|
|
144
|
+
|
|
145
|
+
if (missing.length === 0) {
|
|
146
|
+
console.log('All essential tools are already installed.\n');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
console.log(`Missing ${missing.length} tool(s): ${missing.map(t => t.name).join(', ')}\n`);
|
|
151
|
+
|
|
152
|
+
// Confirm installation unless --force
|
|
153
|
+
let shouldInstall = options.force;
|
|
154
|
+
if (!shouldInstall) {
|
|
155
|
+
const rl = createPrompt();
|
|
156
|
+
shouldInstall = await confirm(rl, 'Install missing tools?');
|
|
157
|
+
rl.close();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!shouldInstall) {
|
|
161
|
+
console.log('Setup cancelled.\n');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Install each missing tool
|
|
166
|
+
console.log('\nInstalling missing tools...\n');
|
|
167
|
+
|
|
168
|
+
let installed = 0;
|
|
169
|
+
let failed = 0;
|
|
170
|
+
|
|
171
|
+
for (const tool of missing) {
|
|
172
|
+
console.log(`Installing ${tool.name}...`);
|
|
173
|
+
const success = await installTool(tool);
|
|
174
|
+
if (success) {
|
|
175
|
+
installed++;
|
|
176
|
+
console.log(` ${tool.name} installed successfully.\n`);
|
|
177
|
+
} else {
|
|
178
|
+
failed++;
|
|
179
|
+
console.log(` ${tool.name} installation failed.\n`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Show final status
|
|
184
|
+
console.log('─'.repeat(50));
|
|
185
|
+
console.log(`\nSetup complete: ${installed} installed, ${failed} failed.\n`);
|
|
186
|
+
|
|
187
|
+
// Show updated status
|
|
188
|
+
if (installed > 0) {
|
|
189
|
+
const updatedStatuses = getToolStatuses();
|
|
190
|
+
displayToolStatus(updatedStatuses);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Create and configure the command
|
|
195
|
+
const setup = new Command('setup')
|
|
196
|
+
.description('Install essential tools required for DevUtils CLI')
|
|
197
|
+
.option('--force', 'Install without prompting')
|
|
198
|
+
.option('--check', 'Check tool status without installing')
|
|
199
|
+
.action(async (options) => {
|
|
200
|
+
if (options.check) {
|
|
201
|
+
const statuses = getToolStatuses();
|
|
202
|
+
displayToolStatus(statuses);
|
|
203
|
+
const missing = statuses.filter(s => !s.installed);
|
|
204
|
+
if (missing.length > 0) {
|
|
205
|
+
console.log(`Run 'dev setup' to install missing tools.\n`);
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
await runSetup(options);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
module.exports = setup;
|