Module: Bundler::Spinel::Scaffold

Defined in:
lib/bundler/spinel/engine_installer.rb

Overview

Scaffolds a minimal Spinel project so onboarding is ‘bundle install && spinel-compat init && bin/build` (spinelgems#9, stretch). Writes a Gemfile with the engine marker + a framework gem, a hello entrypoint, and a bin/build that provisions the engine, vendors deps, and compiles.

Class Method Summary collapse

Class Method Details

.app_rbObject



218
219
220
221
222
223
224
225
226
# File 'lib/bundler/spinel/engine_installer.rb', line 218

def app_rb
  <<~RUBY
    require "tep"

    get "/" do
      "hello from a Spinel-compiled Tep app\\n"
    end
  RUBY
end

.build_shObject



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/bundler/spinel/engine_installer.rb', line 228

def build_sh
  <<~SH
    #!/usr/bin/env bash
    # From a fresh checkout to a native binary. The Gemfile's `engine: spinel`
    # marker makes `bundle install` refuse to run under CRuby, so we resolve
    # with `bundle lock` and place deps with `spinel-compat vendor` instead.
    set -e
    command -v tep >/dev/null 2>&1 || gem install tep   # the tep build CLI (a compile-time tool)
    [ -f Gemfile.lock ] || bundle lock                  # resolve deps (NOT `bundle install`)
    spinel-compat install-engine                        # fetch+build the pinned engine (cached)
    export SPINEL="${SPINEL:-$HOME/.cache/spinel/current/spinel}"  # tell tep where the engine is
    spinel-compat vendor                                # place deps where Spinel follows them
    tep build app.rb -o app                             # compile -> ./app
    echo "built ./app — run it with: ./app -p 4567"
  SH
end

.gemfileObject



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/bundler/spinel/engine_installer.rb', line 201

def gemfile
  <<~RUBY
    source "https://rubygems.org"

    # Spinel is the engine: code here is compiled ahead-of-time to a native
    # binary, not run on CRuby. `engine_version` is advisory and must be a
    # version literal (bundler parses it as a Gem requirement); the actual
    # engine revision install-engine builds is pinned in ./SPINEL_PIN.
    ruby "3.3.0", engine: "spinel", engine_version: "0.0.0"

    # The web framework — Sinatra-style, compiles via Spinel. >= 0.11.1 builds
    # its C helpers on demand (needed for `gem install tep` without `make`).
    # (For an unreleased sibling instead: gem "tep", git: "https://github.com/OriPekelman/tep.git")
    gem "tep", ">= 0.11.1"
  RUBY
end

.gitignoreObject



245
246
247
248
249
250
251
252
# File 'lib/bundler/spinel/engine_installer.rb', line 245

def gitignore
  <<~TXT
    /app
    /vendor
    *.o
    *.bin
  TXT
end

.init(dir, out: $stdout, rev: nil) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/bundler/spinel/engine_installer.rb', line 160

def init(dir, out: $stdout, rev: nil)
  FileUtils.mkdir_p(dir)
  engine_rev = rev || EngineInstaller::DEFAULT_REV
  write(out, File.join(dir, "Gemfile"), gemfile)
  # The engine revision is a git SHA — NOT a valid `engine_version:` (bundler
  # parses that as a Gem version requirement and `bundle lock` errors on a
  # SHA). So the rev lives in a SPINEL_PIN file, which `install-engine`
  # reads; the Gemfile keeps a version-literal advisory marker.
  write(out, File.join(dir, "SPINEL_PIN"), "#{engine_rev}\n")
  write(out, File.join(dir, "app.rb"), app_rb)
  bin = File.join(dir, "bin", "build")
  FileUtils.mkdir_p(File.dirname(bin))
  write(out, bin, build_sh)
  File.chmod(0o755, bin)
  write(out, File.join(dir, ".gitignore"), gitignore)
  # Pin the *real* CRuby (the one running this, which has spinel-compat) so
  # version managers don't misread the Gemfile's `engine: "spinel"` marker.
  # mise/asdf parse that as `ruby@spinel-0.0.0`, fail to find it, and fall
  # back to a Ruby without bundler-spinel — `bin/build` then can't find
  # `spinel-compat`. A config-tier .tool-versions overrides that parse.
  write(out, File.join(dir, ".tool-versions"), "ruby #{RUBY_VERSION}\n")
  out.puts ""
  out.puts "Scaffolded a Spinel + Tep project in #{dir}/"
  out.puts "Next:"
  out.puts "  cd #{dir}" unless File.expand_path(dir) == Dir.pwd
  out.puts "  ./bin/build           # ensures tep, resolves + vendors deps, provisions Spinel, compiles"
  out.puts "  ./app -p 4567         # run the native binary"
  out.puts ""
  out.puts "(The `engine: spinel` Gemfile marker makes `bundle install` refuse to run"
  out.puts " under CRuby by design — bin/build uses `bundle lock` + `spinel-compat vendor`.)"
end

.write(out, path, body) ⇒ Object



192
193
194
195
196
197
198
199
# File 'lib/bundler/spinel/engine_installer.rb', line 192

def write(out, path, body)
  if File.exist?(path)
    out.puts "  skip (exists): #{path}"
  else
    File.write(path, body)
    out.puts "  create: #{path}"
  end
end