Skip to content

jruby/02: the jruby runtime feedstock (spec 33; the first universal runtime image) - #1

Merged
ronaldtse merged 1 commit into
mainfrom
feat/initial-scaffold
Sep 7, 2026
Merged

ronaldtse merged 1 commit into
mainfrom
feat/initial-scaffold

Conversation

@ronaldtse

Copy link
Copy Markdown
Contributor

jruby/02 — the jruby runtime feedstock (spec 33; the FIRST universal runtime image)

Scaffolds tebako-packages/jruby as a working runtime feedstock: the env image is the
JRuby home, composed ON the published temurin java owner at dispatch (spec 33's
on_runtime form, second feedstock instance after truffleruby's jvm flavor).

The universal-image design (TODO.jruby/02 item 4 — pinned here)

The JRuby dist is genuinely universal: bytecode + the ruby home, and lib/jni/
ships ~26 platforms' jffi stubs (probed 2026-09-07 — record:
TODO.jruby/02-probe-2026-09-07.md at the ecosystem root). So:

  • ONE env image serves all triplets (…-universal.tfs), built once by the
    image leg (mkimage is not byte-reproducible across legs — the created
    timestamp — so one leg's image IS the published asset).
  • The triplet binding comes from the java owner pair (openjdk temurin,
    per-triplet). The release still carries the per-triplet wrapper exe (the 2.5.0
    launcher, byte-identical to tamatebako/tebako's asset) — it keeps the store
    entry well-formed (the scan requires the exe) and serves the spec 33 §5
    standalone fail-closed smoke; it is never the composed process's owner.
  • The per-triplet shard's image.filename names the universal asset —
    tpkg::runtime_store::entry_asset_names flows it verbatim (verified against
    v2.5.0 sources), so no tebako product change was needed.
  • The in-image manifest's provides is the one-or-many form — one
    EngineProvides per covered platform, the model's own multi-platform shape
    (tpkg::manifest::RuntimeProvides, "one entry per platform the image
    covers").
  • The registry entry names the same universal artifact + digest under every
    platform row (the grammar consequence item 4 asked to pin).

What lands

  • recipe.yml — the SSOT: flavors.jvm (the only mode; the flavor shape mirrors
    the sibling feedstocks), the owner edge java >= 21 with no implementation
    key
    (unlike truffleruby-jvm: jruby brings its own JIT, the graalvm compiler is
    not needed — temurin is the default and legal), mount: /__runners__/jruby,
    argv_template: ["-Djruby.home={mount}", "-jar", "{mount}/lib/jruby.jar"] —
    -Djruby.home is load-bearing: the jar's Main-Class: org.jruby.main.Main self-locates jruby.home from the code-source path for
    bin/jruby itself, but RubyGems (require "json") needs the real home —
    probed on the bare host: without the flag require "json" fails with
    "RubyGems' were not loaded"; with it JSON.generateworks (same shape as truffleruby'sllvm.hometoken), entrypoints[jruby, jgem, jirb], the platform coverage list, the universal upstream source (Maven Central, upstream-published .sha256sidecar cross-checked at fetch — the two-anchor rule), and theowner_smoke` pins (the openjdk v2.5.0 temurin pair — 2.5.0 is the first
    spec-33-capable owner line, tebako#552).
  • manifests/runtime.yaml / layout.yaml / registry.yaml — the L1 authority
    (one-or-many provides, the java_home whole-tree annotation, the on_runtime
    block), the pair contract (NO interpreter, NO preload — spec 33 §5), the L3
    registry template (universal artifact under every platform row).
  • tools/build — two roles: ROLE=image (fetch → two-anchor verify → stage →
    dereference symlinks → embed manifests → mkimage → read-back gates) and
    ROLE=pair (per triplet: wrapper exe + sidecar + shard with
    image.filename→universal + the smokes).
  • tools/pins.rb — same shape as the sibling feedstocks (recipe → env),
    emitting the universal image stem beside the per-triplet one.
  • build-payload.yml — image (once, ubuntu) → pair matrix (macos-arm64 +
    linux-gnu-x86_64 — mirrors the recipe's coverage list; windows-ucrt64 is a
    named follow-up, the universal image already serves it) → publish on tags
    (the tag IS the wrapper line).

The smokes (acceptance, TODO.jruby/02)

  • Standalone negative: the pair alone fails closed, exit 65 with the named
    "declares no layout.interpreter" message (spec 33 §5).
  • Composed: shim → jruby → the PUBLISHED temurin 2.5.0 owner; the probe
    payload prints hello from jruby 4.0.0 and loads json (jruby's own
    java-backed default gem) — asserted "engine":"jruby".
  • Cext negative: a payload whose runtime_requirement declares a
    native-extension abi NEVER boots on jruby — the shard carries abi: "none",
    so the resolver's named abi-mismatch error fires at dispatch (the absent-field
    compat window would otherwise match and crash late). Named error, never a boot.

Local gate (pre-CI proof, macos-arm64)

Ran end-to-end with the real 2.5.0 release binaries (tfs / launcher / shim,
sha256-verified against the v2.5.0 monolith) and the PUBLISHED openjdk v2.5.0
temurin pair (two-anchor verified): universal image built + read-back green,
wrapper paired, shard written (universal image.filename), standalone exit 65,
composed smoke green, cext cell refused by name.

Depends on: tebako v2.5.0 (spec 33), tebako-packages/openjdk v2.5.0 (the temurin
owner line). Refs: TODO.jruby/02, spec 33, tebako#552, truffleruby#3 (the
machinery template).

…untime image)

tebako-packages/jruby scaffold: jruby 10.1.1.0 (ruby 4.0.0, Build-Jdk-Spec 21)
on the 2.5.0 owner line.

- The first UNIVERSAL runtime image: jruby-dist ships ~26 platforms' jffi
  (probed), so ONE image tebako-runtime-2.5.0-10.1.1.0-universal.tfs is built
  once by ROLE=image; the per-triplet ROLE=pair legs pair the 2.5.0 launcher
  exe + write shards whose image.filename names the universal asset
  (tpkg::runtime_store::entry_asset_names flows the shard's image.filename
  verbatim — zero product change).
- L1 manifest in the one-or-many provides form (tpkg::manifest::RuntimeProvides
  is Vec<EngineProvides> — the model's own multi-platform shape).
- Owner edge `java >= 21` with NO implementation key (jruby brings its own
  JIT; temurin default, graalvm legal). owner_smoke pins the openjdk v2.5.0
  temurin pair.
- argv_template carries -Djruby.home={mount}: LOAD-BEARING, probed on the
  bare host — without it `require "json"` fails ("`RubyGems' were not
  loaded"); with it JSON.generate works (same shape as truffleruby's
  llvm.home token).
- Shard carries "abi": "none" so native-ext payloads fail at dispatch with
  the resolver's named abi-mismatch instead of crashing late; the cext
  negative cell in tools/build proves it.
- Windows leg: named follow-up (the universal image already serves it; it
  lands with its CI leg).
- Tools block: tfs + tebako-shim pins from the v2.5.0 monolith; NO link-unit
  (the jvm layout grants no preload).

Local gate green: image leg (fetch, two-anchor sha256 vs the upstream
Maven Central sidecar, stage, symlink dereference, manifest+layout embed,
mkimage, read-back) + pair leg (wrapper exe, shard, composed smoke through
the shim: "hello from jruby 4.0.0" + JSON.generate OK + the cext negative
cell).
@ronaldtse
ronaldtse marked this pull request as ready for review September 7, 2026 13:33
@ronaldtse
ronaldtse merged commit 282bdb2 into main Sep 7, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants