gitlo 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,13 +1,15 @@
1
- <div align="center">
1
+ <div align="left">
2
2
 
3
- # šŸ—„ļø gitlo
3
+ [![gitlo](https://raw.githubusercontent.com/dropocol/gitlo/main/banner.png)](https://github.com/dropocol/gitlo)
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/gitlo.svg)](https://www.npmjs.com/package/gitlo)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/gitlo.svg)](https://www.npmjs.com/package/gitlo)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
  [![Node.js Version](https://img.shields.io/node/v/gitlo)](https://nodejs.org)
9
9
 
10
- **Backup all your GitHub repositories locally with one command.**
10
+ # gitlo
11
+
12
+ **Backup your GitHub repos. Never lose your code.**
11
13
 
12
14
  Never lose your code to account deletions, suspensions, or unexpected issues.
13
15
 
@@ -32,7 +34,7 @@ GitHub accounts can be deleted, suspended, or compromised. This tool creates a l
32
34
 
33
35
  ## šŸ“¦ Installation
34
36
 
35
- ### Quick Install (npm - Recommended)
37
+ ### Quick Install (npm)
36
38
 
37
39
  ```bash
38
40
  npm install -g gitlo
@@ -59,15 +61,19 @@ yarn global add gitlo
59
61
  # Clone and build
60
62
  git clone https://github.com/dropocol/gitlo.git
61
63
  cd gitlo
62
- pnpm install
63
- pnpm run build
64
+ npm install
65
+ npm run build
64
66
 
65
67
  # Link globally
66
- pnpm link --global
68
+ npm link --global
67
69
  ```
68
70
 
69
71
  ## Getting a GitHub Token
70
72
 
73
+ gitlo supports two types of GitHub personal access tokens:
74
+
75
+ ### Option 1: Classic Token (Recommended)
76
+
71
77
  1. Go to [https://github.com/settings/tokens](https://github.com/settings/tokens)
72
78
  2. Click "Generate new token (classic)"
73
79
  3. Select these scopes:
@@ -75,6 +81,19 @@ pnpm link --global
75
81
  - `read:user` - Read user profile data
76
82
  4. Generate and copy the token
77
83
 
84
+ ### Option 2: Fine-Grained Token
85
+
86
+ 1. Go to [https://github.com/settings/tokens](https://github.com/settings/tokens)
87
+ 2. Click "Generate new token" → "Fine-grained token"
88
+ 3. Configure permissions:
89
+ - **Repository permissions**:
90
+ - Contents: **Read and write** (required for cloning)
91
+ - Metadata: **Read-only** (required for repository access)
92
+ - **Account permissions**:
93
+ - User profile: **Read-only** (optional, for user info)
94
+ 4. Select the repositories to access (or "All repositories")
95
+ 5. Generate and copy the token
96
+
78
97
  ## šŸš€ Quick Start
79
98
 
80
99
  ### 1. Get a GitHub Token
@@ -227,7 +246,7 @@ gitlo config remove output-dir
227
246
  Schedule automatic backups with cron.
228
247
 
229
248
  ```bash
230
- # Weekly on Sunday at 2 AM (default)
249
+ # Weekly on Sunday at 2 AM (default, updates existing repos)
231
250
  gitlo schedule setup
232
251
 
233
252
  # Daily at 3 AM
@@ -239,8 +258,8 @@ gitlo schedule setup --frequency weekly --day 1
239
258
  # Monthly on the 1st at 2 AM
240
259
  gitlo schedule setup --frequency monthly
241
260
 
242
- # Hourly (only updates existing repos)
243
- gitlo schedule setup --frequency hourly --update
261
+ # Full backup (clone all repos, don't just update)
262
+ gitlo schedule setup --full
244
263
  ```
245
264
 
246
265
  #### `gitlo schedule list`
@@ -330,19 +349,19 @@ If you want to contribute or modify the code:
330
349
 
331
350
  ```bash
332
351
  # Install all dependencies (including dev)
333
- pnpm install
352
+ npm install
334
353
 
335
354
  # Build TypeScript to JavaScript
336
- pnpm run build
355
+ npm run build
337
356
 
338
357
  # Run in development mode
339
- pnpm run dev
358
+ npm run dev
340
359
 
341
360
  # Clean build artifacts
342
- pnpm run clean
361
+ npm run clean
343
362
 
344
363
  # Test local changes globally
345
- pnpm link --global
364
+ npm link --global
346
365
  ```
347
366
 
348
367
  ### Publishing
@@ -351,9 +370,6 @@ When you're ready to publish to npm:
351
370
 
352
371
  ```bash
353
372
  # The prepublishOnly script automatically builds before publishing
354
- pnpm publish
355
-
356
- # Or publish to npm
357
373
  npm publish
358
374
  ```
359
375
 
@@ -385,6 +401,7 @@ your-backup-directory/
385
401
 
386
402
  ```bash
387
403
  # Setup weekly backups (default: Sundays at 2 AM)
404
+ # By default, updates existing repos (faster)
388
405
  gitlo schedule setup
389
406
 
390
407
  # Setup daily backups at 3 AM
@@ -393,8 +410,8 @@ gitlo schedule setup --frequency daily --time 03:00
393
410
  # Setup weekly on Mondays at 2 AM
394
411
  gitlo schedule setup --frequency weekly --day 1 --time 02:00
395
412
 
396
- # Only update existing repos (faster)
397
- gitlo schedule setup --update
413
+ # Full backup (clone all repos including new ones)
414
+ gitlo schedule setup --full
398
415
 
399
416
  # View scheduled jobs
400
417
  gitlo schedule list
@@ -408,9 +425,11 @@ gitlo schedule remove
408
425
  - `-f, --frequency <freq>` - hourly, daily, weekly, monthly (default: weekly)
409
426
  - `-t, --time <time>` - Time in HH:MM format (default: 02:00)
410
427
  - `-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
428
+ - `--full` - Clone all repos instead of just updating existing (default: update)
412
429
  - `-l, --log <path>` - Log file path (default: ~/.gitlo/backup.log)
413
430
 
431
+ **Note:** By default, scheduled backups update existing repositories for faster execution. Use `--full` to clone all repositories including new ones.
432
+
414
433
  ### Manual Cron Setup (Advanced)
415
434
 
416
435
  If you prefer manual setup:
@@ -432,7 +451,7 @@ npm install -g gitlo && gitlo config set token YOUR_TOKEN && gitlo
432
451
  ### One-liner for Development Setup
433
452
 
434
453
  ```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"
454
+ cd ~ && git clone https://github.com/dropocol/gitlo.git && cd gitlo && npm install && npm run build && npm link --global && echo "Setup complete. Run: gitlo config set token YOUR_TOKEN"
436
455
  ```
437
456
 
438
457
  ## Security Notes
package/dist/backup.js CHANGED
@@ -52,10 +52,29 @@ async function cloneOrUpdateRepo(repo, repoPath, options, progress, index, total
52
52
  if (options.updateExisting) {
53
53
  repoSpinner.text = `${progress} Updating ${repo.name}...`;
54
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
- });
55
+ // For private repos with HTTPS, temporarily use authenticated URL
56
+ const originalUrlResult = await git.remote(['get-url', 'origin']);
57
+ const originalUrl = typeof originalUrlResult === 'string'
58
+ ? originalUrlResult.trim().replace(/[\n\r\t]/g, '')
59
+ : '';
60
+ let needsRestore = false;
61
+ if (options.token && options.method === 'https' && originalUrl) {
62
+ const authenticatedUrl = repo.cloneUrl.replace('https://', `https://${options.token}@`);
63
+ await git.remote(['set-url', 'origin', authenticatedUrl.trim()]);
64
+ needsRestore = true;
65
+ }
66
+ try {
67
+ await git.pull('origin', 'main').catch(async () => {
68
+ // Try master if main fails
69
+ await git.pull('origin', 'master');
70
+ });
71
+ }
72
+ finally {
73
+ // Restore original URL without token
74
+ if (needsRestore && originalUrl) {
75
+ await git.remote(['set-url', 'origin', originalUrl]);
76
+ }
77
+ }
59
78
  repoSpinner.succeed(`${progress} ${chalk_1.default.green('āœ“')} Updated ${chalk_1.default.white(repo.name)}`);
60
79
  return { status: 'updated' };
61
80
  }
@@ -66,9 +85,19 @@ async function cloneOrUpdateRepo(repo, repoPath, options, progress, index, total
66
85
  }
67
86
  else {
68
87
  repoSpinner.text = `${progress} Cloning ${repo.name}...`;
69
- const cloneUrl = options.method === 'ssh' ? repo.sshUrl : repo.cloneUrl;
88
+ let cloneUrl = options.method === 'ssh' ? repo.sshUrl : repo.cloneUrl;
89
+ // Inject token into HTTPS URL for private repos
90
+ if (options.token && options.method === 'https') {
91
+ cloneUrl = cloneUrl.replace('https://', `https://${options.token}@`);
92
+ }
70
93
  const parentDir = path.dirname(repoPath);
71
94
  await (0, simple_git_1.default)(parentDir).clone(cloneUrl, repoPath);
95
+ // Remove token from git config after clone for security
96
+ if (options.token && options.method === 'https') {
97
+ const git = (0, simple_git_1.default)(repoPath);
98
+ const cleanUrl = cloneUrl.replace(/https:\/\/[^@]+@/, 'https://').trim();
99
+ await git.remote(['set-url', 'origin', cleanUrl]);
100
+ }
72
101
  repoSpinner.succeed(`${progress} ${chalk_1.default.green('āœ“')} Cloned ${chalk_1.default.white(repo.name)}`);
73
102
  return { status: 'cloned' };
74
103
  }
package/dist/cli.js CHANGED
@@ -285,7 +285,7 @@ function createScheduleCommands(program) {
285
285
  .option('-f, --frequency <freq>', 'Backup frequency: hourly, daily, weekly, monthly', 'weekly')
286
286
  .option('-t, --time <time>', 'Time for daily/weekly/monthly (HH:MM format)', '02:00')
287
287
  .option('-d, --day <day>', 'Day of week for weekly (0-6, 0=Sunday)', '0')
288
- .option('-u, --update', 'Only update existing repos (faster)', false)
288
+ .option('--full', 'Clone all repos instead of updating existing (default: update existing)', false)
289
289
  .option('-l, --log <path>', 'Log file path', '~/.gitlo/backup.log')
290
290
  .action((cmdOptions) => {
291
291
  const { addCronJob, getCronExpression, formatSchedule } = require('./cron.js');
@@ -293,12 +293,12 @@ function createScheduleCommands(program) {
293
293
  frequency: cmdOptions.frequency,
294
294
  time: cmdOptions.time,
295
295
  dayOfWeek: parseInt(cmdOptions.day),
296
- updateOnly: cmdOptions.update,
296
+ updateOnly: !cmdOptions.full,
297
297
  };
298
298
  const logFile = cmdOptions.log.replace(/^~/, require('os').homedir());
299
299
  console.log(chalk_1.default.bold.blue('\nšŸ“… Schedule Automatic Backups\n'));
300
300
  console.log(chalk_1.default.gray(`Frequency: ${formatSchedule(schedule)}`));
301
- console.log(chalk_1.default.gray(`Update only: ${cmdOptions.update ? 'Yes' : 'No (full backup)'}`));
301
+ console.log(chalk_1.default.gray(`Update only: ${!cmdOptions.full ? 'Yes (default)' : 'No (full backup)'}`));
302
302
  console.log(chalk_1.default.gray(`Log file: ${logFile}\n`));
303
303
  if (addCronJob(schedule, logFile)) {
304
304
  console.log(chalk_1.default.green('āœ“ Automatic backup scheduled successfully!'));
package/dist/cron.js CHANGED
@@ -48,7 +48,6 @@ const fs = __importStar(require("fs"));
48
48
  const path = __importStar(require("path"));
49
49
  const os = __importStar(require("os"));
50
50
  const chalk_1 = __importDefault(require("chalk"));
51
- const config_manager_js_1 = require("./config-manager.js");
52
51
  const CRON_COMMENT = '# gitlo-auto-backup';
53
52
  function getCronExpression(schedule) {
54
53
  switch (schedule.frequency) {
@@ -71,9 +70,13 @@ function getCronExpression(schedule) {
71
70
  }
72
71
  }
73
72
  function getCronCommand(updateOnly = false) {
74
- const config = (0, config_manager_js_1.readConfig)();
75
- const gitloPath = (0, child_process_1.execSync)('which gitlo', { encoding: 'utf-8' }).trim() || 'gitlo';
76
- let command = `${gitloPath}`;
73
+ // Use full path to node and the built JS file for cron compatibility
74
+ // Cron has limited PATH, so we can't rely on wrapper scripts
75
+ const nodePath = (0, child_process_1.execSync)('which node', { encoding: 'utf-8' }).trim();
76
+ // Get the path to dist/index.js from the current file location
77
+ // After compilation, this file is at dist/cron.js, so we need index.js in the same dir
78
+ const scriptPath = path.join(path.dirname(__filename), 'index.js');
79
+ let command = `${nodePath} ${scriptPath}`;
77
80
  // Add update flag if specified
78
81
  if (updateOnly) {
79
82
  command += ' --update';
package/dist/index.js CHANGED
@@ -3,11 +3,13 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const commander_1 = require("commander");
5
5
  const cli_js_1 = require("./cli.js");
6
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
7
+ const packageJson = require('../package.json');
6
8
  const program = new commander_1.Command();
7
9
  program
8
10
  .name('gitlo')
9
11
  .description('CLI tool to backup all your GitHub repositories locally')
10
- .version('1.0.0');
12
+ .version(packageJson.version);
11
13
  // Config subcommands
12
14
  (0, cli_js_1.createConfigCommands)(program);
13
15
  // Schedule subcommands
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitlo",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "CLI tool to backup all your GitHub repositories locally",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -16,7 +16,9 @@
16
16
  "start": "node dist/index.js",
17
17
  "dev": "ts-node src/index.ts",
18
18
  "clean": "rm -rf dist node_modules",
19
- "prepublishOnly": "pnpm run build"
19
+ "prepublishOnly": "pnpm run build",
20
+ "link": "pnpm run build && pnpm link --global",
21
+ "unlink": "pnpm unlink --global && rm -f $(which gitlo 2>/dev/null || echo /dev/null)"
20
22
  },
21
23
  "keywords": [
22
24
  "github",
@@ -34,7 +36,7 @@
34
36
  "bugs": {
35
37
  "url": "https://github.com/dropocol/gitlo/issues"
36
38
  },
37
- "packageManager": "pnpm@9.10.0",
39
+ "packageManager": "pnpm@10.33.0",
38
40
  "engines": {
39
41
  "node": ">=18.0.0"
40
42
  },