Files
daily_stock_analysis/scripts/build-backend-macos.sh
mumu 0924dce274 fix: detect stale static bundles in build pipeline and at runtime (#1064) (#1121)
* fix: detect stale static bundles in build pipeline and at runtime (#1064)

Add a packaging-time sanity check and a backend startup self-check that
verifies index.html only references /assets/*.js and /assets/*.css files
that actually exist on disk. This is the most common root cause of the
'Preparing backend...' / blank-page bug seen on the Windows immutable
Release packages: vite emits a fresh index.html with new content hashes,
but the desktop packaging step picks up a stale static/ directory, so
the browser receives a 404 (returned as application/json) for the main
bundle and refuses to execute it.

- scripts/check_static_assets.py: cross-platform sanity script.
- scripts/build-backend{.ps1,-macos.sh}: run the script after npm build
  on the source static/, and again after PyInstaller on the packaged
  static directory; fail the build on mismatch.
- api/app.py: log an actionable ERROR when an inconsistency is detected
  at startup so the root cause appears in logs/desktop.log instead of
  surfacing as a silent blank page.
- api/app.py: replace the StaticFiles mount with an explicit /assets/*
  route so misses return a plain-text 404 with a JS/CSS Content-Type,
  not the default JSON error response that masked diagnosis in #1064.
- tests/test_static_assets_consistency.py: 7 deterministic tests for
  both code paths.

Refs #1064 #1065 #1050
2026-04-25 20:22:59 +08:00

127 lines
3.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
log() {
echo "$1"
}
PYTHON_BIN="${PYTHON_BIN:-}"
if [[ -z "${PYTHON_BIN}" ]]; then
if command -v python3 >/dev/null 2>&1; then
PYTHON_BIN="python3"
else
PYTHON_BIN="python"
fi
fi
if ! command -v "${PYTHON_BIN}" >/dev/null 2>&1; then
echo "Python not found. Please install Python 3.10+ and retry."
exit 1
fi
log "Building React UI (static assets)..."
pushd "${ROOT_DIR}/apps/dsa-web" >/dev/null
if [[ ! -d node_modules ]]; then
npm install
fi
npm run build
popd >/dev/null
log "Verifying static asset references (source)..."
"${PYTHON_BIN}" "${SCRIPT_DIR}/check_static_assets.py" "${ROOT_DIR}/static"
log "Building backend executable..."
if ! "${PYTHON_BIN}" -m PyInstaller --version >/dev/null 2>&1; then
"${PYTHON_BIN}" -m pip install pyinstaller
fi
log "Installing backend dependencies..."
"${PYTHON_BIN}" -m pip install -r "${ROOT_DIR}/requirements.txt"
log "Checking python-multipart availability..."
"${PYTHON_BIN}" -c "import multipart, multipart.multipart"
if [[ -d "${ROOT_DIR}/dist/backend" ]]; then
rm -rf "${ROOT_DIR}/dist/backend"
fi
mkdir -p "${ROOT_DIR}/dist/backend"
if [[ -d "${ROOT_DIR}/dist/stock_analysis" ]]; then
rm -rf "${ROOT_DIR}/dist/stock_analysis"
fi
if [[ -d "${ROOT_DIR}/build/stock_analysis" ]]; then
rm -rf "${ROOT_DIR}/build/stock_analysis"
fi
hidden_imports=(
"multipart"
"multipart.multipart"
"json_repair"
"tiktoken"
"tiktoken_ext"
"tiktoken_ext.openai_public"
"api"
"api.app"
"api.deps"
"api.v1"
"api.v1.router"
"api.v1.endpoints"
"api.v1.endpoints.analysis"
"api.v1.endpoints.history"
"api.v1.endpoints.stocks"
"api.v1.endpoints.health"
"api.v1.schemas"
"api.v1.schemas.analysis"
"api.v1.schemas.history"
"api.v1.schemas.stocks"
"api.v1.schemas.common"
"api.middlewares"
"api.middlewares.error_handler"
"src.services"
"src.services.task_queue"
"src.services.analysis_service"
"src.services.history_service"
"uvicorn.logging"
"uvicorn.loops"
"uvicorn.loops.auto"
"uvicorn.protocols"
"uvicorn.protocols.http"
"uvicorn.protocols.http.auto"
"uvicorn.protocols.websockets"
"uvicorn.protocols.websockets.auto"
"uvicorn.lifespan"
"uvicorn.lifespan.on"
)
hidden_import_args=()
for module in "${hidden_imports[@]}"; do
hidden_import_args+=("--hidden-import=${module}")
done
pushd "${ROOT_DIR}" >/dev/null
cmd=("${PYTHON_BIN}" -m PyInstaller --name stock_analysis --onedir --noconfirm --noconsole --add-data "static:static" --collect-data litellm --collect-data tiktoken)
cmd+=("${hidden_import_args[@]}" "main.py")
echo "Running: ${cmd[*]}"
"${cmd[@]}"
popd >/dev/null
cp -R "${ROOT_DIR}/dist/stock_analysis" "${ROOT_DIR}/dist/backend/stock_analysis"
log "Verifying static asset references (packaged)..."
packaged_static="${ROOT_DIR}/dist/backend/stock_analysis/_internal/static"
if [[ ! -d "${packaged_static}" ]]; then
packaged_static="${ROOT_DIR}/dist/backend/stock_analysis/static"
fi
if [[ -d "${packaged_static}" ]]; then
"${PYTHON_BIN}" "${SCRIPT_DIR}/check_static_assets.py" "${packaged_static}"
else
log "WARNING: could not locate packaged static directory under dist/backend/stock_analysis; skipping post-package check."
fi
log "Backend build completed."