218 lines
9.6 KiB
Text
218 lines
9.6 KiB
Text
|
|
#!/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
|
||
|
|
}
|