#!/usr/bin/env node /** * Download ripgrep binaries for all target platforms * This script downloads official ripgrep binaries from GitHub releases * and extracts them to dist-standalone/ripgrep-binaries/ */ import { exec } from "child_process" import fs from "fs" import https from "https" import path from "path" import { pipeline } from "stream/promises" import * as tar from "tar" import { promisify } from "util" import { createGunzip } from "zlib" const execAsync = promisify(exec) const RIPGREP_VERSION = "14.1.1" const OUTPUT_DIR = "dist-standalone/ripgrep-binaries" // Platform configurations const PLATFORMS = [ { name: "darwin-x64", archiveName: `ripgrep-${RIPGREP_VERSION}-x86_64-apple-darwin.tar.gz`, url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-x86_64-apple-darwin.tar.gz`, binaryPath: "rg", isZip: false, }, { name: "darwin-arm64", archiveName: `ripgrep-${RIPGREP_VERSION}-aarch64-apple-darwin.tar.gz`, url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-aarch64-apple-darwin.tar.gz`, binaryPath: "rg", isZip: false, }, { name: "linux-x64", archiveName: `ripgrep-${RIPGREP_VERSION}-x86_64-unknown-linux-musl.tar.gz`, url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-x86_64-unknown-linux-musl.tar.gz`, binaryPath: "rg", isZip: false, }, { name: "linux-arm64", archiveName: `ripgrep-${RIPGREP_VERSION}-aarch64-unknown-linux-gnu.tar.gz`, url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-aarch64-unknown-linux-gnu.tar.gz`, binaryPath: "rg", isZip: false, }, { name: "win-x64", archiveName: `ripgrep-${RIPGREP_VERSION}-x86_64-pc-windows-msvc.zip`, url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/ripgrep-${RIPGREP_VERSION}-x86_64-pc-windows-msvc.zip`, binaryPath: "rg.exe", isZip: true, }, ] /** * Download a file from a URL */ async function downloadFile(url, destPath) { return new Promise((resolve, reject) => { console.log(` Downloading: ${url}`) const file = fs.createWriteStream(destPath) https .get(url, (response) => { if (response.statusCode === 302 || response.statusCode === 301) { // Handle redirect return downloadFile(response.headers.location, destPath).then(resolve).catch(reject) } if (response.statusCode !== 200) { reject(new Error(`Failed to download: ${response.statusCode} ${response.statusMessage}`)) return } response.pipe(file) file.on("finish", () => { file.close() resolve() }) }) .on("error", (err) => { fs.unlink(destPath, () => {}) // Delete the file on error reject(err) }) file.on("error", (err) => { fs.unlink(destPath, () => {}) // Delete the file on error reject(err) }) }) } /** * Extract a tar.gz file */ async function extractTarGz(tarPath, destDir) { console.log(` Extracting tar.gz to: ${destDir}`) return pipeline( fs.createReadStream(tarPath), createGunzip(), tar.extract({ cwd: destDir, strip: 1, // Remove the top-level directory from the archive }), ) } /** * Extract a zip file using unzip command */ async function extractZip(zipPath, destDir) { console.log(` Extracting zip to: ${destDir}`) try { // Use -o to overwrite existing files without prompting await execAsync(`unzip -o -q "${zipPath}" -d "${destDir}"`) // Find the extracted directory (usually ripgrep-VERSION-arch) const items = fs.readdirSync(destDir) const extractedDir = items.find((item) => item.startsWith("ripgrep-")) if (extractedDir) { // Move files from subdirectory to destDir const subDir = path.join(destDir, extractedDir) const files = fs.readdirSync(subDir) for (const file of files) { const srcPath = path.join(subDir, file) const destPath = path.join(destDir, file) // Remove destination if it exists (to avoid ENOTEMPTY error) if (fs.existsSync(destPath)) { const stats = fs.statSync(destPath) if (stats.isDirectory()) { fs.rmSync(destPath, { recursive: true, force: true }) } else { fs.unlinkSync(destPath) } } fs.renameSync(srcPath, destPath) } // Remove the now-empty subdirectory fs.rmdirSync(subDir) } } catch (error) { throw new Error(`Failed to extract zip: ${error.message}`) } } /** * Download and extract ripgrep for a specific platform */ async function downloadRipgrepForPlatform(platform) { console.log(`\nšŸ“¦ Processing ${platform.name}...`) const platformDir = path.join(OUTPUT_DIR, platform.name) const archivePath = path.join(OUTPUT_DIR, platform.archiveName) // Create output directory fs.mkdirSync(platformDir, { recursive: true }) try { // Download await downloadFile(platform.url, archivePath) console.log(` āœ“ Downloaded`) // Extract if (platform.isZip) { await extractZip(archivePath, platformDir) } else { await extractTarGz(archivePath, platformDir) } console.log(` āœ“ Extracted`) // Verify the binary exists const binaryPath = path.join(platformDir, platform.binaryPath) if (!fs.existsSync(binaryPath)) { throw new Error(`Binary not found at ${binaryPath}`) } // Make binary executable (Unix only) if (!platform.isZip) { fs.chmodSync(binaryPath, 0o755) } console.log(` āœ“ Binary ready: ${binaryPath}`) // Clean up archive file fs.unlinkSync(archivePath) console.log(` āœ“ Cleaned up`) return true } catch (error) { console.error(` āœ— Failed: ${error.message}`) throw error } } /** * Main function */ async function main() { console.log("šŸš€ Ripgrep Binary Downloader") console.log(` Version: ${RIPGREP_VERSION}`) console.log(` Output: ${OUTPUT_DIR}`) // Create output directory fs.mkdirSync(OUTPUT_DIR, { recursive: true }) // Download for all platforms const results = [] for (const platform of PLATFORMS) { try { await downloadRipgrepForPlatform(platform) results.push({ platform: platform.name, success: true }) } catch (error) { results.push({ platform: platform.name, success: false, error: error.message }) } } // Print summary console.log("\n" + "=".repeat(50)) console.log("šŸ“Š Summary:") console.log("=".repeat(50)) let successCount = 0 for (const result of results) { const status = result.success ? "āœ…" : "āŒ" console.log(`${status} ${result.platform}`) if (result.success) { successCount++ } else { console.log(` Error: ${result.error}`) } } console.log("=".repeat(50)) console.log(`āœ“ ${successCount}/${PLATFORMS.length} platforms successful`) if (successCount < PLATFORMS.length) { process.exit(1) } console.log("\nāœ… All ripgrep binaries downloaded successfully!") } // Run the script main().catch((error) => { console.error("\nāŒ Fatal error:", error) process.exit(1) })