ontoref/install/install.nu

310 lines
12 KiB
Plaintext
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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"
}