website-htmx-rustelo-code/scripts/build/distro.nu
2026-07-10 03:44:13 +01:00

123 lines
5.4 KiB
Text

#!/usr/bin/env nu
# Assemble a transportable distro tarball from the single manifest
# provisioning/distro.ncl — the one source of truth shared with the Dockerfiles
# (image_only) and content.nu (content_hot).
#
# The tarball is the PV payload (site_tree) plus the active profile's image_only
# artifacts: the Leptos WASM pkg/ for leptos-hydration, nothing for htmx-ssr.
# Unpacked at /var/www/site/ it is exactly what the content-agnostic image
# expects on its PV.
#
# Profile is auto-detected from rendering.ncl (same source as `just build-auto`)
# unless given explicitly. website.css is rebuilt first so site/public/styles is
# current — both profiles serve it from the PV.
#
# Usage:
# nu scripts/build/distro.nu [profile] # profile: leptos-hydration | htmx-ssr
# nu scripts/build/distro.nu --out dist --skip-css
def detect-profile [project_root: string]: nothing -> string {
let dev_root = ($env.DEV_ROOT? | default "/Users/Akasha/Development")
let detector = $"($dev_root)/rustelo/scripts/check-wasm-needed.nu"
if ($detector | path exists) {
let res = (do {
cd $project_root
^nu $detector --routes-dir site/config --workspace-config site/config/rendering.ncl
} | complete)
match $res.exit_code {
0 => "leptos-hydration"
1 => "htmx-ssr"
_ => { error make { msg: $"check-wasm-needed.nu failed: ($res.stderr | str trim)" } }
}
} else {
open $"($project_root)/site/config/rendering.ncl"
| parse --regex 'default_profile = "(?P<p>[^"]+)"' | get p?.0? | default "leptos-hydration"
}
}
def pick-tar []: nothing -> string {
let gtar = (do { ^which gtar } | complete)
if $gtar.exit_code == 0 { ($gtar.stdout | str trim) } else { "tar" }
}
def main [
profile?: string
--out: string = "dist"
--skip-css
]: nothing -> nothing {
let project_root = ($env.FILE_PWD | path dirname | path dirname) # scripts/build → root
let manifest = $"($project_root)/provisioning/distro.ncl"
if not ($manifest | path exists) { error make { msg: $"manifest not found: ($manifest)" } }
let resolved = if ($profile | is-not-empty) {
if $profile not-in ["leptos-hydration" "htmx-ssr"] {
error make { msg: $"invalid profile '($profile)' (leptos-hydration | htmx-ssr)" }
}
$profile
} else {
detect-profile $project_root
}
print $"profile: ($resolved)"
let distro = (^nickel export $manifest --format json | from json)
let site_tree = $distro.site_tree
let image_only = ($distro.image_only | get $resolved)
let wanted = ($site_tree ++ $image_only)
# Refresh website.css so the PV-served stylesheet matches current sources.
if not $skip_css {
print "css: rebuilding (pnpm run css:build)"
do { cd $project_root; ^pnpm run css:build } | complete
| if $in.exit_code != 0 { error make { msg: "css:build failed" } } else { ignore }
}
let present = ($wanted | where { |p| ($project_root | path join $p) | path exists })
let missing = ($wanted | where { |p| not (($project_root | path join $p) | path exists) })
for m in $missing {
if ($m | str contains "pkg") {
print $"(ansi yellow)warning:(ansi reset) ($m) missing — run `cargo leptos build --release` first for leptos-hydration"
} else {
print $"(ansi yellow)warning:(ansi reset) manifest path absent: ($m)"
}
}
if ($present | is-empty) { error make { msg: "nothing to pack — all manifest paths absent" } }
let timestamp = (date now | format date "%Y%m%dT%H%M%S")
let git_sha = (do { cd $project_root; ^git rev-parse --short HEAD } | complete
| if $in.exit_code == 0 { $in.stdout | str trim } else { "unknown" })
# Normalise both sources into the PV layout rooted at /var/www. Source paths
# diverge (site/* are already PV-shaped; the Leptos pkg is at target/site/pkg
# but must land at site/pkg next to the binary's WASM). Stage so the archive
# is the merged tree — unpacked with `tar -C /var/www` like content.nu does.
let stage = (^mktemp -d | str trim)
for src in $present {
let dest = if ($src | str starts-with "target/site/") {
$src | str replace "target/site/" "site/"
} else { $src }
let dest_abs = ($stage | path join $dest)
mkdir ($dest_abs | path dirname)
^cp -R ($project_root | path join $src) $dest_abs
}
let out_dir = if ($out | str starts-with "/") { $out } else { $project_root | path join $out }
mkdir $out_dir
let outfile = $"($out_dir)/website-($resolved)-($timestamp).tgz"
let manifest_out = $"($out_dir)/website-($resolved)-($timestamp).manifest.json"
{
version: $timestamp, profile: $resolved, git_sha: $git_sha,
site_tree: $site_tree, image_only: $image_only, packed: $present,
} | to json | save -f $manifest_out
let tar_bin = (pick-tar)
do { ^$tar_bin --exclude='.DS_Store' -czf $outfile -C $stage site } | complete
| if $in.exit_code != 0 { ^rm -rf $stage; error make { msg: "tar failed" } } else { ignore }
^rm -rf $stage
let size = (ls $outfile | get size | first)
print $"(ansi green)✅(ansi reset) ($outfile) (($size))"
print $" manifest: ($manifest_out)"
print $" layout: archive root = site/ → unpack with: tar -xzf <tarball> -C /var/www"
}