download-all-ripgrep.cjs•6.53 kB
#!/usr/bin/env node
/**
* Download all ripgrep binaries for cross-platform MCPB bundles
*
* This script downloads ripgrep binaries for all supported platforms
* and places them in node_modules/@vscode/ripgrep/bin/ with platform-specific names.
*/
const https = require('https');
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const yauzl = require('yauzl');
const { pipeline } = require('stream');
const streamPipeline = promisify(pipeline);
// Ripgrep versions from @vscode/ripgrep
const VERSION = 'v13.0.0-10';
const MULTI_ARCH_VERSION = 'v13.0.0-4';
// All supported platforms
const PLATFORMS = [
{ name: 'x86_64-apple-darwin', ext: 'tar.gz', version: VERSION },
{ name: 'aarch64-apple-darwin', ext: 'tar.gz', version: VERSION },
{ name: 'x86_64-pc-windows-msvc', ext: 'zip', version: VERSION },
{ name: 'aarch64-pc-windows-msvc', ext: 'zip', version: VERSION },
{ name: 'i686-pc-windows-msvc', ext: 'zip', version: VERSION },
{ name: 'x86_64-unknown-linux-musl', ext: 'tar.gz', version: VERSION },
{ name: 'aarch64-unknown-linux-musl', ext: 'tar.gz', version: VERSION },
{ name: 'i686-unknown-linux-musl', ext: 'tar.gz', version: VERSION },
{ name: 'arm-unknown-linux-gnueabihf', ext: 'tar.gz', version: MULTI_ARCH_VERSION },
{ name: 'powerpc64le-unknown-linux-gnu', ext: 'tar.gz', version: MULTI_ARCH_VERSION },
{ name: 's390x-unknown-linux-gnu', ext: 'tar.gz', version: VERSION }
];
const TEMP_DIR = path.join(__dirname, '../.ripgrep-downloads');
const OUTPUT_DIR = path.join(__dirname, '../node_modules/@vscode/ripgrep/bin');
function ensureDir(dir) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
async function downloadFile(url, dest) {
return new Promise((resolve, reject) => {
console.log(` 📥 ${path.basename(dest)}...`);
https.get(url, {
headers: {
'User-Agent': 'vscode-ripgrep',
'Accept': 'application/octet-stream'
}
}, (response) => {
if (response.statusCode === 302 || response.statusCode === 301) {
// Follow redirect
downloadFile(response.headers.location, dest).then(resolve).catch(reject);
return;
}
if (response.statusCode !== 200) {
reject(new Error(`Failed to download: ${response.statusCode}`));
return;
}
const file = fs.createWriteStream(dest);
response.pipe(file);
file.on('finish', () => {
file.close();
resolve();
});
file.on('error', (err) => {
fs.unlink(dest, () => reject(err));
});
}).on('error', reject);
});
}
async function extractZip(zipPath, outputDir, targetBinary) {
return new Promise((resolve, reject) => {
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
if (err) return reject(err);
zipfile.readEntry();
zipfile.on('entry', (entry) => {
if (entry.fileName.endsWith('rg.exe')) {
zipfile.openReadStream(entry, (err, readStream) => {
if (err) return reject(err);
const outputPath = path.join(outputDir, targetBinary);
const writeStream = fs.createWriteStream(outputPath);
readStream.pipe(writeStream);
writeStream.on('close', () => {
zipfile.close();
resolve();
});
writeStream.on('error', reject);
});
} else {
zipfile.readEntry();
}
});
zipfile.on('end', resolve);
zipfile.on('error', reject);
});
});
}
async function extractTarGz(tarPath, outputDir, targetBinary) {
return new Promise((resolve, reject) => {
const { execSync } = require('child_process');
const tempExtractDir = path.join(TEMP_DIR, 'extract-' + Date.now());
try {
ensureDir(tempExtractDir);
// Extract tar.gz
execSync(`tar -xzf "${tarPath}" -C "${tempExtractDir}"`, { stdio: 'pipe' });
// Find the rg binary
const files = fs.readdirSync(tempExtractDir, { recursive: true });
const rgFile = files.find(f => f.endsWith('/rg') || f === 'rg');
if (rgFile) {
const sourcePath = path.join(tempExtractDir, rgFile);
const destPath = path.join(outputDir, targetBinary);
fs.copyFileSync(sourcePath, destPath);
fs.chmodSync(destPath, 0o755);
}
// Cleanup
fs.rmSync(tempExtractDir, { recursive: true, force: true });
resolve();
} catch (error) {
reject(error);
}
});
}
async function downloadAndExtractPlatform(platform) {
const { name, ext, version } = platform;
const fileName = `ripgrep-${version}-${name}.${ext}`;
const url = `https://github.com/microsoft/ripgrep-prebuilt/releases/download/${version}/${fileName}`;
const tempFile = path.join(TEMP_DIR, fileName);
// Determine output binary name
const isWindows = name.includes('windows');
const binaryName = isWindows ? `rg-${name}.exe` : `rg-${name}`;
const outputPath = path.join(OUTPUT_DIR, binaryName);
// Skip if binary already exists
if (fs.existsSync(outputPath)) {
console.log(` ✓ ${name} (cached)`);
return;
}
try {
// Download if not already cached
if (!fs.existsSync(tempFile)) {
await downloadFile(url, tempFile);
}
// Extract
ensureDir(OUTPUT_DIR);
if (ext === 'zip') {
await extractZip(tempFile, OUTPUT_DIR, binaryName);
} else {
await extractTarGz(tempFile, OUTPUT_DIR, binaryName);
}
console.log(` ✓ ${name}`);
} catch (error) {
console.error(` ✗ Failed: ${name} - ${error.message}`);
throw error;
}
}
async function main() {
console.log('🌍 Downloading ripgrep binaries for all platforms...\n');
ensureDir(TEMP_DIR);
ensureDir(OUTPUT_DIR);
// Download and extract all platforms
for (const platform of PLATFORMS) {
await downloadAndExtractPlatform(platform);
}
console.log('\n✅ All ripgrep binaries downloaded successfully!');
console.log(`📁 Binaries location: ${OUTPUT_DIR}`);
console.log('\nFiles created:');
const files = fs.readdirSync(OUTPUT_DIR).filter(f => f.startsWith('rg-'));
files.forEach(f => console.log(` - ${f}`));
}
// Run if called directly
if (require.main === module) {
main().catch((error) => {
console.error('❌ Error:', error);
process.exit(1);
});
}
module.exports = { downloadAndExtractPlatform, PLATFORMS };