mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-17 08:41:19 -07:00
feat: bundle ripgrep binaries into SEA for offline support (#25342)
This commit is contained in:
committed by
GitHub
parent
366f9e4766
commit
55620235c0
@@ -108,4 +108,16 @@ if (!existsSync(bundleMcpSrc)) {
|
||||
cpSync(bundleMcpSrc, bundleMcpDest, { recursive: true, dereference: true });
|
||||
console.log('Copied bundled chrome-devtools-mcp to bundle/bundled/');
|
||||
|
||||
// 7. Copy pre-built ripgrep vendor binaries
|
||||
const ripgrepVendorSrc = join(root, 'packages/core/vendor/ripgrep');
|
||||
const ripgrepVendorDest = join(bundleDir, 'vendor', 'ripgrep');
|
||||
if (existsSync(ripgrepVendorSrc)) {
|
||||
mkdirSync(ripgrepVendorDest, { recursive: true });
|
||||
cpSync(ripgrepVendorSrc, ripgrepVendorDest, {
|
||||
recursive: true,
|
||||
dereference: true,
|
||||
});
|
||||
console.log('Copied ripgrep vendor binaries to bundle/vendor/ripgrep/');
|
||||
}
|
||||
|
||||
console.log('Assets copied to bundle/');
|
||||
|
||||
146
scripts/download-ripgrep-binaries.ts
Normal file
146
scripts/download-ripgrep-binaries.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @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);
|
||||
});
|
||||
Reference in New Issue
Block a user