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 +41 -22
- package/dist/backup.js +34 -5
- package/dist/cli.js +3 -3
- package/dist/cron.js +7 -4
- package/dist/index.js +3 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
<div align="
|
|
1
|
+
<div align="left">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/dropocol/gitlo)
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/gitlo)
|
|
6
6
|
[](https://www.npmjs.com/package/gitlo)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
[](https://nodejs.org)
|
|
9
9
|
|
|
10
|
-
|
|
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
|
|
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
|
-
|
|
63
|
-
|
|
64
|
+
npm install
|
|
65
|
+
npm run build
|
|
64
66
|
|
|
65
67
|
# Link globally
|
|
66
|
-
|
|
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
|
-
#
|
|
243
|
-
gitlo schedule setup --
|
|
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
|
-
|
|
352
|
+
npm install
|
|
334
353
|
|
|
335
354
|
# Build TypeScript to JavaScript
|
|
336
|
-
|
|
355
|
+
npm run build
|
|
337
356
|
|
|
338
357
|
# Run in development mode
|
|
339
|
-
|
|
358
|
+
npm run dev
|
|
340
359
|
|
|
341
360
|
# Clean build artifacts
|
|
342
|
-
|
|
361
|
+
npm run clean
|
|
343
362
|
|
|
344
363
|
# Test local changes globally
|
|
345
|
-
|
|
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
|
-
#
|
|
397
|
-
gitlo schedule setup --
|
|
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
|
-
-
|
|
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 &&
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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('
|
|
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.
|
|
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.
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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(
|
|
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.
|
|
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@
|
|
39
|
+
"packageManager": "pnpm@10.33.0",
|
|
38
40
|
"engines": {
|
|
39
41
|
"node": ">=18.0.0"
|
|
40
42
|
},
|