ReviewOS

stacks/buddy

New ecosystems: Python, Rust, Go, Ruby (+ pnpm catalogs, engines)

#1385
Closed chris opened this 9 days ago ·
9 days ago

Part of #1367 (Phase 3 — Dependency Engine v2).

Goal

Extend the dependency engine beyond JS/PHP/Actions/Docker/pkgx to the four most-requested ecosystems, plus close JS-side gaps. Renovate's breadth is its moat; each ecosystem we add compounds ours. Current inventory and writer paths live in src/scanner/package-scanner.ts (discovery), src/registry/registry-client.ts (versions), src/buddy.ts:generateAllFileUpdates:1108 (writers).

Ecosystems

1. Python — pyproject.toml (PEP 621 + poetry/uv), requirements*.txt

  • Registry: PyPI JSON API (https://pypi.org/pypi/{name}/json); version specifiers PEP 440 (NOT semver — needs a pep440.ts comparator)
  • Lockfiles: uv.lock, poetry.lock via regenerateLockFile pattern (src/utils/lock-file.ts) when the tool is present
  • OSV ecosystem: PyPI (extend toOsvEcosystem in src/services/security-advisories.ts — currently npm+Packagist only)

2. Rust — Cargo.toml (+ workspace deps)

  • Registry: crates.io API (https://crates.io/api/v1/crates/{name}); semver-compatible caret semantics
  • Cargo.lock regeneration via cargo update -p when available
  • OSV: crates.io

3. Go — go.mod

  • Registry: Go module proxy (https://proxy.golang.org/{module}/@v/list + @latest); pseudo-versions and /v2+ major-suffix paths handled
  • Writer: rewrite require lines; go.sum via go mod tidy when toolchain present, else PR notes the needed command
  • OSV: Go

4. Ruby — Gemfile

  • Registry: RubyGems API (https://rubygems.org/api/v1/versions/{name}/latest.json); pessimistic operator (~>) preserved
  • Gemfile.lock via bundle lock --update <gem> when available
  • OSV: RubyGems

JS-side gaps (same issue since they touch the same files)

  • pnpm catalogs (#117): parse pnpm-workspace.yaml catalog:/catalogs: sections; update catalog entries and treat catalog: protocol deps as resolvable
  • package.json engines bumping (with #1384 rules gating) — delivers #107
  • yarn/npm overrides/resolutions awareness: don't propose updates that overrides pin (surface via resolution-drift, #1372)

Architecture requirement (do this first)

Introduce an EcosystemAdapter interface so each ecosystem is a pluggable module instead of more methods on Buddy:

interface EcosystemAdapter {
  name: string
  detect(dir): Promise<string[]>                 // manifest paths
  parse(file): Promise<PackageDependency[]>
  latest(dep, opts): Promise<VersionInfo | null> // registry lookup w/ caching
  applyUpdate(fileContent, update): string       // formatting-preserving write
  postWrite?(dir): Promise<void>                 // lockfile regeneration
  osvEcosystem?: string
}

Migrate ONE existing ecosystem (composer is the cleanest) onto the interface as the reference implementation, then add the new four. Don't rewrite all existing ecosystems in this issue — follow-ups can migrate incrementally.

Tasks

  • EcosystemAdapter interface + registry + composer migration (reference impl)
  • Python adapter (incl. PEP 440 comparator with test vectors from the spec)
  • Rust, Go, Ruby adapters — each: fixtures, formatting-preservation tests, lockfile path, OSV wiring
  • pnpm catalogs (#117) + engines (#107) + overrides awareness
  • Grouping/labels: new ecosystem labels; PR body tables per ecosystem (extend src/pr/pr-generator.ts table generation)
  • Docs: per-ecosystem pages under docs/features/

Acceptance criteria

  • Fixture repos for each ecosystem produce correct, formatting-preserving update PRs with working lockfile regeneration when the toolchain exists (and a clear note when not)
  • OSV advisories surface for all four new ecosystems
  • #117's catalog fixture updates correctly

Dependencies

  • #1384 (rules) recommended first so new ecosystems get rule support from day one. Closes #117, delivers #107 (with I17).
9 days ago

Core landed in 48e81ff: the EcosystemAdapter interface plus Python, Rust, Go and Ruby.

Each adapter owns discovery, parsing, version comparison and a formatting-preserving write. Both belong to the adapter for concrete reasons — ~= and ~> are compatible-release policies that a shared rewriter would flatten into pins, and Python is PEP 440 where 1.0.post1 is newer than 1.0, so a shared comparator would propose a downgrade for every post-release on PyPI, forever.

Each declines what looks like a dependency but is not a version bump: Python's -e ./-r directives, Rust path and git deps, Go's // indirect requirements and pseudo-versions, Ruby's commented and path gems — and refuses yanked releases. Lockfile regeneration is best-effort: a runner without cargo still gets a PR saying what needs regenerating locally.

All four are wired into scanForUpdates, the write path, matchEcosystems rules, and OSV. Docs at docs/advanced/ecosystems.md.

Still open on this issue, deliberately not attempted:

  • pnpm catalogs (#117) — pnpm-workspace.yaml catalog:/catalogs:
  • package.json engines bumping (#107) — rules can already target it, the scanner still cannot detect it
  • yarn/npm overrides/resolutions awareness

These are JS-side and independent of the adapter work; each is now a small, well-scoped task against a seam that exists. Worth splitting into their own issues rather than leaving this one open indefinitely.

9 days ago

Complete as of bc1a59f. All items on this issue now shipped.

Core (48e81ff): the EcosystemAdapter interface plus Python, Rust, Go and Ruby. Each adapter owns discovery, parsing, comparison and a formatting-preserving write — both belong to the adapter for concrete reasons: ~= and ~> are compatible-release policies a shared rewriter would flatten into pins, and Python is PEP 440 where 1.0.post1 is newer than 1.0.

JS-side gaps (bc1a59f):

  • pnpm catalogs → #117
  • engines → #107
  • overrides/resolutions awareness — an update an override defeats is worse than no update: the PR changes package.json, passes CI, merges, and installs exactly the same tree. It looks like progress. Only an override that actually excludes the new version holds it back; one whose range already admits it is not in the way.

Docs at docs/advanced/ecosystems.md, including the three rules a new adapter should follow — each learned from a bug in these four.

Sign in to comment on this issue.