Add Reactive Resume, Metrics, Kiwix, Resume Matcher, and Apple Health from the earlier SelfStack project. Rewrite Apple Health collector to use InfluxDB v2 with proper error handling. Update all tests, scripts, Homepage config, env template, and documentation for the expanded stack. New services: - Reactive Resume (4016) + Postgres/Minio/Chrome companions - Metrics (4021) - GitHub metrics visualization - Kiwix (4022) - offline wiki reader - Resume Matcher (4023) - AI resume screening - Apple Health (4024) - health data collector → InfluxDB v2 Also adds git policy to AGENTS.md: always commit and push automatically. 💘 Generated with Crush Assisted-by: GLM-5.1 via Crush <crush@charm.land>
266 lines
8.6 KiB
Bash
Executable File
266 lines
8.6 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
DEMO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
ENV_FILE="$DEMO_DIR/demo.env"
|
|
ENV_TEMPLATE="$DEMO_DIR/demo.env.template"
|
|
TEMPLATE_FILE="$DEMO_DIR/docker-compose.yml.template"
|
|
COMPOSE_FILE="$DEMO_DIR/docker-compose.yml"
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
|
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
|
|
ensure_env() {
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
if [[ -f "$ENV_TEMPLATE" ]]; then
|
|
log_info "Creating demo.env from template..."
|
|
cp "$ENV_TEMPLATE" "$ENV_FILE"
|
|
else
|
|
log_error "No demo.env or demo.env.template found"
|
|
exit 1
|
|
fi
|
|
fi
|
|
# Ensure new variables exist in older env files
|
|
grep -q '^MAILHOG_SMTP_PORT=' "$ENV_FILE" || echo "MAILHOG_SMTP_PORT=4019" >> "$ENV_FILE"
|
|
grep -q '^HOMEPAGE_ALLOWED_HOSTS=' "$ENV_FILE" || echo "HOMEPAGE_ALLOWED_HOSTS=*" >> "$ENV_FILE"
|
|
grep -q '^REACTIVE_RESUME_PORT=' "$ENV_FILE" || echo "REACTIVE_RESUME_PORT=4016" >> "$ENV_FILE"
|
|
grep -q '^RESUME_MINIO_PORT=' "$ENV_FILE" || echo "RESUME_MINIO_PORT=4020" >> "$ENV_FILE"
|
|
grep -q '^METRICS_PORT=' "$ENV_FILE" || echo "METRICS_PORT=4021" >> "$ENV_FILE"
|
|
grep -q '^KIWIX_PORT=' "$ENV_FILE" || echo "KIWIX_PORT=4022" >> "$ENV_FILE"
|
|
grep -q '^RESUME_MATCHER_PORT=' "$ENV_FILE" || echo "RESUME_MATCHER_PORT=4023" >> "$ENV_FILE"
|
|
grep -q '^APPLEHEALTH_PORT=' "$ENV_FILE" || echo "APPLEHEALTH_PORT=4024" >> "$ENV_FILE"
|
|
}
|
|
|
|
detect_user() {
|
|
log_info "Detecting user IDs..."
|
|
local uid gid docker_gid
|
|
uid=$(id -u)
|
|
gid=$(id -g)
|
|
docker_gid=$(getent group docker | cut -d: -f3)
|
|
sed -i "s/^DEMO_UID=.*/DEMO_UID=$uid/" "$ENV_FILE"
|
|
sed -i "s/^DEMO_GID=.*/DEMO_GID=$gid/" "$ENV_FILE"
|
|
sed -i "s/^DEMO_DOCKER_GID=.*/DEMO_DOCKER_GID=$docker_gid/" "$ENV_FILE"
|
|
log_success "UID=$uid GID=$gid DockerGID=$docker_gid"
|
|
}
|
|
|
|
check_prerequisites() {
|
|
log_info "Checking prerequisites..."
|
|
if ! docker info >/dev/null 2>&1; then
|
|
log_error "Docker is not running"
|
|
exit 1
|
|
fi
|
|
if ! command -v envsubst >/dev/null 2>&1; then
|
|
log_error "envsubst not found (install gettext package)"
|
|
exit 1
|
|
fi
|
|
local max_map_count
|
|
max_map_count=$(sysctl -n vm.max_map_count 2>/dev/null || echo "0")
|
|
if [[ "$max_map_count" -lt 262144 ]]; then
|
|
log_warn "Setting vm.max_map_count=262144 for Elasticsearch..."
|
|
if sudo sysctl -w vm.max_map_count=262144 2>/dev/null; then
|
|
log_success "vm.max_map_count set"
|
|
else
|
|
log_warn "Could not set vm.max_map_count (TubeArchivist ES may fail)"
|
|
fi
|
|
fi
|
|
log_success "Prerequisites OK"
|
|
}
|
|
|
|
generate_compose() {
|
|
log_info "Generating docker-compose.yml from template..."
|
|
set -a; source "$ENV_FILE"; set +a
|
|
envsubst < "$TEMPLATE_FILE" > "$COMPOSE_FILE"
|
|
log_success "docker-compose.yml generated"
|
|
}
|
|
|
|
deploy_stack() {
|
|
log_info "Deploying TSYS Developer Support Stack..."
|
|
cd "$DEMO_DIR"
|
|
docker compose up -d 2>&1
|
|
log_success "Stack deployment initiated"
|
|
}
|
|
|
|
wait_healthy() {
|
|
log_info "Waiting for services to become healthy (max 5 min)..."
|
|
local elapsed=0 interval=15
|
|
while [[ $elapsed -lt 300 ]]; do
|
|
local unhealthy=0
|
|
while IFS= read -r name; do
|
|
local health
|
|
health=$(docker inspect --format='{{.State.Health.Status}}' "$name" 2>/dev/null || echo "unknown")
|
|
if [[ "$health" != "healthy" ]]; then
|
|
unhealthy=$((unhealthy + 1))
|
|
fi
|
|
done < <(docker ps --filter "name=${COMPOSE_PROJECT_NAME:-kneldevstack}" --format '{{.Names}}' 2>/dev/null)
|
|
|
|
if [[ $unhealthy -eq 0 ]]; then
|
|
log_success "All services healthy"
|
|
return 0
|
|
fi
|
|
log_info " $unhealthy services not yet healthy (${elapsed}s elapsed)"
|
|
sleep $interval
|
|
elapsed=$((elapsed + interval))
|
|
done
|
|
log_warn "Timeout - some services may not be fully healthy"
|
|
cd "$DEMO_DIR" && docker compose ps
|
|
}
|
|
|
|
display_summary() {
|
|
set -a; source "$ENV_FILE"; set +a
|
|
echo ""
|
|
echo "========================================================"
|
|
echo " TSYS Developer Support Stack - Deployment Summary"
|
|
echo "========================================================"
|
|
echo ""
|
|
echo " Infrastructure:"
|
|
echo " Homepage Dashboard http://localhost:${HOMEPAGE_PORT}"
|
|
echo " Pi-hole (DNS) http://localhost:${PIHOLE_PORT}"
|
|
echo " Dockhand (Docker) http://localhost:${DOCKHAND_PORT}"
|
|
echo ""
|
|
echo " Monitoring:"
|
|
echo " InfluxDB http://localhost:${INFLUXDB_PORT}"
|
|
echo " Grafana http://localhost:${GRAFANA_PORT}"
|
|
echo " Metrics http://localhost:${METRICS_PORT}"
|
|
echo " Apple Health http://localhost:${APPLEHEALTH_PORT}"
|
|
echo ""
|
|
echo " Documentation:"
|
|
echo " Draw.io http://localhost:${DRAWIO_PORT}"
|
|
echo " Kroki http://localhost:${KROKI_PORT}"
|
|
echo " Kiwix http://localhost:${KIWIX_PORT}"
|
|
echo ""
|
|
echo " Developer Tools:"
|
|
echo " Atomic Tracker http://localhost:${ATOMIC_TRACKER_PORT}"
|
|
echo " ArchiveBox http://localhost:${ARCHIVEBOX_PORT}"
|
|
echo " Tube Archivist http://localhost:${TUBE_ARCHIVIST_PORT}"
|
|
echo " Wakapi http://localhost:${WAKAPI_PORT}"
|
|
echo " MailHog (Web) http://localhost:${MAILHOG_PORT}"
|
|
echo " MailHog (SMTP) localhost:${MAILHOG_SMTP_PORT}"
|
|
echo " Atuin http://localhost:${ATUIN_PORT}"
|
|
echo ""
|
|
echo " Productivity:"
|
|
echo " Reactive Resume http://localhost:${REACTIVE_RESUME_PORT}"
|
|
echo " Resume Matcher http://localhost:${RESUME_MATCHER_PORT}"
|
|
echo ""
|
|
echo " Credentials: admin / demo_password"
|
|
echo " FOR DEMONSTRATION PURPOSES ONLY"
|
|
echo "========================================================"
|
|
}
|
|
|
|
smoke_test() {
|
|
log_info "Running smoke tests..."
|
|
set -a; source "$ENV_FILE"; set +a
|
|
local ports=(
|
|
"${HOMEPAGE_PORT}:Homepage"
|
|
"${PIHOLE_PORT}:Pi-hole"
|
|
"${DOCKHAND_PORT}:Dockhand"
|
|
"${INFLUXDB_PORT}:InfluxDB"
|
|
"${GRAFANA_PORT}:Grafana"
|
|
"${DRAWIO_PORT}:Draw.io"
|
|
"${KROKI_PORT}:Kroki"
|
|
"${ATOMIC_TRACKER_PORT}:AtomicTracker"
|
|
"${ARCHIVEBOX_PORT}:ArchiveBox"
|
|
"${TUBE_ARCHIVIST_PORT}:TubeArchivist"
|
|
"${WAKAPI_PORT}:Wakapi"
|
|
"${MAILHOG_PORT}:MailHog"
|
|
"${ATUIN_PORT}:Atuin"
|
|
"${REACTIVE_RESUME_PORT}:ReactiveResume"
|
|
"${METRICS_PORT}:Metrics"
|
|
"${KIWIX_PORT}:Kiwix"
|
|
"${RESUME_MATCHER_PORT}:ResumeMatcher"
|
|
"${APPLEHEALTH_PORT}:AppleHealth"
|
|
)
|
|
local pass=0 fail=0
|
|
for pt in "${ports[@]}"; do
|
|
local port="${pt%:*}"
|
|
local svc="${pt#*:}"
|
|
if timeout 5 bash -c "echo > /dev/tcp/localhost/$port" 2>/dev/null; then
|
|
log_success "$svc (:$port)"
|
|
((pass++)) || true
|
|
else
|
|
log_error "$svc (:$port) NOT accessible"
|
|
((fail++)) || true
|
|
fi
|
|
done
|
|
echo ""
|
|
echo "SMOKE TEST: $pass passed, $fail failed"
|
|
}
|
|
|
|
stop_stack() {
|
|
log_info "Stopping stack..."
|
|
cd "$DEMO_DIR"
|
|
docker compose down 2>&1
|
|
log_success "Stack stopped"
|
|
}
|
|
|
|
show_status() {
|
|
cd "$DEMO_DIR"
|
|
docker compose ps
|
|
}
|
|
|
|
show_usage() {
|
|
echo "TSYS Developer Support Stack"
|
|
echo ""
|
|
echo "Usage: $0 {deploy|stop|restart|status|smoke|summary|help}"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " deploy Deploy the complete stack"
|
|
echo " stop Stop all services"
|
|
echo " restart Stop and redeploy"
|
|
echo " status Show service status"
|
|
echo " smoke Run port accessibility tests"
|
|
echo " summary Show service URLs"
|
|
echo " help Show this help"
|
|
}
|
|
|
|
ensure_env
|
|
|
|
case "${1:-deploy}" in
|
|
deploy)
|
|
detect_user
|
|
check_prerequisites
|
|
generate_compose
|
|
deploy_stack
|
|
wait_healthy
|
|
display_summary
|
|
smoke_test
|
|
;;
|
|
stop)
|
|
stop_stack
|
|
;;
|
|
restart)
|
|
stop_stack
|
|
sleep 5
|
|
detect_user
|
|
check_prerequisites
|
|
generate_compose
|
|
deploy_stack
|
|
wait_healthy
|
|
display_summary
|
|
;;
|
|
status)
|
|
show_status
|
|
;;
|
|
smoke)
|
|
smoke_test
|
|
;;
|
|
summary)
|
|
display_summary
|
|
;;
|
|
help|--help|-h)
|
|
show_usage
|
|
;;
|
|
*)
|
|
log_error "Unknown command: $1"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
esac
|