Class: Bundler::Spinel::Proxy
- Inherits:
-
Object
- Object
- Bundler::Spinel::Proxy
- Defined in:
- lib/bundler/spinel/proxy.rb
Overview
A curated RubyGems source (Compact Index protocol) that serves only vetted gems. Point a Gemfile at it:
source "http://localhost:9292"
gem "cleangem"
and ‘bundle lock` resolves *only against vetted gems*. A gem that isn’t served (no acceptable verdict for the pinned engine rev) becomes a plain “could not find compatible versions” resolution failure — no plugin, no engine-directive trick. The whitelist is not a file: it’s the acceptable subset of the ledger at this rev, plus a local store of .gem artifacts we built/verified ourselves.
Empirically the third-party rubygems ecosystem is ~all-rejected today, so the load-bearing mode is ‘:store` — a directory of our own Spinel-vetted .gem files. (Filtered read-through of upstream is a documented extension.)
NOTE: this CRuby/WEBrick implementation is the MVP that proves Bundler resolves against it. The dogfood target is to serve the same endpoints from a Spinel-compiled Tep app — see ARCHITECTURE.md §“Dogfooding”.
Instance Method Summary collapse
-
#catalog ⇒ Object
name => { version => spec } for every vetted gem in the store.
-
#initialize(store:, ledger: Ledger.new, engine: Engine.new, min_verdict: :verified) ⇒ Proxy
constructor
store: dir of vetted *.gem files (the curated artifacts).
-
#mount_on(server) ⇒ Object
Mount the Compact Index endpoints onto an existing WEBrick server, so a combined server (Server) can serve the human site statically and the curated source from the same process — the apex double-duty layout.
- #serve(port: 9292, quiet: true) ⇒ Object
-
#write_static(out) ⇒ Object
Write the curated source as a static Compact Index tree: out/names out/versions out/info/<gem> out/gems/<file>.gem All digest/JSON happens here, offline, in CRuby.
Constructor Details
#initialize(store:, ledger: Ledger.new, engine: Engine.new, min_verdict: :verified) ⇒ Proxy
store: dir of vetted *.gem files (the curated artifacts).
31 32 33 34 35 36 37 |
# File 'lib/bundler/spinel/proxy.rb', line 31 def initialize(store:, ledger: Ledger.new, engine: Engine.new, min_verdict: :verified) @store = store @ledger = ledger @engine = engine @min_verdict = min_verdict end |
Instance Method Details
#catalog ⇒ Object
name => { version => spec } for every vetted gem in the store.
40 41 42 43 44 45 46 47 |
# File 'lib/bundler/spinel/proxy.rb', line 40 def catalog @catalog ||= Dir[File.join(@store, "*.gem")].each_with_object({}) do |path, acc| spec = Gem::Package.new(path).spec next unless acceptable?(spec.name, spec.version.to_s) (acc[spec.name] ||= {})[spec.version.to_s] = { spec: spec, path: path } end end |
#mount_on(server) ⇒ Object
Mount the Compact Index endpoints onto an existing WEBrick server, so a combined server (Server) can serve the human site statically and the curated source from the same process — the apex double-duty layout.
83 |
# File 'lib/bundler/spinel/proxy.rb', line 83 def mount_on(server) = mount(server) |
#serve(port: 9292, quiet: true) ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/bundler/spinel/proxy.rb', line 67 def serve(port: 9292, quiet: true) server = WEBrick::HTTPServer.new( Port: port, Logger: WEBrick::Log.new(quiet ? File::NULL : $stderr), AccessLog: [] ) mount(server) trap("INT") { server.shutdown } warn "[spinel-proxy] curated source on http://localhost:#{port} " \ "(#{catalog.size} gems, min=#{@min_verdict}, rev=#{@engine.rev})" server.start end |
#write_static(out) ⇒ Object
Write the curated source as a static Compact Index tree:
out/names out/versions out/info/<gem> out/gems/<file>.gem
All digest/JSON happens here, offline, in CRuby. The result is plain text + file bytes — so the dogfood server (Tep/Spinel, which has neither digest nor JSON — probed 2026-05-26) only has to serve static files.
54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/bundler/spinel/proxy.rb', line 54 def write_static(out) require "fileutils" FileUtils.mkdir_p(File.join(out, "info")) FileUtils.mkdir_p(File.join(out, "gems")) File.write(File.join(out, "names"), names_body) File.write(File.join(out, "versions"), versions_body) catalog.each_key { |name| File.write(File.join(out, "info", name), info_body(name)) } catalog.values.flat_map(&:values).each do |e| FileUtils.cp(e[:path], File.join(out, "gems", File.basename(e[:path]))) end out end |