#!/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 data dir as ONTOREF_ROOT so the installed wrapper is self-contained # and does not require the source repo to be present at runtime. 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:-($data_dir)}"' ) 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" } # ── 3b. Reflection scripts (data dir) ───────────────────────────────────── # The global CLI wrapper calls $data_dir/reflection/bin/ontoref.nu directly. # Copy the entire reflection/ tree so the install is autonomous (no dev repo needed). let reflection_src = $"($repo_root)/reflection" let reflection_dest = $"($data_dir)/reflection" if not ($reflection_src | path exists) { error make { msg: $"reflection/ not found: ($reflection_src)" } } mkdir $reflection_dest mut refl_updated = 0 mut refl_skipped = 0 for src_file in (glob $"($reflection_src)/**/*" | where { |f| ($f | path type) == "file" }) { let rel = ($src_file | str replace $"($reflection_src)/" "") let dest_file = $"($reflection_dest)/($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 $refl_updated = $refl_updated + 1 } else { $refl_skipped = $refl_skipped + 1 } } print $"✓ reflection ($reflection_dest)/ updated=($refl_updated) unchanged=($refl_skipped)" # ── 3c. CLI templates (project.ncl, ontoref-config.ncl, ontology/ stubs) ── # `ontoref setup` reads from $ONTOREF_ROOT/templates/ — copy the repo-level # templates/ tree so the installed CLI works without the source repo present. let cli_templates_src = $"($repo_root)/templates" let cli_templates_dest = $"($data_dir)/templates" if ($cli_templates_src | path exists) { mkdir $cli_templates_dest mut tmpl_updated = 0 mut tmpl_skipped = 0 for src_file in (glob $"($cli_templates_src)/**/*" | where { |f| ($f | path type) == "file" }) { let rel = ($src_file | str replace $"($cli_templates_src)/" "") let dest_file = $"($cli_templates_dest)/($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 $tmpl_updated = $tmpl_updated + 1 } else { $tmpl_skipped = $tmpl_skipped + 1 } } print $"✓ cli-templates ($cli_templates_dest)/ updated=($tmpl_updated) unchanged=($tmpl_skipped)" } else { print $" (ansi yellow)warn(ansi reset) templates/ not found at ($cli_templates_src)" } # ── 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.) + hooks ──────────────────── # The bootstrapper looks for *.nu at $data_dir/install/. # `ontoref hooks-install` looks for install/hooks/{post-commit,post-merge}. 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))" } let hooks_src = $"($repo_root)/install/hooks" let hooks_dest = $"($install_dest)/hooks" if ($hooks_src | path exists) { mkdir $hooks_dest for f in (glob $"($hooks_src)/*" | where { |p| ($p | path type) == "file" }) { let dest_f = $"($hooks_dest)/(($f | path basename))" install-if-changed $f $dest_f $"install/hooks/(($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" }