ontoref-code/install/assemble-bundle.nu

218 lines
9.6 KiB
Text
Raw Normal View History

#!/usr/bin/env nu
# install/assemble-bundle.nu — assemble a self-contained ontoref distribution
# bundle (ADR-038) from an ALREADY cross-compiled binary plus the data layer.
#
# This NEVER recompiles. It consumes target/<triple>/release/ontoref-daemon and
# packages it with the bootstrapper, the CLI wrapper, a target-arch nickel, the
# data layer (reflection/ ontology/ domains/ templates/ public/ install/) and a
# config skeleton into dist/ontoref-<os>-<arch>.tar.gz (+ .sha256). The curl|sh
# installer (install/install.sh) extracts this and mirrors install.nu's layout.
#
# nu install/assemble-bundle.nu --target x86_64-unknown-linux-musl
# nu install/assemble-bundle.nu smoke --target x86_64-unknown-linux-musl
#
# Config is taken ONLY from install/resources/ — never from the (possibly
# corrupt) per-project .ontoref/config.ncl.
# Map a cargo target triple to the bundle's os/arch suffix.
def target-to-osarch [target: string]: nothing -> record {
match $target {
"x86_64-unknown-linux-musl" => { os: "linux", arch: "amd64" }
"aarch64-unknown-linux-musl" => { os: "linux", arch: "arm64" }
"x86_64-unknown-linux-gnu" => { os: "linux", arch: "amd64" }
"aarch64-unknown-linux-gnu" => { os: "linux", arch: "arm64" }
_ => { error make { msg: $"unsupported target triple: ($target)" } }
}
}
# Resolve the cargo target directory (honours .cargo/config.toml target-dir).
def cargo-target-dir [root: string]: nothing -> string {
let meta = (do { cd $root; ^cargo metadata --format-version 1 --no-deps } | complete)
if $meta.exit_code == 0 {
$meta.stdout | from json | get target_directory
} else {
$"($root)/target"
}
}
# Recursively copy every file under src into dest, preserving the relative tree.
def copy-tree [src: string, dest: string]: nothing -> int {
if not ($src | path exists) { return 0 }
mkdir $dest
let files = (glob $"($src)/**/*" | where { |f| ($f | path type) == "file" })
for src_file in $files {
let rel = ($src_file | str replace $"($src)/" "")
let dest_file = $"($dest)/($rel)"
let dest_parent = ($dest_file | path dirname)
mkdir $dest_parent
cp $src_file $dest_file
}
$files | length
}
# Repo-side reflection source: .ontoref/reflection (ADR-032) or legacy reflection/.
def reflection-src [root: string]: nothing -> string {
if ($"($root)/.ontoref/reflection" | path exists) {
$"($root)/.ontoref/reflection"
} else {
$"($root)/reflection"
}
}
# Locate a target-arch nickel binary. Explicit --nickel wins; otherwise look for
# a pre-fetched per-arch binary under dist/nickel/<target>/nickel; otherwise fall
# back to the host nickel with a loud warning (arch may not match the target).
def resolve-nickel [root: string, target: string, explicit: string]: nothing -> string {
if ($explicit | is-not-empty) {
if not ($explicit | path exists) {
error make { msg: $"--nickel path does not exist: ($explicit)" }
}
return $explicit
}
let prefetched = $"($root)/dist/nickel/($target)/nickel"
if ($prefetched | path exists) { return $prefetched }
let host = (which nickel)
if ($host | is-empty) {
error make { msg: "nickel not found — pass --nickel <target-arch-binary> or place it at dist/nickel/<target>/nickel" }
}
let host_path = ($host | first | get path)
print $" (ansi yellow)warn(ansi reset) bundling host nickel ($host_path) — arch may differ from target ($target); pass --nickel for cross bundles"
$host_path
}
# Assemble the bundle. Returns the tarball path.
def assemble [target: string, root: string, bin_dir: string, version: string, nickel: string, out_dir: string]: nothing -> string {
let osarch = (target-to-osarch $target)
let resolved_bin_dir = if ($bin_dir | is-not-empty) {
$bin_dir
} else {
$"((cargo-target-dir $root))/($target)/release"
}
let daemon_src = $"($resolved_bin_dir)/ontoref-daemon"
if not ($daemon_src | path exists) {
error make { msg: $"binary not found: ($daemon_src)\n run: cross build --target ($target) --release" }
}
let tier_src = $"($resolved_bin_dir)/ontoref-tier"
let nickel_bin = (resolve-nickel $root $target $nickel)
let stage = $"($out_dir)/.stage-($osarch.os)-($osarch.arch)"
if ($stage | path exists) { rm -rf $stage }
mkdir $"($stage)/bin"
mkdir $"($stage)/data"
mkdir $"($stage)/config"
# ── bin/ ───────────────────────────────────────────────────────────────────
cp $daemon_src $"($stage)/bin/ontoref-daemon.bin"
if ($tier_src | path exists) { cp $tier_src $"($stage)/bin/ontoref-tier" }
cp $"($root)/install/ontoref-daemon-boot" $"($stage)/bin/ontoref-daemon-boot"
cp $"($root)/install/ontoref-global" $"($stage)/bin/ontoref-global"
cp $nickel_bin $"($stage)/bin/nickel"
for f in (glob $"($stage)/bin/*" | where { |p| ($p | path type) == "file" }) { chmod +x $f }
# ── data/ — reflection, protocol ontology, domains, templates, UI, install ──
let refl_n = (copy-tree (reflection-src $root) $"($stage)/data/reflection")
let ont_n = (copy-tree $"($root)/ontology" $"($stage)/data/ontology")
let dom_n = (copy-tree $"($root)/domains" $"($stage)/data/domains")
let tpl_n = (copy-tree $"($root)/templates" $"($stage)/data/templates")
let ui_n = (copy-tree $"($root)/crates/ontoref-daemon/templates" $"($stage)/data/templates")
let pub_n = (copy-tree $"($root)/crates/ontoref-daemon/public" $"($stage)/data/public")
mkdir $"($stage)/data/install"
for f in (glob $"($root)/install/*.nu") {
cp $f $"($stage)/data/install/(($f | path basename))"
}
# ── config/ — skeleton ONLY from install/resources (never .ontoref/config.ncl) ──
let res = $"($root)/install/resources"
for name in ["config.ncl", "projects.ncl", "remote-projects.ncl", "streams.json"] {
let src = $"($res)/($name)"
if ($src | path exists) { cp $src $"($stage)/config/($name)" }
}
copy-tree $"($res)/schemas" $"($stage)/config/schemas" | ignore
# ── BUNDLE_INFO.json — version/target provenance for the installer ──────────
let info = {
schema: "ontoref-bundle/1",
version: $version,
target: $target,
os: $osarch.os,
arch: $osarch.arch,
components: { reflection: $refl_n, ontology: $ont_n, domains: $dom_n, templates: ($tpl_n + $ui_n), public: $pub_n },
}
$info | to json | save --force $"($stage)/BUNDLE_INFO.json"
# ── tar.gz + sha256 ──────────────────────────────────────────────────────────
mkdir $out_dir
let tarball = $"($out_dir)/ontoref-($osarch.os)-($osarch.arch).tar.gz"
let tar_res = (do { ^tar -czf $tarball -C $stage "." } | complete)
if $tar_res.exit_code != 0 {
error make { msg: $"tar failed: ($tar_res.stderr)" }
}
let digest = (open --raw $tarball | hash sha256)
$"($digest) (($tarball | path basename))\n" | save --force $"($tarball).sha256"
rm -rf $stage
print $"✓ bundle ($tarball)"
print $" sha256 ($digest)"
print $" layout bin/{ontoref-daemon.bin,ontoref-daemon-boot,ontoref-global,nickel} data/ config/"
$tarball
}
# Assemble a bundle for one target triple.
def main [
--target: string = "x86_64-unknown-linux-musl" # cargo target triple
--bin-dir: string = "" # override binary dir (default: <target_dir>/<triple>/release)
--root: string = "" # repo root (default: PWD)
--version: string = "" # version stamp (default: workspace.package.version)
--nickel: string = "" # target-arch nickel binary to bundle
--out-dir: string = "" # output dir (default: <root>/dist)
]: nothing -> nothing {
let root = if ($root | is-empty) { $env.PWD } else { $root }
let out = if ($out_dir | is-empty) { $"($root)/dist" } else { $out_dir }
let ver = if ($version | is-not-empty) {
$version
} else {
let cargo = (open $"($root)/Cargo.toml")
$cargo.workspace.package.version
}
assemble $target $root $bin_dir $ver $nickel $out | ignore
}
# Smoke gate (ADR-038): assemble the native bundle, extract it, run the daemon
# binary, and confirm the expected layout. Fails the release pipeline BEFORE any
# distribution step. Native-arch only — an arm64 binary cannot run on amd64.
def "main smoke" [
--target: string = "x86_64-unknown-linux-musl"
--root: string = ""
]: nothing -> nothing {
let root = if ($root | is-empty) { $env.PWD } else { $root }
let osarch = (target-to-osarch $target)
let out = $"($root)/dist"
let cargo = (open $"($root)/Cargo.toml")
let ver = $cargo.workspace.package.version
let tarball = (assemble $target $root "" $ver "" $out)
let probe = $"($out)/.smoke-($osarch.os)-($osarch.arch)"
if ($probe | path exists) { rm -rf $probe }
mkdir $probe
let untar = (do { ^tar -xzf $tarball -C $probe } | complete)
if $untar.exit_code != 0 {
error make { msg: $"smoke: extraction failed: ($untar.stderr)" }
}
for entry in ["bin/ontoref-daemon.bin", "bin/ontoref-daemon-boot", "bin/ontoref-global", "bin/nickel", "BUNDLE_INFO.json"] {
if not ($"($probe)/($entry)" | path exists) {
error make { msg: $"smoke: bundle missing ($entry)" }
}
}
let ver_res = (do { ^$"($probe)/bin/ontoref-daemon.bin" --version } | complete)
if $ver_res.exit_code != 0 {
error make { msg: $"smoke: daemon binary failed to run --version:\n($ver_res.stderr)" }
}
print $"✓ smoke daemon runs: (($ver_res.stdout | str trim))"
rm -rf $probe
}