clawdie-iso/scripts/stage-colibri-iso.sh

172 lines
6.4 KiB
Bash
Raw Normal View History

#!/bin/sh
# Stage prebuilt Colibri FreeBSD service artifacts into an ISO/image root.
#
# This script does not build Colibri. Build or provide artifacts first:
# (cd /home/clawdie/ai/colibri && cargo build --workspace --release)
#
# Usage:
# COLIBRI_REPO=/home/clawdie/ai/colibri scripts/stage-colibri-iso.sh /path/to/image-root
# COLIBRI_ARTIFACT_DIR=/path/to/release scripts/stage-colibri-iso.sh /path/to/image-root
set -eu
if [ "${1:-}" = "" ]; then
echo "usage: $0 DESTDIR" >&2
exit 64
fi
DESTDIR=$1
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
REPO_ROOT=$(CDPATH= cd -- "${SCRIPT_DIR}/.." && pwd)
COLIBRI_REPO=${COLIBRI_REPO:-"/home/clawdie/ai/colibri"}
COLIBRI_ARTIFACT_DIR=${COLIBRI_ARTIFACT_DIR:-"${COLIBRI_REPO}/target/release"}
COLIBRI_STAGE_ENABLE=${COLIBRI_STAGE_ENABLE:-YES}
COLIBRI_STAGE_INCLUDE_TUI=${COLIBRI_STAGE_INCLUDE_TUI:-1}
COLIBRI_COST_MODE=${COLIBRI_COST_MODE:-smart}
BIN_DIR="${DESTDIR}/usr/local/bin"
RC_DIR="${DESTDIR}/usr/local/etc/rc.d"
ETC_DIR="${DESTDIR}/usr/local/etc/colibri"
DB_DIR="${DESTDIR}/var/db/colibri"
RUN_DIR="${DESTDIR}/var/run/colibri"
LOG_DIR="${DESTDIR}/var/log/colibri"
NEWSYSLOG_DIR="${DESTDIR}/usr/local/etc/newsyslog.conf.d"
RC_SOURCE="${COLIBRI_REPO}/packaging/freebsd/colibri_daemon.in"
NEWSYSLOG_SOURCE="${COLIBRI_REPO}/packaging/freebsd/newsyslog-colibri.conf"
require_file() {
if [ ! -f "$1" ]; then
echo "missing required Colibri artifact: $1" >&2
exit 66
fi
}
require_exec() {
if [ ! -x "$1" ]; then
echo "missing executable Colibri artifact: $1" >&2
echo "hint: (cd ${COLIBRI_REPO} && cargo build --workspace --release)" >&2
exit 66
fi
}
copy_bin() {
require_exec "${COLIBRI_ARTIFACT_DIR}/$1"
install -m 0555 "${COLIBRI_ARTIFACT_DIR}/$1" "${BIN_DIR}/$1"
}
require_file "${RC_SOURCE}"
require_file "${NEWSYSLOG_SOURCE}"
mkdir -p "${BIN_DIR}" "${RC_DIR}" "${ETC_DIR}" "${NEWSYSLOG_DIR}" "${DB_DIR}" "${RUN_DIR}" "${LOG_DIR}"
copy_bin colibri-daemon
copy_bin colibri
copy_bin colibri-smoke-agent
copy_bin colibri-mcp
if [ "${COLIBRI_STAGE_INCLUDE_TUI}" != "0" ] && [ -x "${COLIBRI_ARTIFACT_DIR}/colibri-tui" ]; then
copy_bin colibri-tui
fi
install -m 0555 "${RC_SOURCE}" "${RC_DIR}/colibri_daemon"
install -m 0644 "${NEWSYSLOG_SOURCE}" "${NEWSYSLOG_DIR}/colibri.conf"
if ! grep -q '^command="/usr/sbin/daemon"' "${RC_DIR}/colibri_daemon" || \
! grep -Eq -- '-o .*colibri_daemon_(program|binary)' "${RC_DIR}/colibri_daemon"; then
echo "ERROR: staged colibri_daemon rc.d script does not supervise colibri-daemon with daemon(8)" >&2
echo " Update COLIBRI_REPO (${COLIBRI_REPO}) before building; the live USB must not block boot in rc.d." >&2
exit 66
fi
# Fix procname: the upstream procname="/usr/sbin/daemon" doesn't match
# daemon(8)'s process title ("daemon: name[pid] (daemon)"). Match the
# first word instead. Since check_pidfile uses the pidfile (PID-scoped),
# there's no collision with other daemon(8) instances.
sed -i '' 's/^procname="\/usr\/sbin\/daemon"$/procname="daemon:"/' \
"${RC_DIR}/colibri_daemon"
# Fix ${name}_program override: rc.subr line 1120 silently replaces
# command= with ${name}_program if set, so colibri_daemon_program=
# overrode command="/usr/sbin/daemon" — daemon(8) was never invoked.
# Rename the variable so rc.subr leaves command= alone. Newer Colibri sources
# may already use colibri_daemon_binary; this replacement is intentionally
# harmless when there is no old variable left.
sed -i '' 's/colibri_daemon_program/colibri_daemon_binary/g' \
"${RC_DIR}/colibri_daemon"
# Remove -u from daemon(8) args: rc.subr already runs as colibri via su,
# so daemon(8)'s own privilege drop double-drops and fails with
# "failed to set user environment".
sed -i '' 's/ -u \${colibri_daemon_user} //' \
"${RC_DIR}/colibri_daemon"
# Fix pidfile permissions: daemon(8) -P creates the pidfile as 0600
# owned by the target user, which blocks non-root users (clawdie) from
# running 'service colibri_daemon status'. Chmod in poststart.
# Use awk instead of sed append syntax: BSD sed's multi-line append form is
# easy to get wrong and can corrupt the rc.d script by appending to every line.
# Newer Colibri sources may already carry these chmods; do not duplicate them.
if ! grep -q 'chmod 644 "${pidfile}"' "${RC_DIR}/colibri_daemon"; then
_rc_tmp="${RC_DIR}/colibri_daemon.tmp"
awk '
{ print }
/socket ready/ {
print " chmod 644 \"${pidfile}\" 2>/dev/null || true"
print " chmod 660 \"${colibri_daemon_socket}\" 2>/dev/null || true"
}
' "${RC_DIR}/colibri_daemon" > "${_rc_tmp}"
mv "${_rc_tmp}" "${RC_DIR}/colibri_daemon"
chmod 0555 "${RC_DIR}/colibri_daemon"
fi
# Add DeepSeek API key and cache warming to daemon environment.
# These are injected into the rc.d prestart so the daemon picks them up
# without needing a separate config file.
sed -i '' '/export COLIBRI_COST_MODE/a\
export DEEPSEEK_API_KEY="${DEEPSEEK_API_KEY:-}"\
export COLIBRI_CACHE_WARMING="true"\
export COLIBRI_CACHE_WARMING_INTERVAL_HOURS="6"' \
"${RC_DIR}/colibri_daemon"
cat > "${ETC_DIR}/rc.conf.sample" <<EOF
# Colibri control plane service defaults for the Clawdie ISO.
# Merge into /etc/rc.conf or /etc/rc.conf.d/colibri_daemon.
colibri_daemon_enable="${COLIBRI_STAGE_ENABLE}"
colibri_daemon_user="colibri"
colibri_daemon_group="colibri"
colibri_daemon_data_dir="/var/db/colibri"
colibri_daemon_run_dir="/var/run/colibri"
colibri_daemon_socket="/var/run/colibri/colibri.sock"
colibri_daemon_db_path="/var/db/colibri/colibri.sqlite"
colibri_daemon_logfile="/var/log/colibri/daemon.log"
colibri_daemon_host="\$(hostname)"
colibri_cost_mode="${COLIBRI_COST_MODE}"
EOF
cat > "${ETC_DIR}/README.iso" <<'EOF'
Colibri ISO staging notes
=========================
The ISO build creates the colibri user/group and stages the rc.d service.
The colibri-daemon runs under daemon(8) supervision and is enabled at boot.
If the daemon fails, it restarts automatically without blocking SDDM/XFCE.
Runtime validation:
service colibri_daemon start
colibri status
colibri create-task --title "iso smoke"
colibri list-tasks --status queued
colibri-mcp tools
COLIBRI_MCP_WRITE=1 colibri-mcp tools # trusted write-capable MCP profile
service colibri_daemon stop
EOF
chmod 0755 "${DB_DIR}" "${RUN_DIR}" "${LOG_DIR}"
cat <<EOF
Staged Colibri into ${DESTDIR}
artifacts: ${COLIBRI_ARTIFACT_DIR}
rc.d : ${RC_SOURCE}
enable : ${COLIBRI_STAGE_ENABLE}
cost : ${COLIBRI_COST_MODE}
EOF