@fredlackey/devutils 0.0.1 → 0.0.3
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/src/utils/windows/choco.js +82 -26
- package/src/utils/windows/winget.js +89 -27
|
@@ -1,24 +1,746 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* backup-all - Backup multiple user directories using rsync
|
|
5
|
+
*
|
|
6
|
+
* Migrated from legacy dotfiles alias.
|
|
7
|
+
* Original:
|
|
8
|
+
* backup-all(){
|
|
9
|
+
* excludes=".terraform,.android,.atom,.bash_sessions,bower_components,.cache,.cups,.dropbox,.DS_Store,.git,_gsdata_,.idea,node_modules,.next,.npm,.nvm,\$RECYCLE.BIN,System\ Volume\ Information,.TemporaryItems,.Trash,.Trashes,.tmp,.viminfo"
|
|
10
|
+
*
|
|
11
|
+
* backupdir="$*"
|
|
12
|
+
* backupcmd="rsync -arv --progress --no-links --exclude={$excludes} ~/Downloads $backupdir"
|
|
13
|
+
* eval "$backupcmd"
|
|
14
|
+
*
|
|
15
|
+
* backupdir="$*$(date +"%Y%m%d%H%M%S")/"
|
|
16
|
+
* backupcmd="rsync -arv --progress --no-links --exclude={$excludes} ~/Backups ~/Desktop ~/Documents ~/Microsoft ~/Movies ~/Music ~/Pictures ~/Public ~/Source ~/Templates ~/Temporary ~/Videos $backupdir"
|
|
17
|
+
* mkdir -p "$backupdir"
|
|
18
|
+
* eval "$backupcmd"
|
|
19
|
+
*
|
|
20
|
+
* cd "$backupdir"
|
|
21
|
+
* ls -la
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
5
24
|
* @module scripts/backup-all
|
|
6
25
|
*/
|
|
7
26
|
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
const os = require('../utils/common/os');
|
|
30
|
+
const shell = require('../utils/common/shell');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Directories and files to exclude from backup.
|
|
34
|
+
* These are common development artifacts, caches, and system files
|
|
35
|
+
* that should not be included in user backups.
|
|
36
|
+
*/
|
|
37
|
+
const EXCLUDES = [
|
|
38
|
+
'.terraform',
|
|
39
|
+
'.android',
|
|
40
|
+
'.atom',
|
|
41
|
+
'.bash_sessions',
|
|
42
|
+
'bower_components',
|
|
43
|
+
'.cache',
|
|
44
|
+
'.cups',
|
|
45
|
+
'.dropbox',
|
|
46
|
+
'.DS_Store',
|
|
47
|
+
'.git',
|
|
48
|
+
'_gsdata_',
|
|
49
|
+
'.idea',
|
|
50
|
+
'node_modules',
|
|
51
|
+
'.next',
|
|
52
|
+
'.npm',
|
|
53
|
+
'.nvm',
|
|
54
|
+
'$RECYCLE.BIN',
|
|
55
|
+
'System Volume Information',
|
|
56
|
+
'.TemporaryItems',
|
|
57
|
+
'.Trash',
|
|
58
|
+
'.Trashes',
|
|
59
|
+
'.tmp',
|
|
60
|
+
'.viminfo'
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* User directories to back up with a timestamp.
|
|
65
|
+
* These are common user folders that typically contain important data.
|
|
66
|
+
* Only directories that exist will be backed up.
|
|
67
|
+
*/
|
|
68
|
+
const TIMESTAMPED_DIRECTORIES = [
|
|
69
|
+
'Backups',
|
|
70
|
+
'Desktop',
|
|
71
|
+
'Documents',
|
|
72
|
+
'Microsoft',
|
|
73
|
+
'Movies',
|
|
74
|
+
'Music',
|
|
75
|
+
'Pictures',
|
|
76
|
+
'Public',
|
|
77
|
+
'Source',
|
|
78
|
+
'Templates',
|
|
79
|
+
'Temporary',
|
|
80
|
+
'Videos'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Generates a timestamp string in YYYYMMDDHHmmss format.
|
|
85
|
+
* This format is sortable and filesystem-safe.
|
|
86
|
+
*
|
|
87
|
+
* @returns {string} Timestamp string like "20250107143052"
|
|
88
|
+
*/
|
|
89
|
+
function getTimestamp() {
|
|
90
|
+
const now = new Date();
|
|
91
|
+
const year = now.getFullYear();
|
|
92
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
93
|
+
const day = String(now.getDate()).padStart(2, '0');
|
|
94
|
+
const hour = String(now.getHours()).padStart(2, '0');
|
|
95
|
+
const minute = String(now.getMinutes()).padStart(2, '0');
|
|
96
|
+
const second = String(now.getSeconds()).padStart(2, '0');
|
|
97
|
+
return `${year}${month}${day}${hour}${minute}${second}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Checks if rsync is available on the system.
|
|
102
|
+
*
|
|
103
|
+
* @returns {boolean} True if rsync command exists, false otherwise
|
|
104
|
+
*/
|
|
105
|
+
function isRsyncAvailable() {
|
|
106
|
+
return shell.commandExists('rsync');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Checks if robocopy is available on the system (Windows).
|
|
111
|
+
*
|
|
112
|
+
* @returns {boolean} True if robocopy command exists, false otherwise
|
|
113
|
+
*/
|
|
114
|
+
function isRobocopyAvailable() {
|
|
115
|
+
return shell.commandExists('robocopy');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Lists the contents of a directory and prints to console.
|
|
120
|
+
* Uses Node.js fs module for cross-platform compatibility.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} dirPath - The directory to list
|
|
123
|
+
* @returns {void}
|
|
124
|
+
*/
|
|
125
|
+
function listDirectory(dirPath) {
|
|
126
|
+
try {
|
|
127
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
128
|
+
|
|
129
|
+
console.log(`\nContents of ${dirPath}:`);
|
|
130
|
+
console.log('-'.repeat(60));
|
|
131
|
+
|
|
132
|
+
for (const entry of entries) {
|
|
133
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
134
|
+
try {
|
|
135
|
+
const stats = fs.statSync(fullPath);
|
|
136
|
+
const type = entry.isDirectory() ? 'd' : '-';
|
|
137
|
+
const size = entry.isDirectory() ? '<DIR>' : formatBytes(stats.size);
|
|
138
|
+
const mtime = stats.mtime.toISOString().slice(0, 19).replace('T', ' ');
|
|
139
|
+
console.log(`${type} ${mtime} ${size.padStart(12)} ${entry.name}`);
|
|
140
|
+
} catch {
|
|
141
|
+
console.log(`? ${'?'.padStart(19)} ${'?'.padStart(12)} ${entry.name}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.error(`Could not list directory: ${err.message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Formats bytes into human-readable size.
|
|
151
|
+
*
|
|
152
|
+
* @param {number} bytes - Number of bytes
|
|
153
|
+
* @returns {string} Formatted size string like "1.5 MB"
|
|
154
|
+
*/
|
|
155
|
+
function formatBytes(bytes) {
|
|
156
|
+
if (bytes === 0) return '0 B';
|
|
157
|
+
const k = 1024;
|
|
158
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
159
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
160
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Gets the list of existing directories from the home folder.
|
|
165
|
+
* Only returns directories that actually exist on the system.
|
|
166
|
+
*
|
|
167
|
+
* @param {string[]} dirNames - Array of directory names to check
|
|
168
|
+
* @returns {string[]} Array of full paths to existing directories
|
|
169
|
+
*/
|
|
170
|
+
function getExistingDirectories(dirNames) {
|
|
171
|
+
const homeDir = os.getHomeDir();
|
|
172
|
+
const existing = [];
|
|
173
|
+
|
|
174
|
+
for (const dirName of dirNames) {
|
|
175
|
+
const fullPath = path.join(homeDir, dirName);
|
|
176
|
+
try {
|
|
177
|
+
const stats = fs.statSync(fullPath);
|
|
178
|
+
if (stats.isDirectory()) {
|
|
179
|
+
existing.push(fullPath);
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
// Directory doesn't exist, skip it
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return existing;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Builds rsync exclude arguments from the EXCLUDES array.
|
|
191
|
+
*
|
|
192
|
+
* @returns {string} String of --exclude arguments for rsync
|
|
193
|
+
*/
|
|
194
|
+
function buildRsyncExcludes() {
|
|
195
|
+
return EXCLUDES.map(e => `--exclude='${e}'`).join(' ');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Builds robocopy exclude arguments from the EXCLUDES array.
|
|
200
|
+
* Robocopy uses /XD for directories and /XF for files.
|
|
201
|
+
*
|
|
202
|
+
* @returns {string} String of /XD and /XF arguments for robocopy
|
|
203
|
+
*/
|
|
204
|
+
function buildRobocopyExcludes() {
|
|
205
|
+
// Robocopy needs separate /XD for directories and /XF for files
|
|
206
|
+
// Most of our excludes are directories
|
|
207
|
+
const dirExcludes = EXCLUDES.map(e => `"${e}"`).join(' ');
|
|
208
|
+
return `/XD ${dirExcludes}`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Backs up directories using rsync (Unix/macOS/Linux).
|
|
213
|
+
* Uses rsync with archive mode, verbose output, progress display,
|
|
214
|
+
* and excludes symbolic links.
|
|
215
|
+
*
|
|
216
|
+
* @param {string[]} sourceDirs - Array of source directory paths
|
|
217
|
+
* @param {string} destDir - Destination directory path
|
|
218
|
+
* @returns {Promise<boolean>} True if backup succeeded, false otherwise
|
|
219
|
+
*/
|
|
220
|
+
async function backupWithRsync(sourceDirs, destDir) {
|
|
221
|
+
const excludes = buildRsyncExcludes();
|
|
222
|
+
const sources = sourceDirs.map(d => `"${d}"`).join(' ');
|
|
223
|
+
|
|
224
|
+
// Create destination directory if it doesn't exist
|
|
225
|
+
try {
|
|
226
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
227
|
+
} catch (err) {
|
|
228
|
+
console.error(`Failed to create destination directory: ${err.message}`);
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const cmd = `rsync -arv --progress --no-links ${excludes} ${sources} "${destDir}"`;
|
|
233
|
+
|
|
234
|
+
console.log(`\nBacking up to: ${destDir}`);
|
|
235
|
+
console.log('Running rsync... (this may take a while)\n');
|
|
236
|
+
|
|
237
|
+
const result = await shell.exec(cmd, { timeout: 0 }); // No timeout for large backups
|
|
238
|
+
|
|
239
|
+
if (result.code !== 0) {
|
|
240
|
+
console.error(`rsync failed with code ${result.code}`);
|
|
241
|
+
if (result.stderr) {
|
|
242
|
+
console.error(result.stderr);
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Print rsync output (includes file list and summary)
|
|
248
|
+
if (result.stdout) {
|
|
249
|
+
console.log(result.stdout);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Backs up directories using robocopy (Windows).
|
|
257
|
+
* Uses robocopy with mirror mode and progress display.
|
|
258
|
+
*
|
|
259
|
+
* @param {string[]} sourceDirs - Array of source directory paths
|
|
260
|
+
* @param {string} destDir - Destination directory path
|
|
261
|
+
* @returns {Promise<boolean>} True if backup succeeded, false otherwise
|
|
262
|
+
*/
|
|
263
|
+
async function backupWithRobocopy(sourceDirs, destDir) {
|
|
264
|
+
const excludes = buildRobocopyExcludes();
|
|
265
|
+
|
|
266
|
+
// Create destination directory if it doesn't exist
|
|
267
|
+
try {
|
|
268
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
269
|
+
} catch (err) {
|
|
270
|
+
console.error(`Failed to create destination directory: ${err.message}`);
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
console.log(`\nBacking up to: ${destDir}`);
|
|
275
|
+
console.log('Running robocopy... (this may take a while)\n');
|
|
276
|
+
|
|
277
|
+
// Robocopy needs to be called for each source directory
|
|
278
|
+
for (const sourceDir of sourceDirs) {
|
|
279
|
+
const dirName = path.basename(sourceDir);
|
|
280
|
+
const targetDir = path.join(destDir, dirName);
|
|
281
|
+
|
|
282
|
+
// /E = copy subdirectories including empty ones
|
|
283
|
+
// /R:3 = retry 3 times on failed copies
|
|
284
|
+
// /W:5 = wait 5 seconds between retries
|
|
285
|
+
// /NP = no progress (percentage) - can cause issues with output
|
|
286
|
+
// /XJ = exclude junction points (similar to --no-links in rsync)
|
|
287
|
+
const cmd = `robocopy "${sourceDir}" "${targetDir}" /E /R:3 /W:5 /XJ ${excludes}`;
|
|
288
|
+
|
|
289
|
+
console.log(`Copying: ${sourceDir} -> ${targetDir}`);
|
|
290
|
+
|
|
291
|
+
const result = await shell.exec(cmd, { timeout: 0 });
|
|
292
|
+
|
|
293
|
+
// Robocopy uses exit codes differently:
|
|
294
|
+
// 0 = No files copied, no errors
|
|
295
|
+
// 1 = Files copied successfully
|
|
296
|
+
// 2 = Extra files or directories detected
|
|
297
|
+
// 4 = Some mismatched files detected
|
|
298
|
+
// 8+ = Errors occurred
|
|
299
|
+
if (result.code >= 8) {
|
|
300
|
+
console.error(`robocopy failed for ${sourceDir} with code ${result.code}`);
|
|
301
|
+
if (result.stderr) {
|
|
302
|
+
console.error(result.stderr);
|
|
303
|
+
}
|
|
304
|
+
// Continue with other directories instead of failing completely
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
|
|
8
311
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
312
|
+
* Pure Node.js backup implementation (fallback when no system tools available).
|
|
313
|
+
* This is a simple recursive copy that respects the exclude list.
|
|
314
|
+
* Note: This is significantly slower than rsync/robocopy for large directories.
|
|
11
315
|
*
|
|
12
|
-
* @param {string[]}
|
|
13
|
-
* @param {string}
|
|
316
|
+
* @param {string[]} sourceDirs - Array of source directory paths
|
|
317
|
+
* @param {string} destDir - Destination directory path
|
|
318
|
+
* @returns {Promise<boolean>} True if backup succeeded, false otherwise
|
|
319
|
+
*/
|
|
320
|
+
async function backupWithNodeJS(sourceDirs, destDir) {
|
|
321
|
+
console.log(`\nBacking up to: ${destDir}`);
|
|
322
|
+
console.log('Using Node.js copy (no rsync/robocopy available)...');
|
|
323
|
+
console.log('Note: This may be slower than native backup tools.\n');
|
|
324
|
+
|
|
325
|
+
// Create destination directory
|
|
326
|
+
try {
|
|
327
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
328
|
+
} catch (err) {
|
|
329
|
+
console.error(`Failed to create destination directory: ${err.message}`);
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
let totalFiles = 0;
|
|
334
|
+
let copiedFiles = 0;
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Recursively copies a directory while respecting excludes.
|
|
338
|
+
*
|
|
339
|
+
* @param {string} src - Source path
|
|
340
|
+
* @param {string} dest - Destination path
|
|
341
|
+
*/
|
|
342
|
+
function copyRecursive(src, dest) {
|
|
343
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
344
|
+
|
|
345
|
+
for (const entry of entries) {
|
|
346
|
+
// Check if this entry should be excluded
|
|
347
|
+
if (EXCLUDES.includes(entry.name)) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const srcPath = path.join(src, entry.name);
|
|
352
|
+
const destPath = path.join(dest, entry.name);
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
if (entry.isDirectory()) {
|
|
356
|
+
// Skip symbolic links (equivalent to rsync --no-links)
|
|
357
|
+
const stats = fs.lstatSync(srcPath);
|
|
358
|
+
if (stats.isSymbolicLink()) {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
fs.mkdirSync(destPath, { recursive: true });
|
|
363
|
+
copyRecursive(srcPath, destPath);
|
|
364
|
+
} else if (entry.isFile()) {
|
|
365
|
+
// Skip symbolic links
|
|
366
|
+
const stats = fs.lstatSync(srcPath);
|
|
367
|
+
if (stats.isSymbolicLink()) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
totalFiles++;
|
|
372
|
+
fs.copyFileSync(srcPath, destPath);
|
|
373
|
+
copiedFiles++;
|
|
374
|
+
|
|
375
|
+
// Show progress every 100 files
|
|
376
|
+
if (copiedFiles % 100 === 0) {
|
|
377
|
+
console.log(`Copied ${copiedFiles} files...`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
} catch (err) {
|
|
381
|
+
console.error(`Warning: Could not copy ${srcPath}: ${err.message}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
for (const sourceDir of sourceDirs) {
|
|
387
|
+
const dirName = path.basename(sourceDir);
|
|
388
|
+
const targetDir = path.join(destDir, dirName);
|
|
389
|
+
|
|
390
|
+
console.log(`Copying: ${sourceDir}`);
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
394
|
+
copyRecursive(sourceDir, targetDir);
|
|
395
|
+
} catch (err) {
|
|
396
|
+
console.error(`Warning: Could not backup ${sourceDir}: ${err.message}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
console.log(`\nBackup complete: ${copiedFiles} files copied.`);
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Backs up user directories on macOS using rsync.
|
|
406
|
+
* Rsync is pre-installed on macOS and provides efficient incremental backups.
|
|
407
|
+
*
|
|
408
|
+
* @param {string[]} args - Command line arguments (destination path)
|
|
409
|
+
* @returns {Promise<void>}
|
|
410
|
+
*/
|
|
411
|
+
async function do_backup_all_macos(args) {
|
|
412
|
+
if (!isRsyncAvailable()) {
|
|
413
|
+
console.error('Error: rsync is not available.');
|
|
414
|
+
console.error('rsync should be pre-installed on macOS.');
|
|
415
|
+
console.error('If missing, install with: brew install rsync');
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
await do_backup_all_rsync(args);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Backs up user directories on Ubuntu using rsync.
|
|
424
|
+
* Rsync is typically pre-installed on Ubuntu.
|
|
425
|
+
*
|
|
426
|
+
* @param {string[]} args - Command line arguments (destination path)
|
|
427
|
+
* @returns {Promise<void>}
|
|
428
|
+
*/
|
|
429
|
+
async function do_backup_all_ubuntu(args) {
|
|
430
|
+
if (!isRsyncAvailable()) {
|
|
431
|
+
console.error('Error: rsync is not available.');
|
|
432
|
+
console.error('Install it with: sudo apt-get install rsync');
|
|
433
|
+
process.exit(1);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
await do_backup_all_rsync(args);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Backs up user directories on Raspberry Pi OS using rsync.
|
|
441
|
+
* Rsync is typically pre-installed on Raspberry Pi OS.
|
|
442
|
+
*
|
|
443
|
+
* @param {string[]} args - Command line arguments (destination path)
|
|
444
|
+
* @returns {Promise<void>}
|
|
445
|
+
*/
|
|
446
|
+
async function do_backup_all_raspbian(args) {
|
|
447
|
+
if (!isRsyncAvailable()) {
|
|
448
|
+
console.error('Error: rsync is not available.');
|
|
449
|
+
console.error('Install it with: sudo apt-get install rsync');
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
await do_backup_all_rsync(args);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Backs up user directories on Amazon Linux using rsync.
|
|
458
|
+
* Rsync is typically pre-installed on Amazon Linux.
|
|
459
|
+
*
|
|
460
|
+
* @param {string[]} args - Command line arguments (destination path)
|
|
14
461
|
* @returns {Promise<void>}
|
|
15
462
|
*/
|
|
16
|
-
async function
|
|
17
|
-
|
|
463
|
+
async function do_backup_all_amazon_linux(args) {
|
|
464
|
+
if (!isRsyncAvailable()) {
|
|
465
|
+
console.error('Error: rsync is not available.');
|
|
466
|
+
console.error('Install it with: sudo yum install rsync');
|
|
467
|
+
console.error('Or on Amazon Linux 2023: sudo dnf install rsync');
|
|
468
|
+
process.exit(1);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
await do_backup_all_rsync(args);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Backs up user directories on Windows using robocopy.
|
|
476
|
+
* Robocopy is built into Windows and provides robust file copying.
|
|
477
|
+
*
|
|
478
|
+
* @param {string[]} args - Command line arguments (destination path)
|
|
479
|
+
* @returns {Promise<void>}
|
|
480
|
+
*/
|
|
481
|
+
async function do_backup_all_cmd(args) {
|
|
482
|
+
await do_backup_all_windows(args);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Backs up user directories on Windows PowerShell using robocopy.
|
|
487
|
+
* Robocopy is built into Windows and provides robust file copying.
|
|
488
|
+
*
|
|
489
|
+
* @param {string[]} args - Command line arguments (destination path)
|
|
490
|
+
* @returns {Promise<void>}
|
|
491
|
+
*/
|
|
492
|
+
async function do_backup_all_powershell(args) {
|
|
493
|
+
await do_backup_all_windows(args);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Backs up user directories in Git Bash using rsync if available,
|
|
498
|
+
* otherwise falls back to robocopy via PowerShell.
|
|
499
|
+
*
|
|
500
|
+
* @param {string[]} args - Command line arguments (destination path)
|
|
501
|
+
* @returns {Promise<void>}
|
|
502
|
+
*/
|
|
503
|
+
async function do_backup_all_gitbash(args) {
|
|
504
|
+
// Git Bash may have rsync if installed via pacman/mingw
|
|
505
|
+
if (isRsyncAvailable()) {
|
|
506
|
+
await do_backup_all_rsync(args);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Fall back to Windows robocopy
|
|
511
|
+
await do_backup_all_windows(args);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Core rsync-based backup implementation used by Unix-like systems.
|
|
516
|
+
* This function handles the two-phase backup:
|
|
517
|
+
* 1. Downloads folder backed up without timestamp
|
|
518
|
+
* 2. Other user directories backed up to timestamped folder
|
|
519
|
+
*
|
|
520
|
+
* @param {string[]} args - Command line arguments (destination path)
|
|
521
|
+
* @returns {Promise<void>}
|
|
522
|
+
*/
|
|
523
|
+
async function do_backup_all_rsync(args) {
|
|
524
|
+
if (args.length === 0) {
|
|
525
|
+
console.error('Usage: backup-all <destination-directory>');
|
|
526
|
+
console.error('');
|
|
527
|
+
console.error('Example: backup-all /Volumes/Backup/');
|
|
528
|
+
console.error(' backup-all /mnt/backup/');
|
|
529
|
+
process.exit(1);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const destBase = args.join(' '); // Handle paths with spaces
|
|
533
|
+
const homeDir = os.getHomeDir();
|
|
534
|
+
|
|
535
|
+
// Phase 1: Backup Downloads to destination (no timestamp)
|
|
536
|
+
// This allows Downloads to be overwritten/updated on each backup
|
|
537
|
+
const downloadsPath = path.join(homeDir, 'Downloads');
|
|
538
|
+
if (fs.existsSync(downloadsPath)) {
|
|
539
|
+
console.log('='.repeat(60));
|
|
540
|
+
console.log('Phase 1: Backing up Downloads (no timestamp)');
|
|
541
|
+
console.log('='.repeat(60));
|
|
542
|
+
await backupWithRsync([downloadsPath], destBase);
|
|
543
|
+
} else {
|
|
544
|
+
console.log('Skipping Downloads (directory not found)');
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Phase 2: Backup other directories to timestamped folder
|
|
548
|
+
const timestamp = getTimestamp();
|
|
549
|
+
const timestampedDest = path.join(destBase, timestamp);
|
|
550
|
+
const existingDirs = getExistingDirectories(TIMESTAMPED_DIRECTORIES);
|
|
551
|
+
|
|
552
|
+
if (existingDirs.length === 0) {
|
|
553
|
+
console.log('\nNo user directories found to backup.');
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
console.log('\n' + '='.repeat(60));
|
|
558
|
+
console.log(`Phase 2: Backing up ${existingDirs.length} directories (with timestamp)`);
|
|
559
|
+
console.log('='.repeat(60));
|
|
560
|
+
console.log(`Directories: ${existingDirs.map(d => path.basename(d)).join(', ')}`);
|
|
561
|
+
|
|
562
|
+
const success = await backupWithRsync(existingDirs, timestampedDest);
|
|
563
|
+
|
|
564
|
+
if (success) {
|
|
565
|
+
console.log('\n' + '='.repeat(60));
|
|
566
|
+
console.log('Backup completed successfully!');
|
|
567
|
+
console.log('='.repeat(60));
|
|
568
|
+
listDirectory(timestampedDest);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Core Windows backup implementation using robocopy.
|
|
574
|
+
* Handles the two-phase backup process using robocopy instead of rsync.
|
|
575
|
+
*
|
|
576
|
+
* @param {string[]} args - Command line arguments (destination path)
|
|
577
|
+
* @returns {Promise<void>}
|
|
578
|
+
*/
|
|
579
|
+
async function do_backup_all_windows(args) {
|
|
580
|
+
if (args.length === 0) {
|
|
581
|
+
console.error('Usage: backup-all <destination-directory>');
|
|
582
|
+
console.error('');
|
|
583
|
+
console.error('Example: backup-all D:\\Backup');
|
|
584
|
+
console.error(' backup-all "E:\\My Backups"');
|
|
585
|
+
process.exit(1);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const destBase = args.join(' ');
|
|
589
|
+
const homeDir = os.getHomeDir();
|
|
590
|
+
|
|
591
|
+
// Check for robocopy
|
|
592
|
+
if (!isRobocopyAvailable()) {
|
|
593
|
+
console.log('Warning: robocopy not found. Using Node.js fallback (slower).');
|
|
594
|
+
await do_backup_all_nodejs(args);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Phase 1: Backup Downloads to destination (no timestamp)
|
|
599
|
+
const downloadsPath = path.join(homeDir, 'Downloads');
|
|
600
|
+
if (fs.existsSync(downloadsPath)) {
|
|
601
|
+
console.log('='.repeat(60));
|
|
602
|
+
console.log('Phase 1: Backing up Downloads (no timestamp)');
|
|
603
|
+
console.log('='.repeat(60));
|
|
604
|
+
await backupWithRobocopy([downloadsPath], destBase);
|
|
605
|
+
} else {
|
|
606
|
+
console.log('Skipping Downloads (directory not found)');
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Phase 2: Backup other directories to timestamped folder
|
|
610
|
+
const timestamp = getTimestamp();
|
|
611
|
+
const timestampedDest = path.join(destBase, timestamp);
|
|
612
|
+
const existingDirs = getExistingDirectories(TIMESTAMPED_DIRECTORIES);
|
|
613
|
+
|
|
614
|
+
if (existingDirs.length === 0) {
|
|
615
|
+
console.log('\nNo user directories found to backup.');
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
console.log('\n' + '='.repeat(60));
|
|
620
|
+
console.log(`Phase 2: Backing up ${existingDirs.length} directories (with timestamp)`);
|
|
621
|
+
console.log('='.repeat(60));
|
|
622
|
+
console.log(`Directories: ${existingDirs.map(d => path.basename(d)).join(', ')}`);
|
|
623
|
+
|
|
624
|
+
const success = await backupWithRobocopy(existingDirs, timestampedDest);
|
|
625
|
+
|
|
626
|
+
if (success) {
|
|
627
|
+
console.log('\n' + '='.repeat(60));
|
|
628
|
+
console.log('Backup completed successfully!');
|
|
629
|
+
console.log('='.repeat(60));
|
|
630
|
+
listDirectory(timestampedDest);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Pure Node.js backup implementation for when no native tools are available.
|
|
636
|
+
* This is a fallback that works on any platform but may be slower.
|
|
637
|
+
*
|
|
638
|
+
* @param {string[]} args - Command line arguments (destination path)
|
|
639
|
+
* @returns {Promise<void>}
|
|
640
|
+
*/
|
|
641
|
+
async function do_backup_all_nodejs(args) {
|
|
642
|
+
if (args.length === 0) {
|
|
643
|
+
console.error('Usage: backup-all <destination-directory>');
|
|
644
|
+
console.error('');
|
|
645
|
+
console.error('Example: backup-all /path/to/backup');
|
|
646
|
+
process.exit(1);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const destBase = args.join(' ');
|
|
650
|
+
const homeDir = os.getHomeDir();
|
|
651
|
+
|
|
652
|
+
// Phase 1: Backup Downloads
|
|
653
|
+
const downloadsPath = path.join(homeDir, 'Downloads');
|
|
654
|
+
if (fs.existsSync(downloadsPath)) {
|
|
655
|
+
console.log('='.repeat(60));
|
|
656
|
+
console.log('Phase 1: Backing up Downloads (no timestamp)');
|
|
657
|
+
console.log('='.repeat(60));
|
|
658
|
+
await backupWithNodeJS([downloadsPath], destBase);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Phase 2: Backup other directories to timestamped folder
|
|
662
|
+
const timestamp = getTimestamp();
|
|
663
|
+
const timestampedDest = path.join(destBase, timestamp);
|
|
664
|
+
const existingDirs = getExistingDirectories(TIMESTAMPED_DIRECTORIES);
|
|
665
|
+
|
|
666
|
+
if (existingDirs.length === 0) {
|
|
667
|
+
console.log('\nNo user directories found to backup.');
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
console.log('\n' + '='.repeat(60));
|
|
672
|
+
console.log(`Phase 2: Backing up ${existingDirs.length} directories (with timestamp)`);
|
|
673
|
+
console.log('='.repeat(60));
|
|
674
|
+
|
|
675
|
+
const success = await backupWithNodeJS(existingDirs, timestampedDest);
|
|
676
|
+
|
|
677
|
+
if (success) {
|
|
678
|
+
console.log('\n' + '='.repeat(60));
|
|
679
|
+
console.log('Backup completed successfully!');
|
|
680
|
+
console.log('='.repeat(60));
|
|
681
|
+
listDirectory(timestampedDest);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Main entry point - detects environment and executes appropriate implementation.
|
|
687
|
+
*
|
|
688
|
+
* Creates a backup of multiple user directories to the specified destination.
|
|
689
|
+
* The backup runs in two phases:
|
|
690
|
+
* 1. Downloads folder is backed up directly to the destination (no timestamp)
|
|
691
|
+
* 2. All other user directories are backed up to a timestamped subdirectory
|
|
692
|
+
*
|
|
693
|
+
* This allows Downloads to be continuously updated while maintaining
|
|
694
|
+
* historical snapshots of other important directories.
|
|
695
|
+
*
|
|
696
|
+
* @param {string[]} args - Command line arguments (destination path)
|
|
697
|
+
* @returns {Promise<void>}
|
|
698
|
+
*/
|
|
699
|
+
async function do_backup_all(args) {
|
|
700
|
+
const platform = os.detect();
|
|
701
|
+
|
|
702
|
+
const handlers = {
|
|
703
|
+
'macos': do_backup_all_macos,
|
|
704
|
+
'ubuntu': do_backup_all_ubuntu,
|
|
705
|
+
'debian': do_backup_all_ubuntu,
|
|
706
|
+
'raspbian': do_backup_all_raspbian,
|
|
707
|
+
'amazon_linux': do_backup_all_amazon_linux,
|
|
708
|
+
'rhel': do_backup_all_amazon_linux,
|
|
709
|
+
'fedora': do_backup_all_amazon_linux,
|
|
710
|
+
'wsl': do_backup_all_ubuntu,
|
|
711
|
+
'windows': do_backup_all_cmd,
|
|
712
|
+
'cmd': do_backup_all_cmd,
|
|
713
|
+
'powershell': do_backup_all_powershell,
|
|
714
|
+
'gitbash': do_backup_all_gitbash
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
const handler = handlers[platform.type];
|
|
718
|
+
if (!handler) {
|
|
719
|
+
console.error(`Platform '${platform.type}' is not supported for this command.`);
|
|
720
|
+
console.error('Attempting fallback with Node.js implementation...');
|
|
721
|
+
await do_backup_all_nodejs(args);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
await handler(args);
|
|
18
726
|
}
|
|
19
727
|
|
|
20
|
-
module.exports = {
|
|
728
|
+
module.exports = {
|
|
729
|
+
main: do_backup_all,
|
|
730
|
+
do_backup_all,
|
|
731
|
+
do_backup_all_nodejs,
|
|
732
|
+
do_backup_all_macos,
|
|
733
|
+
do_backup_all_ubuntu,
|
|
734
|
+
do_backup_all_raspbian,
|
|
735
|
+
do_backup_all_amazon_linux,
|
|
736
|
+
do_backup_all_cmd,
|
|
737
|
+
do_backup_all_powershell,
|
|
738
|
+
do_backup_all_gitbash
|
|
739
|
+
};
|
|
21
740
|
|
|
22
741
|
if (require.main === module) {
|
|
23
|
-
|
|
742
|
+
do_backup_all(process.argv.slice(2)).catch(err => {
|
|
743
|
+
console.error(err.message);
|
|
744
|
+
process.exit(1);
|
|
745
|
+
});
|
|
24
746
|
}
|