bundler-spinel 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76d26ae3fb919613ecbd538b81af7c07d1000ac69283770dcbe402f25e37e6fa
4
- data.tar.gz: f445da298655fb8e24ef3c862d68e93620ff3271cfbcc25926667c771331b01d
3
+ metadata.gz: b5a1d4be2ef6216a5cd1241e7ef27dd6ee5222f79f21e6924b8c52c0fe02f35b
4
+ data.tar.gz: 0a36f37f19a5333e42515a23ec80524ee99bd2d81322e573bb7b3376b4a231b9
5
5
  SHA512:
6
- metadata.gz: b719d83dde793a35980b3a607625905f9da5e294ce762f39a74039a9fce4771595b5a0734ee4ccf732f549b3378e9e9db28d93f7d70da7f88d976a30f095d38e
7
- data.tar.gz: aa986cc8de2f1d84fcc0eddc5d2f8627802bf49cb218e8160b4741b59185bc351a1be98d123febd55293e703c9dd00b19fae73b88fcf4bc7842891898db3a5be
6
+ metadata.gz: e9fd87e3769ca2246b536ef82c4e6c5c6a704021183008516ce7b41ebf10f0e310d03e297b06887c86cb9f97dc8ae388c6f45f01ce92f1a1a7af791763af0af9
7
+ data.tar.gz: '0894f3ed497a5b7c2e226d5ec5b86ce8b60872775d12c72090df8a6da3f8eead78a93b8ced7e994769e0359a48850be22526192683351a1b16dfe49714744013'
data/CHANGELOG.md CHANGED
@@ -4,6 +4,49 @@ All notable changes to `bundler-spinel` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
5
5
  project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.2.1] — 2026-06-01
8
+
9
+ ### Fixed
10
+ - **`init` scaffold tripped `mise`/`asdf` on the engine marker.** Version managers
11
+ read the Gemfile's `ruby "3.3.0", engine: "spinel", …` and tried to activate
12
+ `ruby@spinel-0.0.0` (missing) → fell back to a Ruby without `bundler-spinel`, so
13
+ `bin/build` failed with `spinel-compat: command not found` (first-user report,
14
+ tep#156). The scaffold now also writes a config-tier `.tool-versions` pinning the
15
+ real CRuby (`ruby <RUBY_VERSION>`), which overrides the Gemfile parse so the
16
+ manager activates the Ruby that has `spinel-compat`. Reproduced + verified the
17
+ fix against `mise` in a container.
18
+
19
+ ## [0.2.0] — 2026-06-01
20
+
21
+ ### Added
22
+ - **`verify` self-localizes a miscompile.** When the differential smoke diverges
23
+ (CRuby and Spinel both run but disagree on stdout), `verify` now runs the
24
+ value-bisection harness (`spinel-dev tools/value-bisect/bisect.sh --json`) on
25
+ the still-on-disk harness and, when it pins the first scalar local to part
26
+ ways, appends a `localized:<file>:<line> <var> cruby=… spinel=…` reason. This
27
+ upgrades a bare `diff:L2 cruby=… spinel=…` ("the outputs differ") into a line
28
+ to look at ("`x` is wrong here"). Strictly best-effort and non-fatal: if the
29
+ harness can't be found (it's a separate repo — override with `SPINEL_BISECT`,
30
+ else probed next to the engine and at `~/sites/spinel-dev`) or can't attribute
31
+ the divergence to a traced scalar, the verdict is returned unchanged.
32
+
33
+ ### Changed
34
+ - **`init` scaffold uses the published tep gem.** Now that tep is on RubyGems
35
+ (`tep 0.11.1`), the generated `Gemfile` emits plain `gem "tep", ">= 0.11.1"`
36
+ instead of the `git:`-fallback comment. Docs (`README`, `docs/adoption.md`)
37
+ updated to the published-gem form. spinelgems#10.
38
+ - **`init`'s `bin/build` made self-sufficient + correct.** A clean-room run
39
+ showed the old `bundle install && ./bin/build` flow couldn't run: the
40
+ `engine: spinel` marker makes `bundle install` refuse under CRuby (by design),
41
+ and `bundle lock` + `vendor` place tep's *lib* but never install the `tep`
42
+ *CLI*. `bin/build` now `gem install`s tep if absent, uses `bundle lock` (not
43
+ install), exports `SPINEL` (so tep finds the provisioned engine), then
44
+ provisions + vendors + compiles; the `init` next-steps reflect this.
45
+ **The full onboarding now runs end-to-end** — validated in a clean `ruby:3.3`
46
+ container: `gem install bundler-spinel` → `init` → `./bin/build` → `./app`
47
+ serves a Spinel-compiled Tep app (needs `tep ≥ 0.11.1`, which builds its C
48
+ helpers on demand). spinelgems#10.
49
+
7
50
  ## [0.1.1] — 2026-06-01
8
51
 
9
52
  ### Fixed
@@ -67,6 +110,8 @@ and the ledger format may all change before `0.0.1`. Install with `--pre`.
67
110
  - Wholesale `survey` with a thread-safe ledger, a per-compile wall-clock
68
111
  timeout (`analyze-timeout` reject reason), and a ledger-based report.
69
112
 
113
+ [0.2.1]: https://github.com/OriPekelman/spinelgems/releases/tag/v0.2.1
114
+ [0.2.0]: https://github.com/OriPekelman/spinelgems/releases/tag/v0.2.0
70
115
  [0.1.1]: https://github.com/OriPekelman/spinelgems/releases/tag/v0.1.1
71
116
  [0.1.0]: https://github.com/OriPekelman/spinelgems/releases/tag/v0.1.0
72
117
  [0.0.1.pre]: https://github.com/OriPekelman/spinelgems/releases/tag/v0.0.1.pre
data/README.md CHANGED
@@ -37,8 +37,8 @@ limited and still growing. So we surveyed the ecosystem and publish a
37
37
  source "https://rubygems.org"
38
38
  ruby "3.3.0", engine: "spinel", engine_version: "0.0.0"
39
39
 
40
- gem "tep", git: "https://…/tep.git" # siblings via path:/git:
41
- gem "some_pure_ruby_lib"
40
+ gem "tep" # published on RubyGems
41
+ gem "some_unreleased_lib", git: "…" # or a sibling via path:/git:
42
42
  ```
43
43
 
44
44
  `bundle lock` resolves normally (it ignores the engine marker); the marker guards
@@ -172,13 +172,21 @@ module Bundler
172
172
  write(out, bin, build_sh)
173
173
  File.chmod(0o755, bin)
174
174
  write(out, File.join(dir, ".gitignore"), gitignore)
175
+ # Pin the *real* CRuby (the one running this, which has spinel-compat) so
176
+ # version managers don't misread the Gemfile's `engine: "spinel"` marker.
177
+ # mise/asdf parse that as `ruby@spinel-0.0.0`, fail to find it, and fall
178
+ # back to a Ruby without bundler-spinel — `bin/build` then can't find
179
+ # `spinel-compat`. A config-tier .tool-versions overrides that parse.
180
+ write(out, File.join(dir, ".tool-versions"), "ruby #{RUBY_VERSION}\n")
175
181
  out.puts ""
176
182
  out.puts "Scaffolded a Spinel + Tep project in #{dir}/"
177
183
  out.puts "Next:"
178
184
  out.puts " cd #{dir}" unless File.expand_path(dir) == Dir.pwd
179
- out.puts " bundle install # resolves deps (gated by the compat ledger)"
180
- out.puts " ./bin/build # provisions Spinel, vendors deps, compiles app.rb"
185
+ out.puts " ./bin/build # ensures tep, resolves + vendors deps, provisions Spinel, compiles"
181
186
  out.puts " ./app -p 4567 # run the native binary"
187
+ out.puts ""
188
+ out.puts "(The `engine: spinel` Gemfile marker makes `bundle install` refuse to run"
189
+ out.puts " under CRuby by design — bin/build uses `bundle lock` + `spinel-compat vendor`.)"
182
190
  end
183
191
 
184
192
  def write(out, path, body)
@@ -200,9 +208,10 @@ module Bundler
200
208
  # engine revision install-engine builds is pinned in ./SPINEL_PIN.
201
209
  ruby "3.3.0", engine: "spinel", engine_version: "0.0.0"
202
210
 
203
- # The web framework — Sinatra-style, compiles via Spinel. Until tep is on
204
- # RubyGems, point at the repo: gem "tep", git: "https://github.com/OriPekelman/tep.git"
205
- gem "tep"
211
+ # The web framework — Sinatra-style, compiles via Spinel. >= 0.11.1 builds
212
+ # its C helpers on demand (needed for `gem install tep` without `make`).
213
+ # (For an unreleased sibling instead: gem "tep", git: "https://github.com/OriPekelman/tep.git")
214
+ gem "tep", ">= 0.11.1"
206
215
  RUBY
207
216
  end
208
217
 
@@ -219,12 +228,16 @@ module Bundler
219
228
  def build_sh
220
229
  <<~SH
221
230
  #!/usr/bin/env bash
222
- # Provision the Spinel compiler, vendor deps where Spinel finds them,
223
- # and compile app.rb to a native binary.
231
+ # From a fresh checkout to a native binary. The Gemfile's `engine: spinel`
232
+ # marker makes `bundle install` refuse to run under CRuby, so we resolve
233
+ # with `bundle lock` and place deps with `spinel-compat vendor` instead.
224
234
  set -e
225
- spinel-compat install-engine # fetch+build the pinned engine (cached)
226
- spinel-compat vendor # place deps + write deps.rb
227
- tep build app.rb -o app # compile -> ./app
235
+ command -v tep >/dev/null 2>&1 || gem install tep # the tep build CLI (a compile-time tool)
236
+ [ -f Gemfile.lock ] || bundle lock # resolve deps (NOT `bundle install`)
237
+ spinel-compat install-engine # fetch+build the pinned engine (cached)
238
+ export SPINEL="${SPINEL:-$HOME/.cache/spinel/current/spinel}" # tell tep where the engine is
239
+ spinel-compat vendor # place deps where Spinel follows them
240
+ tep build app.rb -o app # compile -> ./app
228
241
  echo "built ./app — run it with: ./app -p 4567"
229
242
  SH
230
243
  end
@@ -0,0 +1,83 @@
1
+ require "open3"
2
+ require "json"
3
+
4
+ module Bundler
5
+ module Spinel
6
+ # Upgrades a bare `miscompile` verdict (CRuby and Spinel agree to run but
7
+ # disagree on stdout) into a *located* one: the (file, line, variable) where
8
+ # a scalar local first diverges — via the value-bisection harness that lives
9
+ # in the spinel-dev repo (tools/value-bisect/bisect.sh).
10
+ #
11
+ # Why: a miscompile reason of `diff:L2 cruby="42" spinel="0"` says two outputs
12
+ # differ but not *why*. The bisector traces every scalar local under both
13
+ # CRuby and a Spinel --debug build and reports the first to part ways, turning
14
+ # that into `localized:foo.rb:12 total cruby=42 spinel=0` — a line to look at.
15
+ #
16
+ # Strictly best-effort and non-fatal. If the harness can't be found (it's a
17
+ # separate repo), can't run (the engine dir is a bare binary with no compiler
18
+ # sources / no lldb), or can't pin a scalar (the divergence is in output
19
+ # formatting, not a traced local), verify still returns the miscompile verdict
20
+ # unchanged — this only ever *adds* a `localized:` reason when it has one.
21
+ class Localizer
22
+ def initialize(engine)
23
+ @engine = engine
24
+ end
25
+
26
+ # Run the bisector on `harness` (a self-contained .rb; require_relative'd
27
+ # files are traced too) and return a short reason string, or nil when
28
+ # localization isn't possible or didn't pin a value.
29
+ def localize(harness)
30
+ script = bisect_script or return nil
31
+ # SPINEL_DIR points the bisector at the same compiler the engine uses;
32
+ # it shells out to that checkout's spinel_analyze.rb / spinel_codegen.rb.
33
+ # bisect.sh exits 1 *on divergence* (the case we want), so the exit code
34
+ # is not a success signal — parse stdout regardless. stdout carries only
35
+ # the single JSON object in --json mode; progress goes to stderr.
36
+ out, _err, _st = Open3.capture3(
37
+ { "SPINEL_DIR" => @engine.dir }, "sh", script, "--json", harness
38
+ )
39
+ line = out.lines.map(&:strip).reject(&:empty?).last or return nil
40
+ verdict = begin
41
+ JSON.parse(line)
42
+ rescue JSON::ParserError
43
+ return nil
44
+ end
45
+ format_verdict(verdict)
46
+ rescue StandardError
47
+ nil
48
+ end
49
+
50
+ private
51
+
52
+ def format_verdict(v)
53
+ case v["verdict"]
54
+ when "diverge"
55
+ "localized:#{loc(v)} #{v['variable']} cruby=#{v['cruby']} spinel=#{v['spinel']}"
56
+ when "crash"
57
+ "localized:crash@#{loc(v)} signal=#{v['signal']}"
58
+ end
59
+ # Any other verdict (ok / exit-differ / abort) means the bisector couldn't
60
+ # attribute the divergence to a traced scalar; leave the verdict un-enriched.
61
+ end
62
+
63
+ def loc(v)
64
+ file = v["file"].to_s
65
+ file.empty? ? "line #{v['line']}" : "#{file}:#{v['line']}"
66
+ end
67
+
68
+ # Resolve bisect.sh. It lives in spinel-dev (separate from the compiler
69
+ # engine), so there's no fixed relation to the engine dir — probe a few
70
+ # conventional spots, newest-intent first: an explicit override, a sibling
71
+ # spinel-dev checkout next to the engine, the conventional ~/sites layout.
72
+ def bisect_script
73
+ rel = "tools/value-bisect/bisect.sh"
74
+ candidates = [
75
+ ENV["SPINEL_BISECT"],
76
+ File.expand_path(File.join(@engine.dir, "..", "spinel-dev", rel)),
77
+ File.expand_path("~/sites/spinel-dev/#{rel}")
78
+ ]
79
+ candidates.find { |c| c && File.exist?(c) }
80
+ end
81
+ end
82
+ end
83
+ end
@@ -1,4 +1,5 @@
1
1
  require "open3"
2
+ require_relative "localizer"
2
3
 
3
4
  module Bundler
4
5
  module Spinel
@@ -50,6 +51,15 @@ module Bundler
50
51
  unless verdict == "verified" || verdict == "loaded" || verdict == "clean"
51
52
  reasons = ["rubric:#{rubric(ruby_ok, ruby_err, spin_ok, spin_err, ruby_out, spin_out, gem_name)}"] + reasons
52
53
  end
54
+ # Self-localize a miscompile: a `diff:L2 cruby=… spinel=…` reason says
55
+ # the outputs differ but not where the value went wrong. The bisector
56
+ # traces the still-on-disk harness (require_relative'd gem files included)
57
+ # and, when it pins a diverging scalar, appends `localized:<file>:<line>
58
+ # <var> cruby=… spinel=…`. Best-effort: nil when it can't localize.
59
+ if verdict == "rejected" && reasons.include?("miscompile")
60
+ loc = Localizer.new(@engine).localize(harness)
61
+ reasons += [loc] if loc
62
+ end
53
63
  @ledger.record(@ledger.build(
54
64
  gem: gem_name, version: version, rev: @engine.rev,
55
65
  verdict: verdict, reasons: reasons, probe: full ? "verify-full" : "verify"
@@ -4,6 +4,6 @@ module Bundler
4
4
  # are proven, and `install-engine` now provisions the compiler too, so a
5
5
  # newcomer can `gem install bundler-spinel` and onboard without an
6
6
  # out-of-band Spinel build (spinelgems#9).
7
- VERSION = "0.1.1"
7
+ VERSION = "0.2.1"
8
8
  end
9
9
  end
@@ -10,6 +10,7 @@ require_relative "spinel/ledger"
10
10
  require_relative "spinel/gem_fetcher"
11
11
  require_relative "spinel/probe"
12
12
  require_relative "spinel/verifier"
13
+ require_relative "spinel/localizer"
13
14
  require_relative "spinel/vendorer"
14
15
  require_relative "spinel/survey"
15
16
  require_relative "spinel/checker"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundler-spinel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ori Pekelman
@@ -37,6 +37,7 @@ files:
37
37
  - lib/bundler/spinel/gem_fetcher.rb
38
38
  - lib/bundler/spinel/history.rb
39
39
  - lib/bundler/spinel/ledger.rb
40
+ - lib/bundler/spinel/localizer.rb
40
41
  - lib/bundler/spinel/platform.rb
41
42
  - lib/bundler/spinel/probe.rb
42
43
  - lib/bundler/spinel/proxy.rb