#!/usr/bin/env bash set -euo pipefail project_root="$(cd "$(dirname "$0")/.." && pwd)" python_bin="${PYINSTALLER_PYTHON:-$project_root/.venv-build/bin/python}" companion_root="$project_root/video_companion" if [[ "$(uname -s)" != "Darwin" ]]; then echo "The macOS bundle must be built on macOS." >&2 exit 2 fi if [[ ! -x "$python_bin" ]]; then echo "Build Python was not found: $python_bin" >&2 exit 2 fi if [[ "${SKIP_FRONTEND_INSTALL:-0}" != "1" ]]; then npm ci --prefix "$companion_root" --no-audit --no-fund fi npm run build --prefix "$companion_root" "$python_bin" -m PyInstaller \ --noconfirm \ --clean \ "$project_root/packaging/doctor_workstation.spec" artifact="$project_root/dist/DoctorWorkstation.app" helper="$(find "$artifact" -type f -name 'QtWebEngineProcess' -print -quit)" resources="$(find "$artifact" -type f -name 'qtwebengine_resources*.pak' -print -quit)" companion="$(find "$artifact" -type f -path '*video_companion_dist/index.html' -print -quit)" executable="$artifact/Contents/MacOS/DoctorWorkstation" [[ -n "$helper" ]] || { echo "QtWebEngineProcess is missing from the app." >&2; exit 1; } [[ -n "$resources" ]] || { echo "QtWebEngine resources are missing from the app." >&2; exit 1; } [[ -n "$companion" ]] || { echo "video_companion_dist is missing from the app." >&2; exit 1; } [[ -x "$executable" ]] || { echo "Frozen application entry point is missing." >&2; exit 1; } codesign --verify --deep --strict --verbose=2 "$artifact" temp_root="${TMPDIR:-/tmp}" temp_root="${temp_root%/}" smoke_root="$(mktemp -d "$temp_root/doctor-workstation-smoke.XXXXXX")" cleanup_smoke() { case "$smoke_root" in "$temp_root"/doctor-workstation-smoke.*) rm -rf -- "$smoke_root" ;; *) echo "Refusing to remove unsafe smoke-test directory: $smoke_root" >&2 ;; esac } trap cleanup_smoke EXIT mkdir -p \ "$smoke_root/Library/Application Support" \ "$smoke_root/Library/Caches" \ "$smoke_root/xdg/config" \ "$smoke_root/xdg/state" \ "$smoke_root/xdg/cache" \ "$smoke_root/tmp" set +e env \ TMPDIR="$smoke_root/tmp" \ XDG_CONFIG_HOME="$smoke_root/xdg/config" \ XDG_STATE_HOME="$smoke_root/xdg/state" \ XDG_CACHE_HOME="$smoke_root/xdg/cache" \ DOCTOR_CONFIG_DIR="$smoke_root/doctor/config" \ DOCTOR_LOG_DIR="$smoke_root/doctor/logs" \ DOCTOR_API_BASE_URL="https://127.0.0.1:9" \ DOCTOR_DEMO_MODE="true" \ DOCTOR_VIDEO_MODE="embedded" \ DOCTOR_VIDEO_WEB_URL="" \ DOCTOR_VERIFY_SSL="true" \ DOCTOR_LOG_LEVEL="INFO" \ DOCTOR_SMOKE_TEST="1" \ HTTP_PROXY="http://127.0.0.1:9" \ HTTPS_PROXY="http://127.0.0.1:9" \ ALL_PROXY="http://127.0.0.1:9" \ NO_PROXY="" \ QT_QPA_PLATFORM="offscreen" \ "$executable" --smoke-test >"$smoke_root/stdout.txt" 2>"$smoke_root/stderr.txt" & smoke_pid=$! smoke_running=1 for _ in {1..300}; do if ! kill -0 "$smoke_pid" 2>/dev/null; then smoke_running=0 break fi sleep 0.1 done if [[ "$smoke_running" == "1" ]]; then kill "$smoke_pid" 2>/dev/null wait "$smoke_pid" 2>/dev/null smoke_status=124 else wait "$smoke_pid" smoke_status=$? fi set -e if [[ "$smoke_status" -ne 0 ]]; then cat "$smoke_root/stderr.txt" >&2 echo "Frozen application smoke test failed with exit code $smoke_status." >&2 exit 1 fi diagnostics="$smoke_root/diagnostics.txt" cat "$smoke_root/stdout.txt" "$smoke_root/stderr.txt" >"$diagnostics" find "$smoke_root" -type f -name '*.log' -exec cat {} + >>"$diagnostics" if grep -Eiq 'traceback \(most recent call last\)|unhandled exception|uncaught exception|fatal python error' "$diagnostics"; then cat "$diagnostics" >&2 echo "Frozen application smoke-test logs contain an unhandled exception." >&2 exit 1 fi echo "Frozen entry smoke test passed (--smoke-test, isolated demo mode)." echo "Build complete: $artifact"