#!/usr/bin/env nu # GENERATED — component=lian_build bundle={{BUNDLE_ID}} op=build # Target: {{IMAGE}} let lian_build_root = "{{LIAN_BUILD_ROOT}}" let bundle_dir = $env.FILE_PWD let ctx = (mktemp -d | str trim) let secrets_base = "{{SECRETS_BASE}}" let target = (do { cd $lian_build_root cargo metadata --no-deps --format-version 1 | from json | get target_directory }) let bin = $"($target)/release/lian-build" # ── pre-flight ─────────────────────────────────────────────────────────────── print $"[pre-flight] bundle={{BUNDLE_ID}} (date now | format date '%Y-%m-%dT%H:%M:%S%z')" let directives_export = (do { ^nickel export --format json --import-path $lian_build_root $"($bundle_dir)/build_directives.ncl" } | complete) if $directives_export.exit_code != 0 { print "[pre-flight] WARNING: could not parse build_directives.ncl — skipping pre-flight checks" print $directives_export.stderr } else { let d = ($directives_export.stdout | from json) let adapter = ($d | get -o adapter | default {}) let kind = ($adapter | get -o kind | default "hcloud") let images = ($d | get -o artifacts | default [] | each { |a| $a | get -o image | default "?" } | str join ", ") print $"[pre-flight] image → ($images)" # ── 1. adapter identity + VPN check ───────────────────────────────────── if $kind == "k8s_local" { let ns = ($adapter | get -o k8s_namespace | default "build-system") let dep = ($adapter | get -o k8s_deployment | default "buildkitd") let port = ($adapter | get -o k8s_port | default 1234) print $"[pre-flight] buildkit → k8s_local ns=($ns) deployment=($dep) port=($port)" print "[pre-flight] k8s_local requires VPN — probing cluster..." let probe = (do { ^kubectl get namespace $ns --request-timeout=5s } | complete) if $probe.exit_code != 0 { print "" print "[pre-flight] ABORT: k8s cluster unreachable" print $" target: namespace=($ns)" print $" error: ($probe.stderr | lines | first | default 'no output')" print "" print " Likely cause: VPN is down or no route to cluster" print " Check: wg show (interface up? rx/tx traffic?)" print $" Check: kubectl get ns ($ns) (cluster reachable?)" rm -rf $ctx exit 1 } print $"[pre-flight] cluster ✓ — buildkitd reachable at ($ns)/($dep):($port)" } else if $kind == "hcloud" { let loc = ($adapter | get -o location | default "fsn1") print $"[pre-flight] buildkit → hcloud ephemeral location=($loc)" print "[pre-flight] runner: a Hetzner VM will be spawned, used, and destroyed" } else { print $"[pre-flight] buildkit → ($kind)" } # ── 2. registry credential check ──────────────────────────────────────── let reg_cred_path = ( $d | get -o registry | default {} | get -o push_credential | default {} | get -o path | default "" ) if ($reg_cred_path | is-empty) { print "[pre-flight] registry cred: no path defined — will rely on runner built-in auth" } else { let sops_file = $"($secrets_base)/($reg_cred_path)" if not ($sops_file | path exists) { print "" print "[pre-flight] ABORT: registry credential file not found" print $" expected: ($sops_file)" print $" check: LIAN_BUILD_WORKSPACE_ROOT / LIAN_BUILD_SECRETS_BASE" print "" print " The push will fail 401 — no point building without credentials" rm -rf $ctx exit 1 } let sops_check = (do { ^sops --decrypt $sops_file } | complete) if $sops_check.exit_code != 0 { print "" print "[pre-flight] ABORT: registry credential decrypt failed" print $" file: ($sops_file)" print $" error: ($sops_check.stderr | lines | first | default 'unknown')" print "" print " Hint: set SOPS_AGE_KEY_FILE to your age private key" print " The push will fail 401 — fix credentials before building" rm -rf $ctx exit 1 } print $"[pre-flight] registry cred ✓ ($reg_cred_path)" } } print "" print $"[build] bundle={{BUNDLE_ID}} image={{IMAGE}} started=(date now | format date '%Y-%m-%dT%H:%M:%S%z')" # ── context assembly ───────────────────────────────────────────────────────── if "{{CTX_TYPE}}" == "leptos" { just --justfile $"($lian_build_root)/justfile" _ctx-leptos "{{CONTEXT_SRC}}" $ctx } else if "{{CTX_TYPE}}" == "siblings" { for dir in ("{{CONTEXT_SRC_DIRS}}" | split row " " | where { |d| not ($d | is-empty) }) { let name = ($dir | path basename) ^rsync -a --exclude=target/ --exclude=node_modules/ --exclude=.git/ $"($dir)/" $"($ctx)/($name)/" } } else { ^rsync -a --exclude=target/ --exclude=node_modules/ --exclude=.git/ "{{CONTEXT_SRC}}/" $"($ctx)/" } let build_args = [ "build" "--directives" $"($bundle_dir)/build_directives.ncl" "--context" $ctx "--ssh-key" "{{SSH_KEY}}" "--language" "rust" "--secrets-base" $secrets_base "--runner-image" "{{RUNNER_IMAGE_REF}}" ] let keep_runner = ("LIAN_BUILD_KEEP_RUNNER" in $env) let extra_args = if $keep_runner { ["--keep-runner"] } else { [] } # ── build ──────────────────────────────────────────────────────────────────── # Wrap lian-build in a timeout to prevent hanging on runner cleanup. # Override: LIAN_BUILD_TIMEOUT_SECONDS= let timeout_secs = ($env.LIAN_BUILD_TIMEOUT_SECONDS? | default "600") let timeout_candidates = ( ["/opt/homebrew/bin/timeout" "/usr/bin/timeout" "/usr/local/bin/timeout"] | where { |p| $p | path exists } ) let timeout_bin = if ($timeout_candidates | is-not-empty) { $timeout_candidates | first } else { "" } let run_args = [$bin ...$build_args ...$extra_args] with-env { LIAN_BUILD_NICKEL_IMPORT_PATH: $lian_build_root } { if ($timeout_bin | is-not-empty) { run-external $timeout_bin $timeout_secs ...$run_args } else { run-external ...$run_args } } rm -rf $ctx print $"[build] OK finished=(date now | format date '%Y-%m-%dT%H:%M:%S%z')"