gitlo 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 gitlo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,496 @@
1
+ <div align="center">
2
+
3
+ # šŸ—„ļø gitlo
4
+
5
+ [![npm version](https://img.shields.io/npm/v/gitlo.svg)](https://www.npmjs.com/package/gitlo)
6
+ [![npm downloads](https://img.shields.io/npm/dm/gitlo.svg)](https://www.npmjs.com/package/gitlo)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![Node.js Version](https://img.shields.io/node/v/gitlo)](https://nodejs.org)
9
+
10
+ **Backup all your GitHub repositories locally with one command.**
11
+
12
+ Never lose your code to account deletions, suspensions, or unexpected issues.
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ ## ✨ Features
19
+
20
+ - šŸ” **Secure** - Token stored locally in `~/.gitlo/config.json`
21
+ - šŸ“ **Fork Support** - Optionally backup forked repositories
22
+ - šŸ”’ **Private Repos** - Backs up private repositories with proper token
23
+ - ā° **Auto-Schedule** - Built-in cron scheduler for automatic backups
24
+ - šŸ“Š **Progress Tracking** - Visual progress with spinners and stats
25
+ - šŸš€ **Fast** - Only updates changed repos with `--update` flag
26
+ - šŸ“ **Logging** - Full logs of backup operations
27
+ - šŸŽÆ **Dry Run** - Preview what will be backed up without downloading
28
+
29
+ ## šŸ¤” Why?
30
+
31
+ GitHub accounts can be deleted, suspended, or compromised. This tool creates a local backup of all your repositories so you always have a copy of your work.
32
+
33
+ ## šŸ“¦ Installation
34
+
35
+ ### Quick Install (npm - Recommended)
36
+
37
+ ```bash
38
+ npm install -g gitlo
39
+ ```
40
+
41
+ Or with other package managers:
42
+
43
+ ```bash
44
+ # pnpm
45
+ pnpm add -g gitlo
46
+
47
+ # yarn
48
+ yarn global add gitlo
49
+ ```
50
+
51
+ ### Requirements
52
+
53
+ - **Node.js** >= 18.0.0
54
+ - **Git** installed on your system
55
+
56
+ ### Alternative: Install from Source
57
+
58
+ ```bash
59
+ # Clone and build
60
+ git clone https://github.com/dropocol/gitlo.git
61
+ cd gitlo
62
+ pnpm install
63
+ pnpm run build
64
+
65
+ # Link globally
66
+ pnpm link --global
67
+ ```
68
+
69
+ ## Getting a GitHub Token
70
+
71
+ 1. Go to [https://github.com/settings/tokens](https://github.com/settings/tokens)
72
+ 2. Click "Generate new token (classic)"
73
+ 3. Select these scopes:
74
+ - `repo` - Full control of private repositories
75
+ - `read:user` - Read user profile data
76
+ 4. Generate and copy the token
77
+
78
+ ## šŸš€ Quick Start
79
+
80
+ ### 1. Get a GitHub Token
81
+
82
+ 1. Go to [GitHub Settings → Tokens](https://github.com/settings/tokens)
83
+ 2. Click "Generate new token (classic)"
84
+ 3. Select scopes: ā˜‘ļø `repo` and ā˜‘ļø `read:user`
85
+ 4. Generate and copy the token
86
+
87
+ ### 2. Configure gitlo (One-time setup)
88
+
89
+ ```bash
90
+ # Save your token
91
+ gitlo config set token ghp_xxxxxxxxxxxx
92
+
93
+ # Optional: Set backup directory
94
+ gitlo config set output-dir ~/backups/github
95
+ ```
96
+
97
+ ### 3. Run Backup
98
+
99
+ ```bash
100
+ # Backup all your repos
101
+ gitlo
102
+
103
+ # Include forks too
104
+ gitlo --include-forks
105
+
106
+ # Setup automatic daily backups
107
+ gitlo schedule setup --frequency daily
108
+ ```
109
+
110
+ ## Commands
111
+
112
+ ### `gitlo`
113
+
114
+ Run the backup with configured settings.
115
+
116
+ ### `gitlo config`
117
+
118
+ Manage gitlo configuration settings (token, output directory).
119
+
120
+ ### `gitlo schedule`
121
+
122
+ Schedule automatic backups with cron (built-in scheduler).
123
+
124
+ ## All Commands & Options
125
+
126
+ ### Main Backup Command
127
+
128
+ ```bash
129
+ gitlo [options]
130
+ ```
131
+
132
+ **Options:**
133
+
134
+ - `-t, --token <token>` - GitHub personal access token
135
+ - `-o, --output-dir <dir>` - Output directory for backups
136
+ - `-m, --method <method>` - Clone method: https or ssh (default: https)
137
+ - `--include-private` - Include private repositories (default: true)
138
+ - `--exclude-private` - Exclude private repositories
139
+ - `--include-forks` - Include forked repositories (default: false)
140
+ - `--dry-run` - Show what would be backed up without cloning
141
+ - `--update` - Update existing repositories with git pull
142
+ - `-v, --verbose` - Show detailed progress and filtering information
143
+ - `-h, --help` - Display help
144
+
145
+ ### āš ļø Important: Forks Are Excluded by Default
146
+
147
+ By default, `gitlo` **does not backup forked repositories**. If you have forks you want to backup, use `--include-forks`:
148
+
149
+ ```bash
150
+ # Backup everything including forks
151
+ gitlo --include-forks
152
+
153
+ # Backup only your own repos (no forks) - this is the default
154
+ gitlo
155
+ ```
156
+
157
+ ### Config Commands
158
+
159
+ #### `gitlo config set`
160
+
161
+ Set a configuration value.
162
+
163
+ ```bash
164
+ gitlo config set token <github-token>
165
+ gitlo config set output-dir <path>
166
+ ```
167
+
168
+ **Examples:**
169
+
170
+ ```bash
171
+ # Set your GitHub token
172
+ gitlo config set token ghp_xxxxxxxxxxxx
173
+
174
+ # Set default backup directory
175
+ gitlo config set output-dir ~/backups/github-repos
176
+
177
+ # Use ~ for home directory (automatically expanded)
178
+ gitlo config set output-dir ~/Documents/GitHub-Backups
179
+ ```
180
+
181
+ #### `gitlo config get`
182
+
183
+ View a specific configuration value.
184
+
185
+ ```bash
186
+ gitlo config get token
187
+ gitlo config get output-dir
188
+ ```
189
+
190
+ **Output:**
191
+
192
+ - Token is masked for security (shows: `ghp_****xxxx`)
193
+ - Output-dir shows the full path
194
+
195
+ #### `gitlo config list`
196
+
197
+ List all configuration values.
198
+
199
+ ```bash
200
+ gitlo config list
201
+ ```
202
+
203
+ **Output:**
204
+
205
+ ```
206
+ šŸ“‹ gitlo Configuration
207
+
208
+ token: ghp_****xxxx
209
+ output-dir: /Users/username/backups/github-repos
210
+
211
+ Config file: /Users/username/.gitlo/config.json
212
+ ```
213
+
214
+ #### `gitlo config remove`
215
+
216
+ Remove a configuration value.
217
+
218
+ ```bash
219
+ gitlo config remove token
220
+ gitlo config remove output-dir
221
+ ```
222
+
223
+ ### Schedule Commands
224
+
225
+ #### `gitlo schedule setup`
226
+
227
+ Schedule automatic backups with cron.
228
+
229
+ ```bash
230
+ # Weekly on Sunday at 2 AM (default)
231
+ gitlo schedule setup
232
+
233
+ # Daily at 3 AM
234
+ gitlo schedule setup --frequency daily --time 03:00
235
+
236
+ # Weekly on Monday at 2 AM
237
+ gitlo schedule setup --frequency weekly --day 1
238
+
239
+ # Monthly on the 1st at 2 AM
240
+ gitlo schedule setup --frequency monthly
241
+
242
+ # Hourly (only updates existing repos)
243
+ gitlo schedule setup --frequency hourly --update
244
+ ```
245
+
246
+ #### `gitlo schedule list`
247
+
248
+ View scheduled backup jobs.
249
+
250
+ ```bash
251
+ gitlo schedule list
252
+ ```
253
+
254
+ #### `gitlo schedule remove`
255
+
256
+ Remove automatic backup schedule.
257
+
258
+ ```bash
259
+ gitlo schedule remove
260
+ ```
261
+
262
+ ## Configuration Priority
263
+
264
+ ### Token Priority (first found wins):
265
+
266
+ 1. CLI argument: `gitlo -t TOKEN`
267
+ 2. Environment variable: `GITHUB_TOKEN`
268
+ 3. Config file: `gitlo config set token TOKEN`
269
+
270
+ ### Output Directory Priority:
271
+
272
+ 1. CLI argument: `gitlo -o ~/my-backups`
273
+ 2. Config file: `gitlo config set output-dir ~/backups`
274
+ 3. Default: your GitHub username as folder name
275
+
276
+ ## Usage Examples
277
+
278
+ ### One-Time Setup (Recommended)
279
+
280
+ ```bash
281
+ # Step 1: Save your token
282
+ gitlo config set token ghp_your_token_here
283
+
284
+ # Step 2: Set default backup location (optional)
285
+ gitlo config set output-dir ~/backups/github
286
+
287
+ # Step 3: Run backup anytime
288
+ gitlo
289
+
290
+ # Update existing backups
291
+ gitlo --update
292
+ ```
293
+
294
+ ### Without Config (One-Time Use)
295
+
296
+ ```bash
297
+ # Using environment variable
298
+ export GITHUB_TOKEN=your_token_here
299
+ gitlo
300
+
301
+ # Using CLI argument
302
+ gitlo -t your_token_here
303
+ ```
304
+
305
+ ### Common Workflows
306
+
307
+ ```bash
308
+ # Dry run to preview what will be backed up
309
+ gitlo --dry-run
310
+
311
+ # Backup to specific directory (overrides config)
312
+ gitlo -o ~/backups/my-github-repos
313
+
314
+ # Use SSH for cloning (requires SSH key setup)
315
+ gitlo -m ssh
316
+
317
+ # Only public repos, no forks
318
+ gitlo --exclude-private
319
+
320
+ # Include forks
321
+ gitlo --include-forks
322
+
323
+ # Weekly update of all existing backups
324
+ gitlo --update
325
+ ```
326
+
327
+ ## Development
328
+
329
+ If you want to contribute or modify the code:
330
+
331
+ ```bash
332
+ # Install all dependencies (including dev)
333
+ pnpm install
334
+
335
+ # Build TypeScript to JavaScript
336
+ pnpm run build
337
+
338
+ # Run in development mode
339
+ pnpm run dev
340
+
341
+ # Clean build artifacts
342
+ pnpm run clean
343
+
344
+ # Test local changes globally
345
+ pnpm link --global
346
+ ```
347
+
348
+ ### Publishing
349
+
350
+ When you're ready to publish to npm:
351
+
352
+ ```bash
353
+ # The prepublishOnly script automatically builds before publishing
354
+ pnpm publish
355
+
356
+ # Or publish to npm
357
+ npm publish
358
+ ```
359
+
360
+ ## What Gets Backed Up?
361
+
362
+ - āœ… All your repositories (public by default)
363
+ - āœ… Private repositories (if `--include-private`)
364
+ - āœ… Forks (if `--include-forks`)
365
+ - āœ… Full git history and all branches
366
+ - āš ļø Issues, PRs, and wiki pages are NOT included (only git repos)
367
+
368
+ ## Output Structure
369
+
370
+ ```
371
+ your-backup-directory/
372
+ ā”œā”€ā”€ repo-1/
373
+ │ └── .git/
374
+ ā”œā”€ā”€ repo-2/
375
+ │ └── .git/
376
+ └── repo-3/
377
+ └── .git/
378
+ ```
379
+
380
+ ## Automation
381
+
382
+ ### Automatic Backups with Cron (Built-in)
383
+
384
+ `gitlo` has a built-in scheduler to automatically backup your repos:
385
+
386
+ ```bash
387
+ # Setup weekly backups (default: Sundays at 2 AM)
388
+ gitlo schedule setup
389
+
390
+ # Setup daily backups at 3 AM
391
+ gitlo schedule setup --frequency daily --time 03:00
392
+
393
+ # Setup weekly on Mondays at 2 AM
394
+ gitlo schedule setup --frequency weekly --day 1 --time 02:00
395
+
396
+ # Only update existing repos (faster)
397
+ gitlo schedule setup --update
398
+
399
+ # View scheduled jobs
400
+ gitlo schedule list
401
+
402
+ # Remove scheduled backups
403
+ gitlo schedule remove
404
+ ```
405
+
406
+ #### Schedule Options
407
+
408
+ - `-f, --frequency <freq>` - hourly, daily, weekly, monthly (default: weekly)
409
+ - `-t, --time <time>` - Time in HH:MM format (default: 02:00)
410
+ - `-d, --day <day>` - Day of week 0-6 for weekly (0=Sunday, default: 0)
411
+ - `-u, --update` - Only update existing repos, don't clone new ones
412
+ - `-l, --log <path>` - Log file path (default: ~/.gitlo/backup.log)
413
+
414
+ ### Manual Cron Setup (Advanced)
415
+
416
+ If you prefer manual setup:
417
+
418
+ ```bash
419
+ # Edit crontab
420
+ crontab -e
421
+
422
+ # Add line to backup weekly (Sundays at 2 AM)
423
+ 0 2 * * 0 /usr/local/bin/gitlo --update >> /var/log/gitlo.log 2>&1
424
+ ```
425
+
426
+ ### Quick Install (npm)
427
+
428
+ ```bash
429
+ npm install -g gitlo && gitlo config set token YOUR_TOKEN && gitlo
430
+ ```
431
+
432
+ ### One-liner for Development Setup
433
+
434
+ ```bash
435
+ cd ~ && git clone https://github.com/dropocol/gitlo.git && cd gitlo && pnpm install && pnpm run build && pnpm link --global && echo "Setup complete. Run: gitlo config set token YOUR_TOKEN"
436
+ ```
437
+
438
+ ## Security Notes
439
+
440
+ - Your GitHub token is stored in `~/.gitlo/config.json` on your local machine
441
+ - The token is masked when displayed (shows: `ghp_****xxxx`)
442
+ - Keep your config file secure - it contains your GitHub token!
443
+ - Use file permissions to protect the config: `chmod 600 ~/.gitlo/config.json`
444
+
445
+ ## Troubleshooting
446
+
447
+ ### "Not all repositories were backed up"
448
+
449
+ If `gitlo` reports fewer repositories than you have on GitHub, it's likely due to filtering:
450
+
451
+ **1. Forks are excluded by default**
452
+
453
+ ```bash
454
+ # Check if you have forks being skipped
455
+ gitlo --dry-run
456
+
457
+ # Include forks in backup
458
+ gitlo --include-forks
459
+ ```
460
+
461
+ **2. Private repos might be excluded**
462
+
463
+ ```bash
464
+ # By default, private repos ARE included
465
+ # But if you used --exclude-private:
466
+ gitlo --include-private
467
+ ```
468
+
469
+ **3. Use verbose mode to see what's being filtered**
470
+
471
+ ```bash
472
+ # See detailed information about fetching and filtering
473
+ gitlo -v
474
+ ```
475
+
476
+ ### "Bad credentials" Error
477
+
478
+ Your token is invalid or expired. Generate a new one at [https://github.com/settings/tokens](https://github.com/settings/tokens)
479
+
480
+ ### SSH Cloning Fails
481
+
482
+ Make sure you have SSH keys set up:
483
+
484
+ ```bash
485
+ ssh-keygen -t ed25519 -C "your@email.com"
486
+ ssh-add ~/.ssh/id_ed25519
487
+ # Add public key to GitHub: https://github.com/settings/keys
488
+ ```
489
+
490
+ ### Rate Limiting
491
+
492
+ The tool handles pagination automatically. If you have 1000+ repos, it may take a while.
493
+
494
+ ## License
495
+
496
+ MIT
package/dist/backup.js ADDED
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.cloneOrUpdateRepo = cloneOrUpdateRepo;
40
+ exports.displayRepoList = displayRepoList;
41
+ exports.displaySummary = displaySummary;
42
+ exports.ensureDirectory = ensureDirectory;
43
+ const simple_git_1 = __importDefault(require("simple-git"));
44
+ const chalk_1 = __importDefault(require("chalk"));
45
+ const ora_1 = __importDefault(require("ora"));
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ async function cloneOrUpdateRepo(repo, repoPath, options, progress, index, total) {
49
+ const repoSpinner = (0, ora_1.default)(`${progress} Processing ${repo.name}...`).start();
50
+ try {
51
+ if (fs.existsSync(repoPath)) {
52
+ if (options.updateExisting) {
53
+ repoSpinner.text = `${progress} Updating ${repo.name}...`;
54
+ const git = (0, simple_git_1.default)(repoPath);
55
+ await git.pull('origin', 'main').catch(async () => {
56
+ // Try master if main fails
57
+ await git.pull('origin', 'master');
58
+ });
59
+ repoSpinner.succeed(`${progress} ${chalk_1.default.green('āœ“')} Updated ${chalk_1.default.white(repo.name)}`);
60
+ return { status: 'updated' };
61
+ }
62
+ else {
63
+ repoSpinner.warn(`${progress} ${chalk_1.default.yellow('⚠')} ${repo.name} already exists (use --update to pull changes)`);
64
+ return { status: 'skipped' };
65
+ }
66
+ }
67
+ else {
68
+ repoSpinner.text = `${progress} Cloning ${repo.name}...`;
69
+ const cloneUrl = options.method === 'ssh' ? repo.sshUrl : repo.cloneUrl;
70
+ const parentDir = path.dirname(repoPath);
71
+ await (0, simple_git_1.default)(parentDir).clone(cloneUrl, repoPath);
72
+ repoSpinner.succeed(`${progress} ${chalk_1.default.green('āœ“')} Cloned ${chalk_1.default.white(repo.name)}`);
73
+ return { status: 'cloned' };
74
+ }
75
+ }
76
+ catch (error) {
77
+ repoSpinner.fail(`${progress} ${chalk_1.default.red('āœ—')} Failed to process ${repo.name}`);
78
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
79
+ console.error(chalk_1.default.red(` Error: ${errorMessage}`));
80
+ return { status: 'error', error: errorMessage };
81
+ }
82
+ }
83
+ function displayRepoList(repos) {
84
+ console.log(chalk_1.default.bold('\nšŸ“‹ Repositories to backup:\n'));
85
+ repos.forEach((repo, i) => {
86
+ const privacy = repo.isPrivate ? chalk_1.default.red('šŸ”’') : chalk_1.default.green('🌐');
87
+ console.log(` ${i + 1}. ${privacy} ${chalk_1.default.white(repo.name)}`);
88
+ if (repo.description) {
89
+ console.log(` ${chalk_1.default.gray(repo.description)}`);
90
+ }
91
+ });
92
+ }
93
+ function displaySummary(stats, updateExisting, backupPath) {
94
+ console.log(chalk_1.default.bold('\nšŸ“Š Backup Summary\n'));
95
+ console.log(` ${chalk_1.default.green('āœ“')} Successfully cloned: ${stats.successCount}`);
96
+ if (updateExisting) {
97
+ console.log(` ${chalk_1.default.blue('↻')} Updated: ${stats.updateCount}`);
98
+ }
99
+ if (stats.errorCount > 0) {
100
+ console.log(` ${chalk_1.default.red('āœ—')} Failed: ${stats.errorCount}`);
101
+ }
102
+ console.log(`\n ${chalk_1.default.bold('Location:')} ${chalk_1.default.cyan(backupPath)}\n`);
103
+ }
104
+ function ensureDirectory(dirPath) {
105
+ if (!fs.existsSync(dirPath)) {
106
+ fs.mkdirSync(dirPath, { recursive: true });
107
+ }
108
+ }