feat(usable): package gui-host validation snapshot
Some checks failed
release-smoke / macos-13 / x86_64-apple-darwin (push) Has been cancelled
release-smoke / ubuntu-latest / x86_64-unknown-linux-gnu (push) Has been cancelled
release-smoke / windows-latest / x86_64-pc-windows-msvc (push) Has been cancelled

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Paperclip CTO
2026-03-31 10:21:36 +00:00
parent 19aeb7784b
commit a28dab4cd9
47 changed files with 6894 additions and 771 deletions

155
scripts/tui/demo-stack.sh Executable file
View File

@@ -0,0 +1,155 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
COMPOSE_FILE="${COMPOSE_FILE:-$ROOT_DIR/docker-compose.demo.yml}"
POSTGRES_CONTAINER_NAME="${POSTGRES_CONTAINER_NAME:-dbtool-cli-v1-postgres-1}"
MYSQL_CONTAINER_NAME="${MYSQL_CONTAINER_NAME:-dbtool-cli-v1-mysql-1}"
DEMO_NETWORK_NAME="${DEMO_NETWORK_NAME:-dbtool-cli-v1_default}"
usage() {
echo "usage: scripts/tui/demo-stack.sh <up|down|seed|reset|status>" >&2
exit 2
}
docker_compose_available() {
docker compose version >/dev/null 2>&1
}
container_exists() {
local container_name="$1"
docker container inspect "$container_name" >/dev/null 2>&1
}
container_running() {
local container_name="$1"
[ "$(docker inspect -f '{{.State.Running}}' "$container_name" 2>/dev/null || true)" = "true" ]
}
ensure_network() {
if ! docker network inspect "$DEMO_NETWORK_NAME" >/dev/null 2>&1; then
docker network create "$DEMO_NETWORK_NAME" >/dev/null
fi
}
wait_for_health() {
local container_name="$1"
local max_attempts="${2:-60}"
local attempt=1
while [ "$attempt" -le "$max_attempts" ]; do
local health
health="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}running{{end}}' "$container_name" 2>/dev/null || true)"
if [ "$health" = "healthy" ] || [ "$health" = "running" ]; then
return 0
fi
sleep 2
attempt=$((attempt + 1))
done
echo "error: container did not become healthy: $container_name" >&2
docker logs --tail 50 "$container_name" >&2 || true
exit 1
}
run_postgres_container() {
if container_exists "$POSTGRES_CONTAINER_NAME"; then
if ! container_running "$POSTGRES_CONTAINER_NAME"; then
docker start "$POSTGRES_CONTAINER_NAME" >/dev/null
fi
return
fi
ensure_network
docker run -d \
--name "$POSTGRES_CONTAINER_NAME" \
--network "$DEMO_NETWORK_NAME" \
-p 55432:5432 \
-e POSTGRES_DB=dbtool_demo \
-e POSTGRES_USER=dbtool \
-e POSTGRES_PASSWORD=dbtool \
--health-cmd 'pg_isready -U dbtool -d dbtool_demo' \
--health-interval 5s \
--health-timeout 5s \
--health-retries 20 \
postgres:16 >/dev/null
}
run_mysql_container() {
if container_exists "$MYSQL_CONTAINER_NAME"; then
if ! container_running "$MYSQL_CONTAINER_NAME"; then
docker start "$MYSQL_CONTAINER_NAME" >/dev/null
fi
return
fi
ensure_network
docker run -d \
--name "$MYSQL_CONTAINER_NAME" \
--network "$DEMO_NETWORK_NAME" \
-p 53306:3306 \
-e MYSQL_DATABASE=dbtool_demo \
-e MYSQL_USER=dbtool \
-e MYSQL_PASSWORD=dbtool \
-e MYSQL_ROOT_PASSWORD=dbtoolroot \
--health-cmd 'mysqladmin ping -uroot -pdbtoolroot' \
--health-interval 5s \
--health-timeout 5s \
--health-retries 30 \
mysql:8.4 \
mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci >/dev/null
}
up_stack() {
if docker_compose_available; then
docker compose -f "$COMPOSE_FILE" up -d postgres mysql
else
run_postgres_container
run_mysql_container
fi
wait_for_health "$POSTGRES_CONTAINER_NAME"
wait_for_health "$MYSQL_CONTAINER_NAME"
echo "Demo stack is ready."
}
down_stack() {
if docker_compose_available; then
docker compose -f "$COMPOSE_FILE" down -v
return
fi
docker rm -f "$POSTGRES_CONTAINER_NAME" "$MYSQL_CONTAINER_NAME" >/dev/null 2>&1 || true
}
seed_stack() {
"$ROOT_DIR/examples/scripts/bootstrap-postgres.sh"
"$ROOT_DIR/examples/scripts/bootstrap-mysql.sh"
}
status_stack() {
docker ps --filter "name=$POSTGRES_CONTAINER_NAME" --filter "name=$MYSQL_CONTAINER_NAME" \
--format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
}
command="${1:-}"
case "$command" in
up)
up_stack
;;
down)
down_stack
;;
seed)
seed_stack
;;
reset)
down_stack
up_stack
seed_stack
;;
status)
status_stack
;;
*)
usage
;;
esac

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then
echo "usage: scripts/tui/live-network-smoke.sh <postgres|mysql> <binary-path> [log-path]" >&2
exit 2
fi
if ! command -v script >/dev/null 2>&1; then
echo "error: \`script\` command is required for TTY smoke." >&2
exit 3
fi
target="$1"
binary_path="$2"
log_path="${3:-/tmp/dbtool-tui-$target-live.log}"
if [ ! -x "$binary_path" ]; then
echo "error: binary is not executable: $binary_path" >&2
exit 4
fi
case "$target" in
postgres)
down_presses=1
endpoint='host.docker.internal:55432'
profile_name='reporting-postgres'
export_path='/tmp/dbtool-tui-reporting-postgres-account-ticket-summary-sql.csv'
activation_wait=4
;;
mysql)
down_presses=2
endpoint='host.docker.internal:53306'
profile_name='orders-mysql'
export_path='/tmp/dbtool-tui-orders-mysql-account-ticket-summary-sql.csv'
activation_wait=5
;;
*)
echo "error: unsupported target: $target" >&2
exit 5
;;
esac
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
"$ROOT_DIR/scripts/tui/demo-stack.sh" up
"$ROOT_DIR/scripts/tui/demo-stack.sh" seed
export DBTOOL_PASSWORD="${DBTOOL_PASSWORD:-dbtool}"
rm -f "$log_path"
{
sleep 1
printf '2'
if [ "$down_presses" -ge 1 ]; then
printf '\033[B'
fi
if [ "$down_presses" -ge 2 ]; then
printf '\033[B'
fi
printf '\r'
sleep "$activation_wait"
printf '\033'
printf '\r'
sleep 2
printf 'x'
sleep 2
printf 'q'
} | script -qec "stty rows 40 cols 120; \"$binary_path\"" "$log_path" >/dev/null
grep -aq "$profile_name" "$log_path"
grep -aq "Validation: Connected" "$log_path"
grep -aq "rows returned in" "$log_path"
[ -f "$export_path" ]
echo "TUI live smoke passed for $target."
echo "binary: $binary_path"
echo "endpoint: $endpoint"
echo "export: $export_path"
echo "log: $log_path"

33
scripts/tui/smoke-tty.sh Executable file
View File

@@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -lt 1 ] || [ "$#" -gt 4 ]; then
echo "usage: scripts/tui/smoke-tty.sh <binary-path> [rows] [cols] [log-path]" >&2
exit 2
fi
if ! command -v script >/dev/null 2>&1; then
echo "error: \`script\` command is required for TTY smoke." >&2
exit 3
fi
binary_path="$1"
rows="${2:-40}"
cols="${3:-120}"
log_path="${4:-/tmp/dbtool-tui-smoke.log}"
if [ ! -x "$binary_path" ]; then
echo "error: binary is not executable: $binary_path" >&2
exit 4
fi
command_string="$(printf 'stty rows %s cols %s; "%s"' "$rows" "$cols" "$binary_path")"
rm -f "$log_path"
printf 'q' | script -qec "$command_string" "$log_path" >/dev/null
echo "TTY smoke passed."
echo "binary: $binary_path"
echo "rows: $rows"
echo "cols: $cols"
echo "log: $log_path"