Files
ThothII/scripts/verify-workspace-install-docs.sh
T

179 lines
6.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# Verify the public installation and workspace documentation against the current topology.
set -euo pipefail
if [[ -n "${THT_DOCS_VERIFY_ROOT:-}" ]]; then
root="$(cd "$THT_DOCS_VERIFY_ROOT" && pwd -P)"
else
root="$(cd "$(dirname "$0")/.." && pwd -P)"
fi
fail() {
echo "verify-workspace-install-docs.sh: $*" >&2
exit 1
}
require_file() {
[[ -f "$root/$1" ]] || fail "missing required file: $1"
}
require_text() {
local path="$1" text="$2"
grep -Fq -- "$text" "$root/$path" || fail "$path must contain: $text"
}
forbid_text() {
local path="$1" text="$2"
if grep -Fq -- "$text" "$root/$path"; then
fail "$path retains obsolete reference: $text"
fi
}
verify_compose_topology() {
python3 - "$root/compose.yaml" <<'PY'
import pathlib
import sys
import yaml
compose = yaml.safe_load(pathlib.Path(sys.argv[1]).read_text())
expected = {
"catalog-db", "catalog-migrate", "core", "embedding", "embedding-model-init",
"frontend", "qdrant", "workspace-maintenance",
}
services = compose.get("services", {})
if set(services) != expected:
raise SystemExit(f"compose.yaml services mismatch: {sorted(services)}")
if services["catalog-migrate"].get("profiles") != ["catalog-maintenance"]:
raise SystemExit("catalog-migrate must remain an explicit catalog-maintenance operation")
if services["workspace-maintenance"].get("profiles") != ["workspace-maintenance"]:
raise SystemExit("workspace-maintenance must remain profile-gated")
if services["catalog-migrate"].get("depends_on", {}).get("catalog-db", {}).get("condition") != "service_healthy":
raise SystemExit("catalog-migrate must wait for healthy catalog-db")
PY
require_text scripts/run-stack.sh 'up -d catalog-db'
require_text scripts/run-stack.sh 'run --rm catalog-migrate'
echo "Compose topology contract passed"
}
verify_navigation() {
local path
for path in \
install/first-start.md \
install/standalone-manual-it.md \
install/standalone-manual-en.md \
operations/workspaces.md \
operations/database-management.md \
guida-utente.md \
architecture/overview.md \
contracts/catalog-schema-snapshot.md; do
require_file "docs/$path"
require_text mkdocs.yml "$path"
done
echo "Current documentation navigation contract passed"
}
verify_standalone_installation_guides() {
for guide in docs/install/standalone-manual-it.md docs/install/standalone-manual-en.md; do
require_file "$guide"
for text in \
'git clone https://git.tylconsulting.it/mptyl/ThothII.git' \
'scripts/check-standalone-prerequisites.sh' \
'scripts/install-tht.sh' \
'tht setup --profile local --shell-mode full --shell-default-locale en' \
'scripts/verify-standalone-install.sh' \
'Docker Hub' \
'Gate A' \
'Gate B'; do
require_text "$guide" "$text"
done
done
require_file scripts/check-standalone-prerequisites.sh
require_file scripts/verify-standalone-install.sh
echo "Manual standalone installation guides contract passed"
}
verify_install_and_workspace_guides() {
local install='docs/install/first-start.md'
local workspace='docs/operations/workspaces.md'
require_file "$install"
require_file "$workspace"
for text in \
'tht setup --profile local' \
'./scripts/run-stack.sh' \
'catalog-migrate' \
'tht --installation /absolute/path/thothii-installation.yaml doctor --json'; do
require_text "$install" "$text"
done
for text in \
'workspace inspect' \
'workspace preprocess run' \
'database in **Database Management**' \
'write-only runtime secrets' \
'Runtime sessions support direct PostgreSQL and REST bindings.'; do
require_text "$workspace" "$text"
done
for obsolete in \
'docs/install/local-workspace-registry.md' \
'docs/install/server-workspace-registry.md' \
'docs/install/local.md' \
'docs/install/server.md'; do
forbid_text README.md "$obsolete"
forbid_text "$install" "$obsolete"
forbid_text "$workspace" "$obsolete"
done
echo "Local installation and workspace operations contract passed"
}
verify_examples() {
require_file docs/install/examples/thothii-installation.local.yaml
require_file docs/install/examples/thothii-installation.server.yaml
require_file docs/install/examples/workspace-bindings.env.example
python3 - "$root/docs/install/examples/thothii-installation.local.yaml" \
"$root/docs/install/examples/thothii-installation.server.yaml" \
"$root/docs/install/examples/workspace-bindings.env.example" <<'PY'
import pathlib
import re
import sys
import yaml
local = yaml.safe_load(pathlib.Path(sys.argv[1]).read_text())
server = yaml.safe_load(pathlib.Path(sys.argv[2]).read_text())
bindings = pathlib.Path(sys.argv[3]).read_text()
if local.get("profile") != "local" or server.get("profile") != "server":
raise SystemExit("installation examples must retain their local/server profiles")
for document in (local, server):
catalog = document.get("modelCatalog")
if not isinstance(catalog, dict) or "providers" not in catalog or "authentication" not in document:
raise SystemExit("installation example lacks model catalog or authentication configuration")
if re.search(r"(?:KEY|PASSWORD|TOKEN|SECRET)=[^\n#<][^\n]*", bindings):
raise SystemExit("workspace bindings example contains a secret value")
PY
echo "Installation examples contract passed"
}
verify_all() {
verify_compose_topology
verify_navigation
verify_standalone_installation_guides
verify_install_and_workspace_guides
verify_examples
}
case "${1:-}" in
--fixtures-only)
[[ $# -eq 1 ]] || fail 'usage: verify-workspace-install-docs.sh --fixtures-only | --profile {local|server}'
verify_all
;;
--profile)
[[ $# -eq 2 && "${2:-}" =~ ^(local|server)$ ]] \
|| fail 'usage: verify-workspace-install-docs.sh --fixtures-only | --profile {local|server}'
verify_all
echo "$2 installation documentation verification passed"
;;
*)
fail 'usage: verify-workspace-install-docs.sh --fixtures-only | --profile {local|server}'
;;
esac