160 lines
6.8 KiB
Cheetah
160 lines
6.8 KiB
Cheetah
#!/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=<n>
|
|
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')"
|