refactor: remove portal deployment coupling

This commit is contained in:
2026-08-05 06:58:19 +02:00
parent fd1fd2f802
commit 5d037e97c4
25 changed files with 265 additions and 452 deletions
@@ -1,70 +0,0 @@
#!/bin/sh
set -eu
root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
source_env=${1:-"$root/../../harness/.env"}
workspace=${2:-"$root/../../../tht-workspace-psd"}
auth_file=${3:-"$HOME/.pi/agent/auth.json"}
value() {
awk -F= -v key="$1" '$1 == key { sub(/^[^=]*=/, ""); sub(/[[:space:]].*$/, ""); print; exit }' "$source_env"
}
required() {
result=$(value "$1")
[ -n "$result" ] || { echo "missing $1 in local source configuration" >&2; exit 2; }
printf '%s' "$result"
}
test -f "$source_env"
test -d "$workspace"
test -f "$auth_file"
ca=$(value THT_SSL_CA)
[ -z "$ca" ] || test -f "$ca"
model_key=$(jq -er '.zai.key' "$auth_file")
test -n "$model_key"
umask 077
mkdir -p "$root/deploy/secrets" "$root/deploy/workspaces"
: >"$root/deploy/thothii.env"
cp "$root/deploy/compose.psd-local.yaml.example" "$root/deploy/compose.psd-local.yaml"
cp "$root/deploy/workspaces/psd.yaml.example" "$root/deploy/workspaces/psd.yaml"
cat >"$root/.env" <<EOF
COMPOSE_FILE=compose.yaml:deploy/compose.psd-local.yaml
COMPOSE_PROFILES=
THT_SECRETS_FILE=./deploy/secrets/thothii.secrets
THOTH_HTTP_PORT=8080
AUTH_MODE=none
THOTH_PUBLIC_EXPOSURE=false
MAX_PI_PROCESSES=4
PI_PROVIDER=zai
PI_MODEL=glm-5.2
PI_THINKING=medium
PI_AUTH_FILE=$auth_file
THT_PROFILE=workstation
THT_DB_NAME=$(required THT_DB_NAME)
THT_DWH_REST_URL=$(required THT_DWH_REST_URL)
THT_VEC_REST_URL=$(required THT_VEC_REST_URL)
THT_VEC_WRITE_REST_URL=$(required THT_VEC_WRITE_REST_URL)
THT_OLLAMA_URL=http://host.docker.internal:11434
THT_DOCS_ROOT=/data/workspaces/psd
THT_PSD_WORKSPACE_HOST_PATH=$workspace
EOF
cat >"$root/deploy/secrets/thothii.secrets" <<EOF
THT_MODEL_API_KEY=$model_key
THT_DWH_API_KEY=$(required THT_DWH_API_KEY)
THT_VEC_API_KEY=$(required THT_VEC_API_KEY)
THT_VEC_WRITE_API_KEY=$(required THT_VEC_WRITE_API_KEY)
EOF
if [ -n "$ca" ]; then
cat >>"$root/deploy/compose.psd-local.yaml" <<EOF
- type: bind
source: $ca
target: /run/secrets/ca-chain.pem
read_only: true
EOF
printf '%s\n' 'THT_CA=/run/secrets/ca-chain.pem' >>"$root/deploy/secrets/thothii.secrets"
else
printf '%s\n' 'THT_CA=/etc/ssl/certs/ca-certificates.crt' >>"$root/deploy/secrets/thothii.secrets"
fi
chmod 600 "$root/.env" "$root/deploy/thothii.env" "$root/deploy/secrets/thothii.secrets"
echo "Local PSD Docker configuration materialized without printing secret values."
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Smoke test del deploy standalone ThothII (core + frontend).
# Usa docker-compose.dev.yml (rete propria, porte host). Non tocca il portale.
# Prereq: deploy/thothii.env popolato + ruoli DB creati + pi-config + settings.json.
# Usa docker-compose.dev.yml (rete propria, porte host).
# Prereq: deploy/thothii.env popolato, endpoint esterni configurati e profilo Pi locale.
set -euo pipefail
cd "$(dirname "$0")/.."
+12 -60
View File
@@ -1,68 +1,20 @@
#!/usr/bin/env bash
# run-stack.sh — avvia i 3 layer reali di ThothII per la validazione end-to-end.
# run-stack.sh — avvia lo stack Compose locale di ThothII in primo piano.
#
# frontend (browser) → backend (Fastify :8787) → pi --mode rpc (GLM 5.2) → tht/harness → DWH
# Il core include Pi; DWH, vector DB, embedding e LLM sono endpoint esterni configurati
# in deploy/env/local.env. Non richiede un eseguibile Pi sull'host.
#
# Prerequisiti: VPN attiva, `pi` su PATH (GLM 5.2 configurato), harness/.env popolato,
# harness/config/tht.yaml -> workspace cliente, deps installate nei 3 progetti.
#
# Uso: ./scripts/run-stack.sh
# Stop: Ctrl-C (termina backend + frontend; i processi pi figli del backend muoiono con esso).
# Preparazione: cp deploy/env/local.env.example deploy/env/local.env e compilare i valori.
# Uso: ./scripts/run-stack.sh [argomenti aggiuntivi per docker compose up]
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
HARNESS="$ROOT/harness"
BACKEND="$ROOT/backend"
FRONTEND="$ROOT/frontend"
THT_BIN="$HARNESS/.venv/bin/tht"
BACKEND_PORT="${BACKEND_PORT:-8787}"
FRONTEND_PORT="${FRONTEND_PORT:-5173}"
LOCAL_ENV_FILE="${THT_LOCAL_ENV_FILE:-$ROOT/deploy/env/local.env}"
# --- preflight ---------------------------------------------------------------
[ -f "$HARNESS/.env" ] || { echo "ERRORE: $HARNESS/.env mancante (credenziali DWH/vector)"; exit 1; }
[ -x "$THT_BIN" ] || { echo "ERRORE: $THT_BIN non trovato (esegui: cd harness && python -m venv .venv && pip install -e .)"; exit 1; }
command -v pi >/dev/null || { echo "ERRORE: 'pi' non sul PATH (configura @earendil-works/pi-coding-agent con GLM 5.2)"; exit 1; }
[ -e "$HARNESS/config/tht.yaml" ] || { echo "ERRORE: $HARNESS/config/tht.yaml mancante (symlink al workspace)"; exit 1; }
[[ -f "$LOCAL_ENV_FILE" ]] || {
echo "ERRORE: $LOCAL_ENV_FILE mancante. Copia deploy/env/local.env.example e configura gli endpoint." >&2
exit 1
}
# Carica le variabili del DWH/vector nell'ambiente: il backend le passa a pi e a tht.
set -a; . "$HARNESS/.env"; set +a
# tht deve essere raggiungibile dal processo pi che il backend spawna.
export PATH="$HARNESS/.venv/bin:$PATH"
echo "== ThothII stack =="
echo " harness : $HARNESS (tht: $THT_BIN)"
echo " backend : http://localhost:$BACKEND_PORT"
echo " frontend: http://localhost:$FRONTEND_PORT"
echo " pi : $(command -v pi)"
echo
# --- avvio -------------------------------------------------------------------
pids=()
cleanup() { echo; echo "Arresto stack..."; for p in "${pids[@]}"; do kill "$p" 2>/dev/null || true; done; }
trap cleanup EXIT INT TERM
# Backend: usa il pi reale + il tht del venv, cwd harness per lo spawn di pi.
(
cd "$BACKEND"
PORT="$BACKEND_PORT" \
THT_HARNESS_DIR="$HARNESS" \
THT_BIN="$THT_BIN" \
PI_BIN="pi" \
AUTH_MODE="none" \
THT_DWH_PRECHECK="1" \
npm run dev
) &
pids+=($!)
# Frontend: il browser usa sempre /api; Vite lo inoltra al backend locale.
(
cd "$FRONTEND"
THT_FRONTEND_API_UPSTREAM="http://localhost:$BACKEND_PORT" \
npm run dev -- --port "$FRONTEND_PORT"
) &
pids+=($!)
echo "Stack avviato. Apri http://localhost:$FRONTEND_PORT e crea una nuova domanda."
echo "Ctrl-C per fermare."
wait
exec docker compose --env-file "$LOCAL_ENV_FILE" \
-f "$ROOT/compose.yaml" -f "$ROOT/deploy/compose.local.yaml" up --build "$@"
@@ -6,6 +6,9 @@ project="thothii-external-lifecycle-$$"
cleanup() { docker compose --project-name "$project" --profile external down --volumes >/dev/null 2>&1 || true; }
trap cleanup EXIT HUP INT TERM
export THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git
export PI_AUTH_FILE=/dev/null
unset THT_VECTOR_BOOTSTRAP_PASSWORD_SECRET_FILE THT_VECTOR_MIGRATOR_PASSWORD_SECRET_FILE
unset THT_VECTOR_READER_PASSWORD_SECRET_FILE THT_VECTOR_WRITER_PASSWORD_SECRET_FILE
rendered=$(docker compose --project-name "$project" --profile external config)
@@ -13,6 +16,10 @@ if printf '%s' "$rendered" | grep -q 'THT_VECTOR_.*PASSWORD_FILE\|vector_.*passw
echo "external config contains local vector secret references" >&2
exit 1
fi
if printf '%s' "$rendered" | grep -Eqi 'omics_portal|chirone|localllm_default|/home/chirone|datamart-builder'; then
echo "external config contains deployment-specific coupling" >&2
exit 1
fi
docker compose --project-name "$project" --profile external up --build --wait core
core=$(docker compose --project-name "$project" --profile external ps -q core)
inspect=$(docker inspect "$core")
@@ -20,4 +27,8 @@ if printf '%s' "$inspect" | grep -q 'THT_VECTOR_.*PASSWORD_FILE\|/run/secrets/ve
echo "external core inspect contains local vector secret references" >&2
exit 1
fi
if printf '%s' "$inspect" | grep -Eqi 'omics_portal|chirone|localllm_default|/home/chirone|datamart-builder'; then
echo "external core inspect contains deployment-specific coupling" >&2
exit 1
fi
echo "external core lifecycle without local vector secrets passed."
+4 -4
View File
@@ -54,13 +54,13 @@ if [ "$response" != '{"backend":"fastify","path":"/health"}' ]; then
exit 1
fi
if ! rg -Fq 'THT_FRONTEND_API_UPSTREAM="http://localhost:$BACKEND_PORT"' "$ROOT/scripts/run-stack.sh"; then
echo "run-stack.sh must configure the Vite internal upstream from BACKEND_PORT" >&2
if ! rg -Fq -- '-f "$ROOT/compose.yaml" -f "$ROOT/deploy/compose.local.yaml" up --build "$@"' "$ROOT/scripts/run-stack.sh"; then
echo "run-stack.sh must start the base+local Compose stack" >&2
exit 1
fi
if rg -q 'VITE_BACKEND_URL' "$ROOT/scripts/run-stack.sh"; then
echo "run-stack.sh must keep the browser base on /api" >&2
if rg -q 'command -v pi|PI_BIN="pi"|PI_BIN=pi' "$ROOT/scripts/run-stack.sh"; then
echo "run-stack.sh must not require a host Pi binary" >&2
exit 1
fi
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
content_targets=(
.dockerignore
compose.yaml
docker-compose.dev.yml
deploy
docker
frontend/vite.config.ts
README.md
docs/install
docs/installazione-docker-4-contesti.md
.env.example
scripts/run-stack.sh
scripts/docker-smoke.sh
)
matches=$(
rg -n -i \
-g '!deploy/workspaces/**' \
-g '!docker/session-migrate.sh' \
-g '!docker/cutover-legacy-sessions.sh' \
-g '!docker/smoke/**' \
'omics_portal|chirone|localllm_default|datamart-builder|compose\.production\.yaml|compose\.psd-local\.yaml' \
"${content_targets[@]}" || true
)
runtime_psd_matches=$(
rg -n -i \
-g '!deploy/workspaces/**' \
-g '!docker/session-migrate.sh' \
-g '!docker/cutover-legacy-sessions.sh' \
-g '!docker/smoke/**' \
'\bpsd\b' \
.dockerignore compose.yaml docker-compose.dev.yml deploy docker frontend/vite.config.ts \
.env.example scripts/run-stack.sh scripts/docker-smoke.sh || true
)
offenders=()
for superseded_file in \
deploy/compose.production.yaml \
deploy/compose.psd-local.yaml.example \
deploy/compose.psd-local.yaml \
scripts/bootstrap-local-psd-docker-config.sh \
harness/tests/test_psd_local_compose_contract.py
do
[[ ! -e "$superseded_file" ]] || offenders+=("$superseded_file (forbidden active deployment filename)")
done
if [[ -n "$matches" ]]; then
while IFS= read -r match; do
offenders+=("$match")
done <<<"$matches"
fi
if [[ -n "$runtime_psd_matches" ]]; then
while IFS= read -r match; do
offenders+=("$match")
done <<<"$runtime_psd_matches"
fi
if rg -n 'command -v pi|PI_BIN="pi"|PI_BIN=pi' scripts/run-stack.sh >/dev/null; then
offenders+=("scripts/run-stack.sh (requires a host Pi binary)")
fi
if ((${#offenders[@]})); then
printf '%s\n' "active deployment coupling found:" >&2
printf '%s\n' "${offenders[@]}" >&2
exit 1
fi
echo "no active PSD, Chirone, or portal deployment coupling found."
+4 -2
View File
@@ -9,7 +9,8 @@ auth_file="$tmp/auth.json"
printf '%s\n' '{}' >"$auth_file"
chmod 0600 "$auth_file"
rendered=$(PI_AUTH_FILE="$auth_file" docker compose config)
rendered=$(THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git \
PI_AUTH_FILE="$auth_file" docker compose config)
printf '%s\n' "$rendered" | grep -q "source: $auth_file"
printf '%s\n' "$rendered" | grep -q 'target: /home/thoth/.pi/agent/auth.json'
printf '%s\n' "$rendered" | grep -A4 'target: /home/thoth/.pi/agent/auth.json' \
@@ -29,6 +30,7 @@ assert settings["enabledModels"] == [
PY
grep -q '^ARG PI_VERSION=0.80.3$' docker/core.Dockerfile
grep -q '^PI_AUTH_FILE=$auth_file$' scripts/bootstrap-local-psd-docker-config.sh
grep -q '^PI_AUTH_FILE=/absolute/path/to/pi-auth.json$' deploy/env/local.env.example
grep -q '^PI_AUTH_FILE=/absolute/path/to/pi-auth.json$' deploy/env/server.env.example
echo "Pi user-auth Compose contract passed."
+13
View File
@@ -44,4 +44,17 @@ printf 'FROM scratch\n' > "$fixture_root/Dockerfile"
"$repo_root/scripts/verify-line-endings.sh" "$fixture_root"
git_fixture="$fixture_root/git-worktree"
mkdir -p "$git_fixture"
git -C "$git_fixture" init -q
printf 'tracked then deleted\n' >"$git_fixture/deleted.md"
git -C "$git_fixture" add deleted.md
rm "$git_fixture/deleted.md"
git_output="$(cd "$git_fixture" && "$repo_root/scripts/verify-line-endings.sh" 2>&1)"
if grep -Fq 'No such file or directory' <<<"$git_output"; then
echo "line-ending verifier tried to read a tracked deletion" >&2
exit 1
fi
echo "line-ending verifier tests passed"
+1
View File
@@ -23,6 +23,7 @@ root="$(cd "$root" && pwd)"
offenders=()
while IFS= read -r -d '' file; do
[[ -f "$file" ]] || continue
case "$file" in
*.ps1) continue ;;
esac