Skip to content

feat: bundle ripgrep binaries into SEA for offline support#25332

Closed
jacob314 wants to merge 1 commit intomainfrom
ripgrep
Closed

feat: bundle ripgrep binaries into SEA for offline support#25332
jacob314 wants to merge 1 commit intomainfrom
ripgrep

Conversation

@jacob314
Copy link
Copy Markdown
Contributor

@jacob314 jacob314 commented Apr 13, 2026

Summary

This PR bundles ripgrep native binaries directly into the Gemini CLI's Single Executable Application (SEA). This solves a critical issue for enterprise users in air-gapped or offline environments, where the CLI would previously hang or fail while attempting to download ripgrep at runtime.

Details

  • Binary Bundling: Pre-built ripgrep binaries for the 5 major architectures (Linux x64/arm64, macOS x64/arm64, Windows x64) are now checked into packages/core/vendor/ripgrep.
  • Asset Management: Updated scripts/copy_bundle_assets.js to ensure the vendor directory is included in the bundle/ folder during the SEA build process.
  • Path Resolution: Refactored packages/core/src/tools/ripGrep.ts to asynchronously check multiple potential paths for the rg executable, supporting both local development and the flattened SEA runtime environment.
  • Maintainer Tooling: Added scripts/download-ripgrep-binaries.js with proper Apache license headers and documentation to allow maintainers to easily update the checked-in binaries.
  • Robust Testing: Updated the test suite to use vi.spyOn for platform mocking and added parameterized tests to verify correct path resolution across all supported operating systems.

Related Issues

Fixes #25332 (Implementation)

How to Validate

  1. Run node scripts/download-ripgrep-binaries.js to verify the maintainer script correctly fetches all 5 architectures.
  2. Run npm run build followed by npm run test -w @google/gemini-cli-core -- src/tools/ripGrep.test.ts to verify unit tests pass.
  3. Run node scripts/build_binary.js to generate a native SEA executable.
  4. Disconnect from the internet and run the generated binary with a search command (e.g., ./dist/darwin-arm64/gemini -e "search for hello") to verify ripgrep works without a network connection.

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
    • Linux
      • Docker

@jacob314 jacob314 requested review from a team as code owners April 13, 2026 20:12
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request transitions the project from dynamically downloading the ripgrep binary at runtime to using a pre-bundled set of platform-specific binaries stored in a vendor directory. This change enhances the stability of the tool by removing external network dependencies during execution and simplifies the binary management process.

Highlights

  • Vendor-based Ripgrep: Replaced the dynamic download of ripgrep with a bundled vendor-based approach to improve reliability and security.
  • Binary Management: Added a new script to download and manage platform-specific ripgrep binaries in the vendor directory.
  • Dependency Cleanup: Removed the '@joshua.litt/get-ripgrep' dependency as it is no longer required.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-cli
Copy link
Copy Markdown
Contributor

gemini-cli Bot commented Apr 13, 2026

Hi @jacob314, thank you so much for your contribution to Gemini CLI! We really appreciate the time and effort you've put into this.

We're making some updates to our contribution process to improve how we track and review changes. Please take a moment to review our recent discussion post: Improving Our Contribution Process & Introducing New Guidelines.

Key Update: Starting January 26, 2026, the Gemini CLI project will require all pull requests to be associated with an existing issue. Any pull requests not linked to an issue by that date will be automatically closed.

Thank you for your understanding and for being a part of our community!

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 13, 2026

🛑 Action Required: Evaluation Approval

Steering changes have been detected in this PR. To prevent regressions, a maintainer must approve the evaluation run before this PR can be merged.

Maintainers:

  1. Go to the Workflow Run Summary.
  2. Click the yellow 'Review deployments' button.
  3. Select the 'eval-gate' environment and click 'Approve'.

Once approved, the evaluation results will be posted here automatically.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 13, 2026

Size Change: -359 kB (-1.05%)

Total Size: 33.8 MB

Filename Size Change
./bundle/chunk-ECQ27BJ4.js 0 B -3.8 kB (removed) 🏆
./bundle/chunk-F4R7VVM6.js 0 B -3.54 MB (removed) 🏆
./bundle/chunk-QM5IP3NK.js 0 B -1.97 MB (removed) 🏆
./bundle/chunk-VMI7QYT2.js 0 B -14.9 MB (removed) 🏆
./bundle/core-XXLM4FSF.js 0 B -46.5 kB (removed) 🏆
./bundle/devtoolsService-MBMJIQXL.js 0 B -28.4 kB (removed) 🏆
./bundle/gemini-CXKVIWDI.js 0 B -553 kB (removed) 🏆
./bundle/interactiveCli-PQTIDREC.js 0 B -1.29 MB (removed) 🏆
./bundle/oauth2-provider-ZUSYXU6K.js 0 B -9.16 kB (removed) 🏆
./bundle/chunk-PC3Y4R7E.js 1.97 MB +1.97 MB (new file) 🆕
./bundle/chunk-RH2HLFXP.js 3.59 MB +3.59 MB (new file) 🆕
./bundle/chunk-S24G5DSG.js 14.5 MB +14.5 MB (new file) 🆕
./bundle/chunk-VD56KHQF.js 3.8 kB +3.8 kB (new file) 🆕
./bundle/core-4DEFWC7R.js 46.6 kB +46.6 kB (new file) 🆕
./bundle/devtoolsService-CN4AJ2V4.js 28.4 kB +28.4 kB (new file) 🆕
./bundle/gemini-IHRG7AH4.js 553 kB +553 kB (new file) 🆕
./bundle/interactiveCli-Z75KSIXX.js 1.29 MB +1.29 MB (new file) 🆕
./bundle/oauth2-provider-CQFQOJVP.js 9.16 kB +9.16 kB (new file) 🆕
ℹ️ View Unchanged
Filename Size Change
./bundle/bundled/third_party/index.js 8 MB 0 B
./bundle/chunk-34MYV7JD.js 2.45 kB 0 B
./bundle/chunk-5AUYMPVF.js 858 B 0 B
./bundle/chunk-5PS3AYFU.js 1.18 kB 0 B
./bundle/chunk-664ZODQF.js 124 kB 0 B
./bundle/chunk-DAHVX5MI.js 206 kB 0 B
./bundle/chunk-IUUIT4SU.js 56.5 kB 0 B
./bundle/chunk-RJTRUG2J.js 39.8 kB 0 B
./bundle/cleanup-O7JAON57.js 0 B -932 B (removed) 🏆
./bundle/devtools-36NN55EP.js 696 kB 0 B
./bundle/dist-T73EYRDX.js 356 B 0 B
./bundle/events-XB7DADIJ.js 418 B 0 B
./bundle/gemini.js 4.97 kB 0 B
./bundle/getMachineId-bsd-TXG52NKR.js 1.55 kB 0 B
./bundle/getMachineId-darwin-7OE4DDZ6.js 1.55 kB 0 B
./bundle/getMachineId-linux-SHIFKOOX.js 1.34 kB 0 B
./bundle/getMachineId-unsupported-5U5DOEYY.js 1.06 kB 0 B
./bundle/getMachineId-win-6KLLGOI4.js 1.72 kB 0 B
./bundle/memoryDiscovery-MYQ3ZWKM.js 0 B -980 B (removed) 🏆
./bundle/multipart-parser-KPBZEGQU.js 11.7 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/client/main.js 222 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/_client-assets.js 229 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/index.js 13.4 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/types.js 132 B 0 B
./bundle/sandbox-macos-permissive-open.sb 890 B 0 B
./bundle/sandbox-macos-permissive-proxied.sb 1.31 kB 0 B
./bundle/sandbox-macos-restrictive-open.sb 3.36 kB 0 B
./bundle/sandbox-macos-restrictive-proxied.sb 3.56 kB 0 B
./bundle/sandbox-macos-strict-open.sb 4.82 kB 0 B
./bundle/sandbox-macos-strict-proxied.sb 5.02 kB 0 B
./bundle/src-QVCVGIUX.js 47 kB 0 B
./bundle/tree-sitter-7U6MW5PS.js 274 kB 0 B
./bundle/tree-sitter-bash-34ZGLXVX.js 1.84 MB 0 B
./bundle/cleanup-SKHPLE3H.js 932 B +932 B (new file) 🆕
./bundle/memoryDiscovery-R4AKKXHF.js 980 B +980 B (new file) 🆕

compressed-size-action

Comment thread scripts/download-ripgrep-binaries.js Fixed
Comment thread scripts/download-ripgrep-binaries.js Fixed
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request transitions the ripgrep tool from dynamic runtime downloading to using bundled binaries. It removes the @joshua.litt/get-ripgrep dependency, adds a vendor directory for platform-specific binaries, and introduces a script to download these prebuilt binaries. The core logic and tests have been updated to resolve ripgrep paths from the local vendor directory. Feedback highlights a missing Apache-2.0 license header in the new script and suggests a null check for the fetch response body to avoid runtime errors.

@@ -0,0 +1,93 @@
import fs from 'node:fs';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The new script is missing the required Apache-2.0 license header. According to the repository style guide, all new source code files must include this header with the current year.

References
  1. License Headers: For all new source code files (.ts, .tsx, .js), include the Apache-2.0 license header with the current year. (e.g., Copyright 2026 Google LLC). This is enforced by ESLint. (link)

Comment thread scripts/download-ripgrep-binaries.js Outdated
const { Readable } = await import('node:stream');
const fileStream = createWriteStream(archivePath);
// @ts-ignore - response.body is a Web Stream, pipeline can handle it in newer Node
await pipeline(Readable.fromWeb(response.body), fileStream);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

response.body can be null according to the Fetch API specification (e.g., if the server returns a 204 No Content response). Passing null to Readable.fromWeb will cause a runtime TypeError. It is safer to verify that the body exists before attempting to stream it.

Suggested change
await pipeline(Readable.fromWeb(response.body), fileStream);
if (!response.body) {
throw new Error("Response body is null for " + url);
}
await pipeline(Readable.fromWeb(response.body), fileStream);

@gemini-cli gemini-cli Bot added the priority/p1 Important and should be addressed in the near term. label Apr 13, 2026
@scidomino scidomino changed the title ripgrep_cache feat: bundle ripgrep binaries into SEA for offline support Apr 13, 2026
// Extract using shell commands for simplicity
if (target.file.endsWith('.tar.gz')) {
const { execSync } = await import('node:child_process');
execSync(`tar -xzf ${archivePath} -C ${CORE_VENDOR_DIR}`);
}
} else if (target.file.endsWith('.zip')) {
const { execSync } = await import('node:child_process');
execSync(`unzip -o -q ${archivePath} -d ${CORE_VENDOR_DIR}`);
@scidomino
Copy link
Copy Markdown
Collaborator

Going to close this so I can open my own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority/p1 Important and should be addressed in the near term.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants