Class: Bundler::Spinel::Ledger

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

Overview

Append-only JSONL record of compatibility verdicts, one line per ‘(gem, version, engine_rev)`. The single source of truth that the lock-time gate, the curated-source whitelist, and the platform-variant opt-in are all views over.

Append-only because verdicts are facts-as-of-a-rev: we never mutate history, we add a newer probe. ‘lookup` returns the most recent matching line, so a re-probe naturally supersedes an older one.

Defined Under Namespace

Classes: Verdict

Constant Summary collapse

DEFAULT_PATH =
File.expand_path("../../../ledger/compat.jsonl", __dir__)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path: ENV.fetch("SPINEL_COMPAT_LEDGER", DEFAULT_PATH)) ⇒ Ledger

Returns a new instance of Ledger.



45
46
47
48
# File 'lib/bundler/spinel/ledger.rb', line 45

def initialize(path: ENV.fetch("SPINEL_COMPAT_LEDGER", DEFAULT_PATH))
  @path = path
  @write_mutex = Mutex.new
end

Instance Attribute Details

#pathObject (readonly)

Returns the value of attribute path.



43
44
45
# File 'lib/bundler/spinel/ledger.rb', line 43

def path
  @path
end

Instance Method Details

#build(gem:, version:, rev:, verdict:, reasons: [], risks: [], probe: "compile") ⇒ Object



94
95
96
97
98
99
100
# File 'lib/bundler/spinel/ledger.rb', line 94

def build(gem:, version:, rev:, verdict:, reasons: [], risks: [], probe: "compile")
  Verdict.new(
    gem: gem, version: version, rev: rev, verdict: verdict,
    reasons: reasons, risks: risks, probe: probe,
    at: Time.now.utc.iso8601
  )
end

#eachObject



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/bundler/spinel/ledger.rb', line 77

def each
  return enum_for(:each) unless block_given?
  return unless File.exist?(@path)

  File.foreach(@path) do |line|
    line = line.strip
    next if line.empty?

    h = JSON.parse(line)
    yield Verdict.new(
      gem: h["gem"], version: h["version"], rev: h["rev"],
      verdict: h["verdict"], reasons: h["reasons"] || [],
      risks: h["risks"] || [], probe: h["probe"], at: h["at"]
    )
  end
end

#known_gemsObject

Every distinct (gem, version) the ledger has ever seen — the candidate set for a forward-compat re-probe sweep under a new rev.



71
72
73
74
75
# File 'lib/bundler/spinel/ledger.rb', line 71

def known_gems
  seen = {}
  each { |v| seen[[v.gem, v.version]] = true }
  seen.keys
end

#lookup(gem, version, rev) ⇒ Object

Most recent verdict for this exact triple, or nil.



63
64
65
66
67
# File 'lib/bundler/spinel/ledger.rb', line 63

def lookup(gem, version, rev)
  result = nil
  each { |v| result = v if v.gem == gem && v.version == version && v.rev == rev }
  result
end

#record(verdict) ⇒ Object

Thread-safe: the survey probes gems in parallel and records from many threads. One ‘write` of the whole line under O_APPEND is a single atomic syscall, so a concurrent `each`/`lookup` reader never sees a torn line; the mutex just serialises writers among themselves.



54
55
56
57
58
59
60
# File 'lib/bundler/spinel/ledger.rb', line 54

def record(verdict)
  FileUtils.mkdir_p(File.dirname(@path))
  @write_mutex.synchronize do
    File.open(@path, "a") { |f| f.write("#{verdict.to_line}\n") }
  end
  verdict
end