mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-17 00:31:44 -07:00
147 lines
5.0 KiB
TypeScript
147 lines
5.0 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2026 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview This script downloads pre-built ripgrep binaries for all supported
|
|
* architectures and platforms. These binaries are checked into the repository
|
|
* under packages/core/vendor/ripgrep.
|
|
*
|
|
* Maintainers should periodically run this script to upgrade the version
|
|
* of ripgrep being distributed.
|
|
*
|
|
* Usage: npx tsx scripts/download-ripgrep-binaries.ts
|
|
*/
|
|
|
|
import fs from 'node:fs';
|
|
import fsPromises from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { pipeline } from 'node:stream/promises';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { createWriteStream } from 'node:fs';
|
|
import { Readable } from 'node:stream';
|
|
import type { ReadableStream } from 'node:stream/web';
|
|
import { execFileSync } from 'node:child_process';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const CORE_VENDOR_DIR = path.join(__dirname, '../packages/core/vendor/ripgrep');
|
|
const VERSION = 'v13.0.0-10';
|
|
|
|
interface Target {
|
|
platform: string;
|
|
arch: string;
|
|
file: string;
|
|
}
|
|
|
|
const targets: Target[] = [
|
|
{ platform: 'darwin', arch: 'arm64', file: 'aarch64-apple-darwin.tar.gz' },
|
|
{ platform: 'darwin', arch: 'x64', file: 'x86_64-apple-darwin.tar.gz' },
|
|
{
|
|
platform: 'linux',
|
|
arch: 'arm64',
|
|
file: 'aarch64-unknown-linux-gnu.tar.gz',
|
|
},
|
|
{ platform: 'linux', arch: 'x64', file: 'x86_64-unknown-linux-musl.tar.gz' },
|
|
{ platform: 'win32', arch: 'x64', file: 'x86_64-pc-windows-msvc.zip' },
|
|
];
|
|
|
|
async function downloadBinary() {
|
|
await fsPromises.mkdir(CORE_VENDOR_DIR, { recursive: true });
|
|
|
|
for (const target of targets) {
|
|
const url = `https://github.com/microsoft/ripgrep-prebuilt/releases/download/${VERSION}/ripgrep-${VERSION}-${target.file}`;
|
|
const archivePath = path.join(CORE_VENDOR_DIR, target.file);
|
|
const binName = `rg-${target.platform}-${target.arch}${target.platform === 'win32' ? '.exe' : ''}`;
|
|
const finalBinPath = path.join(CORE_VENDOR_DIR, binName);
|
|
|
|
if (fs.existsSync(finalBinPath)) {
|
|
console.log(`[Cache] ${binName} already exists.`);
|
|
continue;
|
|
}
|
|
|
|
console.log(`[Download] ${url} -> ${archivePath}`);
|
|
const response = await fetch(url);
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
}
|
|
|
|
if (!response.body) {
|
|
throw new Error(`Response body is null for ${url}`);
|
|
}
|
|
|
|
const fileStream = createWriteStream(archivePath);
|
|
|
|
// Node 18+ global fetch response.body is a ReadableStream (web stream)
|
|
// pipeline(Readable.fromWeb(response.body), fileStream) works in Node 18+
|
|
await pipeline(
|
|
Readable.fromWeb(response.body as ReadableStream),
|
|
fileStream,
|
|
);
|
|
|
|
console.log(`[Extract] Extracting ${archivePath}...`);
|
|
// Extract using shell commands for simplicity
|
|
if (target.file.endsWith('.tar.gz')) {
|
|
execFileSync('tar', ['-xzf', archivePath, '-C', CORE_VENDOR_DIR]);
|
|
// Microsoft's ripgrep release extracts directly to `rg` inside the current directory sometimes
|
|
const sourceBin = path.join(CORE_VENDOR_DIR, 'rg');
|
|
if (fs.existsSync(sourceBin)) {
|
|
await fsPromises.rename(sourceBin, finalBinPath);
|
|
} else {
|
|
// Fallback for sub-directory if it happens
|
|
const extractedDirName = `ripgrep-${VERSION}-${target.file.replace('.tar.gz', '')}`;
|
|
const fallbackSourceBin = path.join(
|
|
CORE_VENDOR_DIR,
|
|
extractedDirName,
|
|
'rg',
|
|
);
|
|
if (fs.existsSync(fallbackSourceBin)) {
|
|
await fsPromises.rename(fallbackSourceBin, finalBinPath);
|
|
await fsPromises.rm(path.join(CORE_VENDOR_DIR, extractedDirName), {
|
|
recursive: true,
|
|
force: true,
|
|
});
|
|
} else {
|
|
throw new Error(
|
|
`Could not find extracted 'rg' binary for ${target.platform} ${target.arch}`,
|
|
);
|
|
}
|
|
}
|
|
} else if (target.file.endsWith('.zip')) {
|
|
execFileSync('unzip', ['-o', '-q', archivePath, '-d', CORE_VENDOR_DIR]);
|
|
const sourceBin = path.join(CORE_VENDOR_DIR, 'rg.exe');
|
|
if (fs.existsSync(sourceBin)) {
|
|
await fsPromises.rename(sourceBin, finalBinPath);
|
|
} else {
|
|
const extractedDirName = `ripgrep-${VERSION}-${target.file.replace('.zip', '')}`;
|
|
const fallbackSourceBin = path.join(
|
|
CORE_VENDOR_DIR,
|
|
extractedDirName,
|
|
'rg.exe',
|
|
);
|
|
if (fs.existsSync(fallbackSourceBin)) {
|
|
await fsPromises.rename(fallbackSourceBin, finalBinPath);
|
|
await fsPromises.rm(path.join(CORE_VENDOR_DIR, extractedDirName), {
|
|
recursive: true,
|
|
force: true,
|
|
});
|
|
} else {
|
|
throw new Error(
|
|
`Could not find extracted 'rg.exe' binary for ${target.platform} ${target.arch}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean up archive
|
|
await fsPromises.unlink(archivePath);
|
|
console.log(`[Success] Saved to ${finalBinPath}`);
|
|
}
|
|
}
|
|
|
|
downloadBinary().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|