#!/usr/bin/env nu # install/install.nu — ontoref-daemon installer # # Installed layout: # # ~/.local/bin/ # ontoref ← global CLI wrapper (install/ontoref-global) # ontoref-daemon ← bootstrapper: nickel export | ontoref-daemon.bin (ADR-004) # ontoref-daemon.bin ← compiled binary (not called directly) # # Platform data/config: # macOS: # data ~/Library/Application Support/ontoref/ templates, public, nulib # config ~/.config/ontoref/ # Linux: # data ~/.local/share/ontoref/ # config ~/.config/ontoref/ # # Dev mode (Cargo.toml present + nu in PATH): also installs ncl-bootstrap Nu helper # Service mode: bootstrapper is the sole entrypoint — .bin never called directly def install-if-changed [src: string, dest: string, label: string] { let needs_update = if ($dest | path exists) { (open --raw $src | hash sha256) != (open --raw $dest | hash sha256) } else { true } if $needs_update { cp $src $dest chmod +x $dest print $"✓ ($label) ($dest)" } else { print $"— ($label) unchanged" } } def main [] { let repo_root = $env.PWD let is_dev = (("Cargo.toml" | path exists) and ((which nu | length) > 0)) let platform = (sys host | get name) let is_mac = ((^uname) == "Darwin") let data_dir = if $is_mac { $"($env.HOME)/Library/Application Support/ontoref" } else { $"($env.HOME)/.local/share/ontoref" } let config_dir = $"($env.HOME)/.config/ontoref" let bin_dir = $"($env.HOME)/.local/bin" mkdir $bin_dir # ── 1. Binary → ontoref-daemon.bin ──────────────────────────────────────── let bin_src = $"($repo_root)/target/release/ontoref-daemon" let bin_dest = $"($bin_dir)/ontoref-daemon.bin" if not ($bin_src | path exists) { error make { msg: $"binary not found: ($bin_src)\n run: cargo build --release -p ontoref-daemon" } } # Stop any running instance — macOS kills the new process if the old one holds the file do { ^pkill -x ontoref-daemon.bin } | ignore do { ^pkill -x ontoref-daemon } | ignore cp $bin_src $bin_dest chmod +x $bin_dest if $is_mac { do { ^xattr -d com.apple.quarantine $bin_dest } | ignore } print $"✓ binary ($bin_dest)" # ── 2. Bootstrapper → ontoref-daemon ────────────────────────────────────── # The bootstrapper IS the public entrypoint. Users call ontoref-daemon, never .bin directly. let boot_src = $"($repo_root)/install/ontoref-daemon-boot" let boot_dest = $"($bin_dir)/ontoref-daemon" install-if-changed $boot_src $boot_dest "bootstrapper" # ── 3. Global CLI wrapper → ontoref ─────────────────────────────────────── # Bake the repo root into the installed script so ONTOREF_ROOT resolves correctly # from any working directory, not just from within the source tree. let cli_src = $"($repo_root)/install/ontoref-global" let cli_dest = $"($bin_dir)/ontoref" let cli_baked = ( open --raw $cli_src | str replace 'ONTOREF_ROOT="${ONTOREF_ROOT:-ontoref}"' $'ONTOREF_ROOT="${ONTOREF_ROOT:-($repo_root)}"' ) let needs_update = if ($cli_dest | path exists) { ($cli_baked | hash sha256) != (open --raw $cli_dest | hash sha256) } else { true } if $needs_update { $cli_baked | save --force $cli_dest chmod +x $cli_dest print $"✓ cli ($cli_dest)" } else { print $"— cli unchanged" } # ── 4. UI assets (data dir) ──────────────────────────────────────────────── let templates_src = $"($repo_root)/crates/ontoref-daemon/templates" let public_src = $"($repo_root)/crates/ontoref-daemon/public" if not ($templates_src | path exists) { error make { msg: $"templates not found: ($templates_src)" } } mkdir $data_dir let asset_dirs = [$templates_src $public_src] mut updated = 0 mut skipped = 0 for asset_dir in $asset_dirs { let dir_name = ($asset_dir | path basename) let dest_base = $"($data_dir)/($dir_name)" mkdir $dest_base for src_file in (glob $"($asset_dir)/**/*" | where { |f| ($f | path type) == "file" }) { let rel = ($src_file | str replace $"($asset_dir)/" "") let dest_file = $"($dest_base)/($rel)" let dest_parent = ($dest_file | path dirname) mkdir $dest_parent let needs_update = if ($dest_file | path exists) { (open --raw $src_file | hash sha256) != (open --raw $dest_file | hash sha256) } else { true } if $needs_update { cp $src_file $dest_file $updated = $updated + 1 } else { $skipped = $skipped + 1 } } } print $"✓ assets ($data_dir)/ updated=($updated) unchanged=($skipped)" # ── 5. Config skeleton + global NATS topology ───────────────────────────── let streams_default = $"($repo_root)/install/resources/streams.json" let streams_dest = $"($config_dir)/streams.json" if ($streams_default | path exists) { mkdir $config_dir if not ($streams_dest | path exists) { cp $streams_default $streams_dest print $"✓ nats topology ($streams_dest)" } else { print $"— nats topology unchanged" } } let config_default = $"($repo_root)/install/resources/config.ncl" let config_dest = $"($config_dir)/config.ncl" let config_example = $"($config_dir)/config.ncl.example" if ($config_default | path exists) { mkdir $config_dir cp $config_default $config_example if ($config_dest | path exists) { print $"ℹ config already exists — not overwritten" print $" example kept: ($config_example)" } else { cp $config_default $config_dest print $"✓ config ($config_dest)" print $" edit with: ontoref config-edit" } } # projects.ncl — local projects; populated by `ontoref project-add` let projects_default = $"($repo_root)/install/resources/projects.ncl" let projects_dest = $"($config_dir)/projects.ncl" if ($projects_default | path exists) and not ($projects_dest | path exists) { cp $projects_default $projects_dest print $"✓ projects ($projects_dest)" } # remote-projects.ncl — remote/push-only projects; populated by `ontoref project-add-remote` let remote_default = $"($repo_root)/install/resources/remote-projects.ncl" let remote_dest = $"($config_dir)/remote-projects.ncl" if ($remote_default | path exists) and not ($remote_dest | path exists) { cp $remote_default $remote_dest print $"✓ remote-projects ($remote_dest)" } # schemas/ — project contract schemas imported by per-project .ontoref/project.ncl let schemas_src = $"($repo_root)/install/resources/schemas" let schemas_dest = $"($config_dir)/schemas" if ($schemas_src | path exists) { mkdir $schemas_dest for f in (ls $schemas_src | get name) { let dest_f = $"($schemas_dest)/(($f | path basename))" if not ($dest_f | path exists) { cp $f $dest_f print $"✓ schema ($dest_f)" } } } # ── 6. Install scripts (gen-projects.nu, etc.) ──────────────────────────── # The bootstrapper (ontoref-daemon-boot) looks for these at $data_dir/install/ # to validate and regenerate projects.ncl before nickel export runs. let install_dest = $"($data_dir)/install" mkdir $install_dest for f in (glob $"($repo_root)/install/*.nu") { let dest_f = $"($install_dest)/(($f | path basename))" install-if-changed $f $dest_f $"install/(($f | path basename))" } # ── 7. Dev extras: ncl-bootstrap Nu helper ──────────────────────────────── if $is_dev { let nulib_dest = $"($data_dir)/nulib" mkdir $nulib_dest cp $"($repo_root)/reflection/nulib/bootstrap.nu" $"($nulib_dest)/bootstrap.nu" print $"✓ ncl-bootstrap ($nulib_dest)/bootstrap.nu" } # ── Summary ──────────────────────────────────────────────────────────────── let mode_tag = if $is_dev { "dev" } else { "service" } print $"\ninstalled mode=($mode_tag) platform=($platform)" print $" bin ($bin_dir)/ontoref, ontoref-daemon, ontoref-daemon.bin" print $" data ($data_dir)/" print $" config ($config_dir)/" print "" print " next: nu install/config-setup.nu" }