Class: Bundler::Spinel::Vendorer

Inherits:
Object
  • Object
show all
Defined in:
lib/bundler/spinel/vendorer.rb

Overview

“Make it work” — the plugin’s primary job. Spinel has no load path (plain ‘require “x”` resolves only against <spinel>/lib) and inlines `require_relative`. So to actually use a resolved dependency in a Spinel build, its source has to be placed somewhere Spinel will follow, with the require wiring generated. This is the reusable form of what projects do by hand today (e.g. Toy’s build_tep_app.sh concatenation, Roundhouse vendoring part of Tep).

Given a Gemfile.lock, vendor each gem’s ‘lib/` into `<into>/<name>/` and emit `<into>/deps.rb` — a manifest of `require_relative`s in lock order. A Spinel program then just does `require_relative “vendor/spinel/deps”`.

Gating is layered on but advisory here: placement and compatibility are different jobs. ‘vendor` warns on non-compatible gems (so the experience is nicer) but still places them; `check` is the hard gate.

Instance Method Summary collapse

Constructor Details

#initialize(engine: Engine.new, ledger: Ledger.new) ⇒ Vendorer

Returns a new instance of Vendorer.



25
26
27
28
29
# File 'lib/bundler/spinel/vendorer.rb', line 25

def initialize(engine: Engine.new, ledger: Ledger.new)
  @engine = engine
  @ledger = ledger
  @fetcher = GemFetcher.new
end

Instance Method Details

#resolve_source(spec, lock_dir) ⇒ Object

path:/git: lockfile sources (toy ↔ tep is the headline case) point at a local tree; we don’t go through ‘gem fetch`. For GEM sources we fall back to the cache-backed RubyGems fetcher. Issue: OriPekelman/spinelgems#3.



60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/bundler/spinel/vendorer.rb', line 60

def resolve_source(spec, lock_dir)
  src = spec.source
  if src.respond_to?(:path) && src.path
    path = src.path.to_s
    # Bundler stores PATH as relative-to-lockfile; resolve to abs.
    path = File.expand_path(path, lock_dir) unless File.absolute_path?(path)
    unless File.directory?(path)
      raise Error, "path: source for #{spec.name} not found: #{path}"
    end
    return path
  end
  @fetcher.fetch(spec.name, spec.version.to_s)
end

#vendor(lockfile = "Gemfile.lock", into: "vendor/spinel", warn_incompatible: true, ext_overrides: {}, ext_disable: []) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/bundler/spinel/vendorer.rb', line 31

def vendor(lockfile = "Gemfile.lock", into: "vendor/spinel", warn_incompatible: true,
           ext_overrides: {}, ext_disable: [])
  parsed = Bundler::LockfileParser.new(File.read(lockfile))
  lock_dir = File.dirname(File.expand_path(lockfile))
  into = File.expand_path(into)
  FileUtils.mkdir_p(into)
  disable = (ext_disable + ENV["SPINEL_EXT_DISABLE"].to_s.split(",")).map(&:strip).reject(&:empty?)

  manifest = []
  exts = 0
  parsed.specs.each do |spec|
    name = spec.name
    version = spec.version.to_s
    src = resolve_source(spec, lock_dir)
    dest = File.join(into, name)
    place(src, dest)
    exts += wire_extensions(src, dest, ext_overrides, disable)
    manifest << require_target(name, dest)
    note_compat(name, version) if warn_incompatible
  end

  write_manifest(into, manifest)
  { into: into, count: manifest.size, extensions: exts }
end