Skip to content

feat(core): fall back to the CoDev mirror for ripgrep downloads - #147

Merged
quickbeard merged 2 commits into
mainfrom
rg-mirror-fallback
Aug 1, 2026
Merged

quickbeard merged 2 commits into
mainfrom
rg-mirror-fallback

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Summary

When rg is neither on PATH nor in ~/.cache/codev/bin, CoDev Code downloads it from GitHub releases on first use — which corporate networks commonly block. PR #146 made that failure visible and retryable, and codevhub#230 stages the binary at install time, but users who never ran codevhub (fresh machine, cleared cache) were still stuck. This adds a second source inside the agent itself: GitHub first, then the CoDev landing page's mirror of the official 15.1.0 executables (quickbeard/codev-landing-page#64, rg-<version>-<platform>-<arch>[.exe]).

Changes

  • packages/core/src/ripgrep/binary.ts
    • The download tail is split into fromGithub (unchanged archive → extract flow) and fromMirror (raw executable, no extraction — staged in a scoped temp dir, then copied to the target + chmod; the cache check is bare file existence, so a partial target must never appear).
    • Fallback engages via Effect.catchCause, not Effect.catch: a corporate proxy block page returns 200 + HTML, which passes filterStatusOk and then dies inside extract as a defect — that case must reach the mirror too.
    • Each attempt is bounded to 30s (Effect.timeout) so a blackholing proxy can't hang first-time file search on OS TCP timeouts before the fallback is even tried.
    • Empty response bodies are now a typed failure (Effect.fail) rather than a defect, so they trigger the fallback as well.
  • packages/core/test/ripgrep-binary.test.ts (new) — first coverage of binary resolution at all, using the mocked-HTTP-client pattern from models.test.ts (AppNodeBuilder + Layer.fresh): fallback order (GitHub URL then exact mirror URL — pinning the hosted-filename contract), bytes land at the target with the exec bit and no leftover staging dir, success is cached (no further requests), failure is not cached (recovers mid-session), and an empty mirror body never creates the target.
  • packages/core/test/preload.ts — redirects XDG_CACHE_HOME to a fresh temp dir for the whole suite, so tests that exercise real binary resolution can never read or clobber the developer's actual ~/.cache/codev (the new tests write fake bytes at the rg target path; models.test.ts's cache writes also become hermetic).
  • Docs — extended the existing PR-146 DIVERGENCES row (mirror behavior, new files, and the three-way version-pin invariant with codev-landing-page and codevhub) and added a CHANGELOG entry under Unreleased.

Cross-repo invariant

VERSION here, RG_VERSION in codevhub, and the landing page's hosted filenames must move together on a ripgrep bump — host new binaries first. Until codev-landing-page#64 deploys, the mirror GET 404s and behavior degrades to exactly today's GitHub-only failure.

Test plan

  • bun typecheck in packages/core and packages/opencode
  • Full core suite from packages/core: 1083 pass, 0 fail (caught and fixed a leak where the fake test binary outlived the file and broke the live ripgrep suites)
  • New tests pass in isolation and alongside test/ripgrep.test.ts + test/filesystem/
  • Manual on a GitHub-blocked network once the landing-page PR deploys: first @ search downloads via the mirror (log shows "github ripgrep download failed, trying the CoDev mirror")

🤖 Generated with Claude Code

quickbeard and others added 2 commits August 1, 2026 09:14
When rg is neither on PATH nor cached, CoDev Code downloads it from
GitHub releases on first use — which corporate networks commonly block,
leaving file search empty until the user intervenes (PR #146 made that
failure visible and retryable; codevhub#230 stages the binary at install
time, but only for users who ran codevhub).

The download tail now tries GitHub first and falls back to the landing
page's mirror of the official executables (codev-landing-page#64), which
hosts them raw — no archive, so the mirror path skips extraction and
stages the binary through a scoped temp dir before copying into place
(the cache check is bare existence, so a partial target must never
appear). Each attempt is bounded to 30s so a blackholing proxy can't
stall first search for minutes. The fallback engages via catchCause, not
catch: a proxy block page returns 200 + HTML, which passes
filterStatusOk and dies in extract as a defect, and that case must reach
the mirror too.

New mocked-HTTP tests cover the fallback order, success caching, and
failure non-caching; test/preload.ts now redirects XDG_CACHE_HOME to a
temp dir so tests exercising the real binary-resolution path can never
touch the developer's ~/.cache/codev.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Same convention as codev-gateway's const.ts: keeps the host out of
casual greps, not a secret. The test's plain-text URL assertion now
doubles as proof the encoded value decodes to the right mirror.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@quickbeard
quickbeard merged commit eb52e13 into main Aug 1, 2026
1 of 5 checks passed
@quickbeard
quickbeard deleted the rg-mirror-fallback branch August 1, 2026 03:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant