git-ripper 1.5.1 → 1.5.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/package.json +1 -1
- package/src/index.js +89 -12
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { program } from "commander";
|
|
2
2
|
import { parseGitHubUrl } from "./parser.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
downloadFolder,
|
|
5
|
+
downloadFolderWithResume,
|
|
6
|
+
downloadFile,
|
|
7
|
+
} from "./downloader.js";
|
|
4
8
|
import { downloadAndArchive } from "./archiver.js";
|
|
5
9
|
import { ResumeManager } from "./resumeManager.js";
|
|
6
10
|
import { fileURLToPath } from "node:url";
|
|
7
11
|
import { dirname, join, resolve, basename } from "node:path";
|
|
8
12
|
import fs from "node:fs";
|
|
9
13
|
import process from "node:process";
|
|
14
|
+
import readline from "node:readline";
|
|
10
15
|
import chalk from "chalk";
|
|
11
16
|
|
|
12
17
|
// Get package.json for version
|
|
@@ -38,7 +43,7 @@ const validateOutputDirectory = (outputDir) => {
|
|
|
38
43
|
const stats = fs.statSync(resolvedDir);
|
|
39
44
|
if (!stats.isDirectory()) {
|
|
40
45
|
throw new Error(
|
|
41
|
-
`Output path exists but is not a directory: ${outputDir}
|
|
46
|
+
`Output path exists but is not a directory: ${outputDir}`,
|
|
42
47
|
);
|
|
43
48
|
}
|
|
44
49
|
}
|
|
@@ -61,7 +66,10 @@ const initializeCLI = () => {
|
|
|
61
66
|
.description("Clone specific folders from GitHub repositories")
|
|
62
67
|
.argument("[url]", "GitHub URL of the folder to clone")
|
|
63
68
|
.option("-o, --output <directory>", "Output directory", process.cwd())
|
|
64
|
-
.option(
|
|
69
|
+
.option(
|
|
70
|
+
"--gh-token <token>",
|
|
71
|
+
"GitHub Personal Access Token for private repositories",
|
|
72
|
+
)
|
|
65
73
|
.option("--zip [filename]", "Create ZIP archive of downloaded files")
|
|
66
74
|
.option("--no-resume", "Disable resume functionality")
|
|
67
75
|
.option("--force-restart", "Ignore existing checkpoints and start fresh")
|
|
@@ -85,7 +93,7 @@ const initializeCLI = () => {
|
|
|
85
93
|
console.log(` Output: ${cp.outputDir}`);
|
|
86
94
|
console.log(` Progress: ${cp.progress}`);
|
|
87
95
|
console.log(
|
|
88
|
-
` Last Updated: ${new Date(cp.timestamp).toLocaleString()}
|
|
96
|
+
` Last Updated: ${new Date(cp.timestamp).toLocaleString()}`,
|
|
89
97
|
);
|
|
90
98
|
if (cp.failedFiles > 0) {
|
|
91
99
|
console.log(chalk.yellow(` Failed Files: ${cp.failedFiles}`));
|
|
@@ -98,7 +106,7 @@ const initializeCLI = () => {
|
|
|
98
106
|
// URL is required for download operations
|
|
99
107
|
if (!url) {
|
|
100
108
|
console.error(
|
|
101
|
-
chalk.red("Error: URL is required for download operations")
|
|
109
|
+
chalk.red("Error: URL is required for download operations"),
|
|
102
110
|
);
|
|
103
111
|
console.log("Use --list-checkpoints to see existing downloads");
|
|
104
112
|
process.exit(1);
|
|
@@ -107,6 +115,66 @@ const initializeCLI = () => {
|
|
|
107
115
|
console.log(`Parsing URL: ${url}`);
|
|
108
116
|
const parsedUrl = parseGitHubUrl(url);
|
|
109
117
|
|
|
118
|
+
// Warning for full repo links
|
|
119
|
+
if (!parsedUrl.branch && !parsedUrl.folderPath) {
|
|
120
|
+
console.log(
|
|
121
|
+
chalk.yellow(
|
|
122
|
+
"\n⚠️ WARNING: You've provided a full repository link.",
|
|
123
|
+
),
|
|
124
|
+
);
|
|
125
|
+
console.log(
|
|
126
|
+
chalk.yellow(
|
|
127
|
+
"This tool will attempt to download the entire repository, which may:",
|
|
128
|
+
),
|
|
129
|
+
);
|
|
130
|
+
console.log(chalk.yellow(" • Trigger GitHub API rate limits"));
|
|
131
|
+
console.log(
|
|
132
|
+
chalk.yellow(" • Take a long time for large repositories"),
|
|
133
|
+
);
|
|
134
|
+
console.log(chalk.yellow(" • Consume significant bandwidth\n"));
|
|
135
|
+
console.log(
|
|
136
|
+
chalk.cyan(
|
|
137
|
+
"💡 Recommendation: Use 'git clone' instead for full repositories:",
|
|
138
|
+
),
|
|
139
|
+
);
|
|
140
|
+
console.log(
|
|
141
|
+
chalk.cyan(
|
|
142
|
+
` git clone https://github.com/${parsedUrl.owner}/${parsedUrl.repo}.git\n`,
|
|
143
|
+
),
|
|
144
|
+
);
|
|
145
|
+
console.log(
|
|
146
|
+
chalk.gray("To download a specific folder, use a URL like:"),
|
|
147
|
+
);
|
|
148
|
+
console.log(
|
|
149
|
+
chalk.gray(
|
|
150
|
+
`https://github.com/${parsedUrl.owner}/${parsedUrl.repo}/tree/<branch>/<folder-path>\n`,
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// Prompt user for confirmation
|
|
155
|
+
const rl = readline.createInterface({
|
|
156
|
+
input: process.stdin,
|
|
157
|
+
output: process.stdout,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const answer = await new Promise((resolve) => {
|
|
161
|
+
rl.question(
|
|
162
|
+
chalk.bold.white("Do you still want to proceed? (Y/n): "),
|
|
163
|
+
(ans) => {
|
|
164
|
+
rl.close();
|
|
165
|
+
resolve(ans.trim().toLowerCase());
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (answer !== "yes" && answer !== "y") {
|
|
171
|
+
console.log(chalk.yellow("\nOperation cancelled by user."));
|
|
172
|
+
process.exit(0);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
console.log(chalk.green("\nProceeding with download...\n"));
|
|
176
|
+
}
|
|
177
|
+
|
|
110
178
|
// Validate output directory
|
|
111
179
|
try {
|
|
112
180
|
options.output = validateOutputDirectory(options.output);
|
|
@@ -133,7 +201,12 @@ const initializeCLI = () => {
|
|
|
133
201
|
try {
|
|
134
202
|
if (createArchive) {
|
|
135
203
|
console.log(`Creating ZIP archive...`);
|
|
136
|
-
await downloadAndArchive(
|
|
204
|
+
await downloadAndArchive(
|
|
205
|
+
parsedUrl,
|
|
206
|
+
options.output,
|
|
207
|
+
archiveName,
|
|
208
|
+
downloadOptions,
|
|
209
|
+
);
|
|
137
210
|
} else if (parsedUrl.type === "blob") {
|
|
138
211
|
console.log(`Downloading file to: ${options.output}`);
|
|
139
212
|
const fileName = basename(parsedUrl.folderPath);
|
|
@@ -144,7 +217,7 @@ const initializeCLI = () => {
|
|
|
144
217
|
parsedUrl.branch,
|
|
145
218
|
parsedUrl.folderPath,
|
|
146
219
|
outputPath,
|
|
147
|
-
options.ghToken
|
|
220
|
+
options.ghToken,
|
|
148
221
|
);
|
|
149
222
|
} else {
|
|
150
223
|
console.log(`Downloading folder to: ${options.output}`);
|
|
@@ -152,10 +225,14 @@ const initializeCLI = () => {
|
|
|
152
225
|
result = await downloadFolderWithResume(
|
|
153
226
|
parsedUrl,
|
|
154
227
|
options.output,
|
|
155
|
-
downloadOptions
|
|
228
|
+
downloadOptions,
|
|
156
229
|
);
|
|
157
230
|
} else {
|
|
158
|
-
result = await downloadFolder(
|
|
231
|
+
result = await downloadFolder(
|
|
232
|
+
parsedUrl,
|
|
233
|
+
options.output,
|
|
234
|
+
downloadOptions,
|
|
235
|
+
);
|
|
159
236
|
}
|
|
160
237
|
}
|
|
161
238
|
} catch (opError) {
|
|
@@ -165,9 +242,9 @@ const initializeCLI = () => {
|
|
|
165
242
|
// Consolidated result and error handling
|
|
166
243
|
if (error) {
|
|
167
244
|
const failMsg =
|
|
168
|
-
operationType === "archive"
|
|
169
|
-
|
|
170
|
-
|
|
245
|
+
operationType === "archive" ?
|
|
246
|
+
`Archive creation failed: ${error.message}`
|
|
247
|
+
: `Download failed: ${error.message}`;
|
|
171
248
|
console.error(chalk.red(failMsg));
|
|
172
249
|
process.exit(1);
|
|
173
250
|
} else if (!createArchive && result && !result.success) {
|