Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions .github/fleet-converge.sh
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,156 @@ converge_verify_only_cap() {
record "$verdict"
}

converge_read_gunbc_host_pin() {
# readGunbcPinStamp authority (gunbc_run.mjs): stamp files, then git HEAD — NOT shell env.
local root="$1" pin=""
for stamp in dsl-version interpreter-version; do
if [ -f "$root/$stamp" ]; then
pin="$(grep -Eo '[0-9a-f]{40}' "$root/$stamp" 2>/dev/null | head -n1 || true)"
if [ -n "$pin" ]; then printf '%s\n' "$pin"; return; fi
fi
done
if [ -d "$root/.git" ]; then
pin="$(git -C "$root" rev-parse HEAD 2>/dev/null | tr -d ' ' || true)"
if [ -n "$pin" ]; then printf '%s\n' "$pin"; return; fi
fi
printf '\n'
}

converge_read_ctrl_gunbc_pin() {
# readCtrlGunbcPin / read-gunbc-pin.sh authority: third_party/gunbc gitlink.
local ctrl="$1" script="$2" pin=""
if [ -f "$ctrl/$script" ]; then
pin="$(bash "$ctrl/$script" "$ctrl" 2>/dev/null || true)"
else
pin="$(git -C "$ctrl" rev-parse --verify --quiet 'HEAD:third_party/gunbc' 2>/dev/null || true)"
fi
printf '%s\n' "$pin"
}

converge_dashboard_process_gunbc_root() {
# PROCESS-effective GUNBC_ROOT: live dashboard PID environ, NOT env file on disk.
# Fail-closed: empty return when dashboard not running or /proc/<pid>/environ unreadable.
local pid="" root=""
for svc in ctrl-dashboard session-dashboard dashboard; do
pid="$(systemctl show "$svc" --property=MainPID --value 2>/dev/null || true)"
if [ -n "$pid" ] && [ "$pid" != "0" ] && [ -r "/proc/$pid/environ" ]; then
root="$(tr '\0' '\n' < "/proc/$pid/environ" | grep -E '^GUNBC_ROOT=' | head -n1 | cut -d= -f2- || true)"
printf '%s\n' "$root"; return
fi
done
printf '\n'
}

converge_dashboard_process_pin() {
# Process-effective pin via readGunbcPinStamp on the live dashboard GUNBC_ROOT.
local root="" pin=""
root="$(converge_dashboard_process_gunbc_root)"
if [ -z "$root" ]; then printf '\n'; return; fi
pin="$(converge_read_gunbc_host_pin "$root")"
printf '%s\n' "$pin"
}

converge_resolve_live_binary_root() {
local root="" pid="" exe=""
root="$(converge_dashboard_process_gunbc_root)"
if [ -n "$root" ] && { [ -x "$root/target/release/gunbc" ] || [ -x "$root/gunbc" ]; }; then printf '%s\n' "$root"; return; fi
for svc in ctrl-dashboard session-dashboard dashboard; do
pid="$(systemctl show "$svc" --property=MainPID --value 2>/dev/null || true)"
if [ -n "$pid" ] && [ "$pid" != "0" ] && [ -r "/proc/$pid/exe" ]; then
exe="$(readlink -f "/proc/$pid/exe" 2>/dev/null || true)"
if [ -n "$exe" ]; then root="$(dirname "$(dirname "$exe")")"; if [ -d "$root" ]; then printf '%s\n' "$root"; return; fi; fi
fi
done
for candidate in "$HOME/.local/share/gunbc/pinned" "$HOME/gunbc-pinned" "/opt/gunbc" "$HOME/gunbc"; do
if [ -x "$candidate/target/release/gunbc" ] || [ -x "$candidate/gunbc" ]; then printf '%s\n' "$candidate"; return; fi
done
printf '\n'
}

emit_gunbc_pinned_detail() {
printf 'converge-receipt host=%s slice=%s knob=%s desired=%s effective=%s verdict=%s drift=%s reason=%s gunbc_root=%s target_sha=%s dsl_mode=%s\n' "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "${10}" "${11}"
}

converge_green_place_marker_ok() {
# ONE artifact: pin=<40hex> first_green_at=<iso8601>. Stale pin must NOT satisfy derived ctrl gitlink.
local ctrl="$1" marker_path="$2" pin_prefix="$3" ts_prefix="$4" desired_pin="$5"
local line="" marker_pin=""
[ -f "$ctrl/$marker_path" ] || return 1
line="$(head -n1 "$ctrl/$marker_path" 2>/dev/null || true)"
marker_pin="$(printf '%s' "$line" | grep -Eo "$3[0-9a-f]{40}" | head -n1 | cut -d= -f2 || true)"
[ -n "$marker_pin" ] && [ "$marker_pin" = "$desired_pin" ] && printf '%s' "$line" | grep -q "$ts_prefix"
}

converge_invoke_quiescent_reload() {
# Option A: converge orchestrates; keen-dove script is the safe executor. Exit 75 = not quiescent (retry next tick).
local ctrl="$1" hook="$2" gunbc_root="$3" target_sha="$4" dsl_mode="$5"
if ! command -v node >/dev/null 2>&1 || [ ! -f "$ctrl/$hook" ]; then return 1; fi
node "$ctrl/$hook" --gunbc-root "$gunbc_root" --target-sha "$target_sha" --dsl-mode "$dsl_mode" --ctrl "$ctrl"
}

converge_gunbc_pinned_tree() {
# host slice knob apply_mode no_op gunbc_root_env gunbc_root_path dsl_mode dsl_path ctrl_checkout
# bin_registry bin_digest bin_name pin_read_script marker_path pin_prefix ts_prefix reload_hook
# DESIRED PIN: desired_pin := ctrl_pin from third_party/gunbc gitlink — never a hardcoded constant.
# NO-OP: binary/dsl/ctrl sha-coherent + process pin + timestamped green-place marker.
# LIVE-APPLY: quiescent-window restart (sub-second, NO runner-style drain); ~10min spawn-retry backstop.
# Option A: on drift invoke quiescent-reload.mjs; exit 75 -> drifted (retry); exit 0 + marker -> converged.
if [ "$8" = "defer_submodule" ]; then checkout="${10}/$9"; else checkout="$9"; fi
ctrl_pin="$(converge_read_ctrl_gunbc_pin "${10}" "${14}")"
desired_pin="$ctrl_pin"
dsl_pin="$(converge_read_gunbc_host_pin "$checkout")"
binary_root="$(converge_resolve_live_binary_root)"
binary_pin=""
if [ -n "$binary_root" ]; then binary_pin="$(converge_read_gunbc_host_pin "$binary_root")"; fi
process_pin="$(converge_dashboard_process_pin)"
pin_ok=0
if [ -n "$ctrl_pin" ] && [ -n "$dsl_pin" ] && [ -n "$binary_pin" ] && [ "$binary_pin" = "$dsl_pin" ] && [ "$dsl_pin" = "$ctrl_pin" ]; then
if [ -n "$process_pin" ] && [ "$process_pin" = "$ctrl_pin" ]; then pin_ok=1; fi
fi
green_ok=0
if converge_green_place_marker_ok "${10}" "${15}" "${16}" "${17}" "$desired_pin"; then green_ok=1; fi
if [ "$5" = "skip_process_pin_coherent_and_marker" ] && [ "$pin_ok" -eq 1 ] && [ "$green_ok" -eq 1 ]; then
verdict=converged; effective="$dsl_pin"
emit_receipt "$1" "$2" "$3" "$desired_pin" "$effective" "$verdict"
record "$verdict"; return
fi
if [ "$4" = "existing_host_quiescent" ]; then
drift_reason="pinned_tree_drift"
if [ -n "$binary_pin" ] && [ -n "$dsl_pin" ] && [ "$binary_pin" != "$dsl_pin" ]; then drift_reason="binary_dsl_sha_skew"; fi
reload_rc=1
if converge_invoke_quiescent_reload "${10}" "${18}" "$7" "$desired_pin" "$8"; then reload_rc=0; else reload_rc=$?; fi
if [ "$reload_rc" -eq 75 ]; then
echo "LIVE-APPLY: quiescent gate refused on $1 (not quiescent); drifted until next converge tick" >&2
elif [ "$reload_rc" -eq 0 ] && converge_green_place_marker_ok "${10}" "${15}" "${16}" "${17}" "$desired_pin"; then
verdict=converged; effective="$desired_pin"
emit_receipt "$1" "$2" "$3" "$desired_pin" "$effective" "$verdict"
record "$verdict"; return
fi
if [ -z "$dsl_pin" ]; then effective=ABSENT; verdict=absent; else effective="$dsl_pin"; verdict=drifted; fi
emit_gunbc_pinned_detail "$1" "$2" "$3" "$desired_pin" "$effective" "$verdict" "1" "$drift_reason" "$7" "$desired_pin" "$8"
record "$verdict"; return
fi
# fresh_standup: restart-free apply path for greenfield hosts (srv3 exemplar).
if [ -d "$checkout/.git" ]; then
(cd "$checkout" && git fetch --prune origin >/dev/null 2>&1) || true
(cd "$checkout" && git checkout --detach "$ctrl_pin" >/dev/null 2>&1) || true
dsl_pin="$(converge_read_gunbc_host_pin "$checkout")"
else dsl_pin=""; fi
mkdir -p "$7"
if [ ! -x "$7/${13}" ]; then
echo "binary pull ${11}@sha:$ctrl_pin into $7 (steady-state CAS/ghcr transport keyed by derived pin)" >&2
if [ ! -x "$7/${13}" ] && [ -d "$checkout" ]; then
(cd "$checkout" && cargo build -p v1-compiler --release --bin "${13}" >/dev/null 2>&1) || true
if [ -x "$checkout/target/release/${13}" ]; then cp "$checkout/target/release/${13}" "$7/${13}" 2>/dev/null || true; fi
fi
fi
if [ -x "$7/${13}" ]; then export "$6=$7"; fi
if [ -z "$dsl_pin" ]; then effective=ABSENT; verdict=absent; else decide_verdict "$dsl_pin" "$desired_pin"; effective="$dsl_pin"; fi
emit_receipt "$1" "$2" "$3" "$desired_pin" "$effective" "$verdict"
record "$verdict"
}

emit_sessions_membership() {
# host slice_cgroup legacy_cgroup (membership signal for the OomdEnforced guard; EMPTY slice => 0, not error)
in_slice=$(ls -d "$2"/docker-*.scope 2>/dev/null | wc -l | tr -d ' ' || true)
Expand All @@ -123,6 +273,7 @@ emit_sessions_membership() {
}

host_begin "srv1"
converge_gunbc_pinned_tree "srv1" "gunbc" "pinned_tree_sha" "existing_host_quiescent" "skip_process_pin_coherent_and_marker" "GUNBC_ROOT" "$HOME/.local/share/gunbc/pinned" "defer_submodule" "third_party/gunbc" "$HOME/ctrl" "ghcr.io/gunb-ai/gunbc/bootstrap" "sha256:BOOTSTRAP_DIGEST_PLACEHOLDER" "gunbc" "scripts/session-dashboard/container/read-gunbc-pin.sh" ".dashboard/converge-green-place.marker" "pin=" "first_green_at=" "scripts/session-dashboard/bin/gunbc-pin-quiescent-reload.mjs"
converge_slice_property "srv1" "runner" "runner_slice_cap_bytes" "system-actions-runner.slice" "MemoryMax" "85899345920" "85899345920" "85899345920"
converge_per_slot_cap "srv1" "runner" "per_slot_memory_max_bytes" "/etc/systemd/system/actions-runner@.service.d/20-fleet-width.conf" "actions-runner@srv1-*.service" "MemoryMax" "8589934592"
converge_per_slot_cap "srv1" "runner" "per_slot_memory_swap_max_bytes" "/etc/systemd/system/actions-runner@.service.d/30-fleet-swap.conf" "actions-runner@srv1-*.service" "MemorySwapMax" "0"
Expand All @@ -137,6 +288,7 @@ converge_slice_property "srv1" "sessions" "cpu_weight" "sessions.slice" "CPUWeig
emit_sessions_membership "srv1" "/sys/fs/cgroup/sessions.slice" "/sys/fs/cgroup/system.slice"
host_summary "srv1"
host_begin "srv2"
converge_gunbc_pinned_tree "srv2" "gunbc" "pinned_tree_sha" "existing_host_quiescent" "skip_process_pin_coherent_and_marker" "GUNBC_ROOT" "$HOME/.local/share/gunbc/pinned" "defer_submodule" "third_party/gunbc" "$HOME/ctrl" "ghcr.io/gunb-ai/gunbc/bootstrap" "sha256:BOOTSTRAP_DIGEST_PLACEHOLDER" "gunbc" "scripts/session-dashboard/container/read-gunbc-pin.sh" ".dashboard/converge-green-place.marker" "pin=" "first_green_at=" "scripts/session-dashboard/bin/gunbc-pin-quiescent-reload.mjs"
converge_slice_property "srv2" "runner" "runner_slice_cap_bytes" "system-actions-runner.slice" "MemoryMax" "85899345920" "85899345920" "85899345920"
converge_per_slot_cap "srv2" "runner" "per_slot_memory_max_bytes" "/etc/systemd/system/actions-runner@.service.d/20-fleet-width.conf" "actions-runner@srv2-*.service" "MemoryMax" "8589934592"
converge_per_slot_cap "srv2" "runner" "per_slot_memory_swap_max_bytes" "/etc/systemd/system/actions-runner@.service.d/30-fleet-swap.conf" "actions-runner@srv2-*.service" "MemorySwapMax" "0"
Expand Down
20 changes: 20 additions & 0 deletions dsl/extdeps/ctrl/gunbc_pin.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module extdeps.ctrl.gunbc_pin

import std.types { String, Bool }
import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }

data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
uri: Uri {
scheme: Https
locator: "github.com/gunb-ai/ctrl/blob/main/scripts/session-dashboard/lib/gunbc_run.mjs"
}
}

fn gunbc_pin_match(read_pin: String, desired_pin: String) -> Bool {
read_pin != "" && desired_pin != "" && read_pin == desired_pin
}

fn gunbc_pin_skewed(host_pin: String, ctrl_pin: String) -> Bool {
host_pin != "" && ctrl_pin != "" && host_pin != ctrl_pin
}
Loading