git-ripper
Advanced tools
Comparing version
{ | ||
"name": "git-ripper", | ||
"version": "1.4.6", | ||
"version": "1.4.7", | ||
"description": "CLI tool that lets you download specific folders from GitHub repositories without cloning the entire repo.", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -91,3 +91,3 @@ import fs from "fs"; | ||
chalk.green( | ||
`✓ Archive created: ${outputPath} (${(size / 1024 / 1024).toFixed( | ||
`Archive created: ${outputPath} (${(size / 1024 / 1024).toFixed( | ||
2 | ||
@@ -94,0 +94,0 @@ )} MB)` |
@@ -55,3 +55,5 @@ import axios from "axios"; | ||
try { | ||
const repoInfoUrl = `https://api.github.com/repos/${owner}/${repo}`; | ||
const repoInfoUrl = `https://api.github.com/repos/${encodeURIComponent( | ||
owner | ||
)}/${encodeURIComponent(repo)}`; | ||
const repoInfoResponse = await axios.get(repoInfoUrl); | ||
@@ -79,3 +81,7 @@ effectiveBranch = repoInfoResponse.data.default_branch; | ||
const apiUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${effectiveBranch}?recursive=1`; | ||
const apiUrl = `https://api.github.com/repos/${encodeURIComponent( | ||
owner | ||
)}/${encodeURIComponent(repo)}/git/trees/${encodeURIComponent( | ||
effectiveBranch | ||
)}?recursive=1`; | ||
@@ -169,3 +175,5 @@ try { | ||
try { | ||
const repoInfoUrl = `https://api.github.com/repos/${owner}/${repo}`; | ||
const repoInfoUrl = `https://api.github.com/repos/${encodeURIComponent( | ||
owner | ||
)}/${encodeURIComponent(repo)}`; | ||
const repoInfoResponse = await axios.get(repoInfoUrl); | ||
@@ -197,6 +205,12 @@ effectiveBranch = repoInfoResponse.data.default_branch; | ||
const baseUrl = `https://raw.githubusercontent.com/${owner}/${repo}`; | ||
const baseUrl = `https://raw.githubusercontent.com/${encodeURIComponent( | ||
owner | ||
)}/${encodeURIComponent(repo)}`; | ||
const encodedFilePath = filePath | ||
.split("/") | ||
.map((part) => encodeURIComponent(part)) | ||
.join("/"); | ||
const fileUrlPath = effectiveBranch | ||
? `/${effectiveBranch}/${filePath}` | ||
: `/${filePath}`; // filePath might be at root | ||
? `/${encodeURIComponent(effectiveBranch)}/${encodedFilePath}` | ||
: `/${encodedFilePath}`; // filePath might be at root | ||
const url = `${baseUrl}${fileUrlPath}`; | ||
@@ -490,3 +504,3 @@ | ||
console.log( | ||
chalk.red(`❌ Download failed: No files were downloaded successfully`) | ||
chalk.red(`Download failed: No files were downloaded successfully`) | ||
); | ||
@@ -500,3 +514,3 @@ return { | ||
} else { | ||
console.log(chalk.yellow(`⚠️ Download completed with errors`)); | ||
console.log(chalk.yellow(`Download completed with errors`)); | ||
return { | ||
@@ -511,3 +525,3 @@ success: false, | ||
console.log( | ||
chalk.green(`✅ All ${succeeded} files downloaded successfully!`) | ||
chalk.green(`All ${succeeded} files downloaded successfully!`) | ||
); | ||
@@ -524,3 +538,3 @@ console.log(chalk.green(`Folder cloned successfully!`)); | ||
// Log the specific error details | ||
console.error(chalk.red(`❌ Error downloading folder: ${error.message}`)); | ||
console.error(chalk.red(`Error downloading folder: ${error.message}`)); | ||
@@ -550,5 +564,13 @@ // Re-throw the error so the main CLI can exit with proper error code | ||
const resumeManager = new ResumeManager(); | ||
const url = `https://github.com/${owner}/${repo}/tree/${branch || "main"}/${ | ||
folderPath || "" | ||
}`; | ||
const encodedFolderPath = folderPath | ||
? folderPath | ||
.split("/") | ||
.map((part) => encodeURIComponent(part)) | ||
.join("/") | ||
: ""; | ||
const url = `https://github.com/${encodeURIComponent( | ||
owner | ||
)}/${encodeURIComponent(repo)}/tree/${encodeURIComponent( | ||
branch || "main" | ||
)}/${encodedFolderPath}`; | ||
@@ -566,3 +588,3 @@ // Clear checkpoint if force restart is requested | ||
chalk.blue( | ||
`🔄 Found previous download from ${new Date( | ||
`Found previous download from ${new Date( | ||
checkpoint.timestamp | ||
@@ -574,3 +596,3 @@ ).toLocaleString()}` | ||
chalk.blue( | ||
`📊 Progress: ${checkpoint.downloadedFiles.length}/${checkpoint.totalFiles} files completed` | ||
`Progress: ${checkpoint.downloadedFiles.length}/${checkpoint.totalFiles} files completed` | ||
) | ||
@@ -601,7 +623,7 @@ ); | ||
chalk.yellow( | ||
`🔧 Detected ${corruptedCount} corrupted files, will re-download` | ||
`Detected ${corruptedCount} corrupted files, will re-download` | ||
) | ||
); | ||
} | ||
console.log(chalk.green(`✅ Verified ${validFiles.length} existing files`)); | ||
console.log(chalk.green(`Verified ${validFiles.length} existing files`)); | ||
} | ||
@@ -655,3 +677,3 @@ | ||
chalk.cyan( | ||
`📥 Starting download of ${totalFiles} files from ${chalk.white( | ||
`Starting download of ${totalFiles} files from ${chalk.white( | ||
owner + "/" + repo | ||
@@ -664,3 +686,3 @@ )}...` | ||
checkpoint.totalFiles = totalFiles; | ||
console.log(chalk.cyan(`📥 Resuming download...`)); | ||
console.log(chalk.cyan(`Resuming download...`)); | ||
} | ||
@@ -680,3 +702,3 @@ | ||
if (remainingFiles.length === 0) { | ||
console.log(chalk.green(`🎉 All files already downloaded!`)); | ||
console.log(chalk.green(`All files already downloaded!`)); | ||
resumeManager.cleanupCheckpoint(url, outputDir); | ||
@@ -687,3 +709,3 @@ return; | ||
console.log( | ||
chalk.cyan(`📥 Downloading ${remainingFiles.length} remaining files...`) | ||
chalk.cyan(`Downloading ${remainingFiles.length} remaining files...`) | ||
); | ||
@@ -772,6 +794,4 @@ | ||
progressBar.stop(); | ||
console.log( | ||
chalk.blue(`\n⏸️ Download interrupted. Progress saved.`) | ||
); | ||
console.log(chalk.blue(`💡 Run the same command again to resume.`)); | ||
console.log(chalk.blue(`\nDownload interrupted. Progress saved.`)); | ||
console.log(chalk.blue(`Run the same command again to resume.`)); | ||
return; | ||
@@ -813,3 +833,3 @@ } | ||
console.log( | ||
chalk.blue(`💡 Run the same command again to retry failed downloads`) | ||
chalk.blue(`Run the same command again to retry failed downloads`) | ||
); | ||
@@ -820,3 +840,3 @@ | ||
console.log( | ||
chalk.red(`❌ Download failed: No files were downloaded successfully`) | ||
chalk.red(`Download failed: No files were downloaded successfully`) | ||
); | ||
@@ -830,3 +850,3 @@ return { | ||
} else { | ||
console.log(chalk.yellow(`⚠️ Download completed with errors`)); | ||
console.log(chalk.yellow(`Download completed with errors`)); | ||
return { | ||
@@ -841,3 +861,3 @@ success: false, | ||
console.log( | ||
chalk.green(`🎉 All ${succeeded} files downloaded successfully!`) | ||
chalk.green(`All ${succeeded} files downloaded successfully!`) | ||
); | ||
@@ -859,5 +879,5 @@ resumeManager.cleanupCheckpoint(url, outputDir); | ||
console.error(chalk.red(`❌ Error downloading folder: ${error.message}`)); | ||
console.error(chalk.red(`Error downloading folder: ${error.message}`)); | ||
throw error; | ||
} | ||
}; |
@@ -79,3 +79,3 @@ import { program } from "commander"; | ||
console.log(chalk.cyan("\n📋 Download Checkpoints:")); | ||
console.log(chalk.cyan("\nDownload Checkpoints:")); | ||
checkpoints.forEach((cp, index) => { | ||
@@ -155,8 +155,8 @@ console.log(chalk.blue(`\n${index + 1}. ID: ${cp.id}`)); | ||
operationType === "archive" | ||
? `❌ Archive creation failed: ${error.message}` | ||
: `❌ Download failed: ${error.message}`; | ||
? `Archive creation failed: ${error.message}` | ||
: `Download failed: ${error.message}`; | ||
console.error(chalk.red(failMsg)); | ||
process.exit(1); | ||
} else if (!createArchive && result && !result.success) { | ||
console.error(chalk.red(`❌ Download failed`)); | ||
console.error(chalk.red(`Download failed`)); | ||
process.exit(1); | ||
@@ -163,0 +163,0 @@ } else if (!createArchive && result && result.isEmpty) { |
@@ -19,6 +19,6 @@ export function parseGitHubUrl(url) { | ||
// Extract components from the matched pattern | ||
const owner = match[1]; | ||
const repo = match[2]; | ||
const branch = match[3]; // Branch might not be in the URL for root downloads | ||
const folderPath = match[4] || ""; // Empty string if no folder path | ||
const owner = decodeURIComponent(match[1]); | ||
const repo = decodeURIComponent(match[2]); | ||
const branch = match[3] ? decodeURIComponent(match[3]) : ""; // Branch is an empty string if not present | ||
const folderPath = match[4] ? decodeURIComponent(match[4]) : ""; // Empty string if no folder path | ||
@@ -25,0 +25,0 @@ // Additional validation |
60722
0.96%1330
1.53%