Files
media_crawler/docker/start.sh
renjue 25e3a1e294 Add monitored npm install with timeout and fallback registry.
Improve container startup by printing periodic install heartbeat logs, enforcing npm install timeout, and retrying with a fallback registry to avoid silent hangs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-09 17:15:24 +08:00

119 lines
4.1 KiB
Bash

#!/usr/bin/env sh
set -eu
GIT_REPO_URL="${GIT_REPO_URL:-https://git.rc707blog.top/rose_cat707/media_crawler.git}"
GIT_BRANCH="${GIT_BRANCH:-main}"
WORKTREE_DIR="${WORKTREE_DIR:-/app/runtime}"
BACKEND_PORT="${FLASK_RUN_PORT:-14620}"
FRONTEND_PORT="${FRONTEND_PORT:-14621}"
NPM_REGISTRY="${NPM_REGISTRY:-https://registry.npmmirror.com}"
FORCE_FRONTEND_REBUILD="${FORCE_FRONTEND_REBUILD:-0}"
NPM_FALLBACK_REGISTRY="${NPM_FALLBACK_REGISTRY:-https://registry.npmjs.org}"
NPM_INSTALL_TIMEOUT_SEC="${NPM_INSTALL_TIMEOUT_SEC:-900}"
NPM_PROGRESS_INTERVAL_SEC="${NPM_PROGRESS_INTERVAL_SEC:-15}"
run_npm_install_with_monitor() {
INSTALL_CMD="$1"
START_TS="$(date +%s)"
sh -c "${INSTALL_CMD}" &
INSTALL_PID=$!
while kill -0 "${INSTALL_PID}" 2>/dev/null; do
NOW_TS="$(date +%s)"
ELAPSED="$((NOW_TS - START_TS))"
if [ "${ELAPSED}" -ge "${NPM_INSTALL_TIMEOUT_SEC}" ]; then
echo "[start] npm install timeout after ${ELAPSED}s, killing process"
kill "${INSTALL_PID}" 2>/dev/null || true
wait "${INSTALL_PID}" 2>/dev/null || true
return 124
fi
echo "[start] npm install running... elapsed=${ELAPSED}s"
sleep "${NPM_PROGRESS_INTERVAL_SEC}"
done
wait "${INSTALL_PID}"
}
echo "[start] syncing source from ${GIT_REPO_URL} (${GIT_BRANCH})"
if [ ! -d "${WORKTREE_DIR}/.git" ]; then
rm -rf "${WORKTREE_DIR}"
git clone --branch "${GIT_BRANCH}" --single-branch "${GIT_REPO_URL}" "${WORKTREE_DIR}"
else
git -C "${WORKTREE_DIR}" fetch origin "${GIT_BRANCH}"
git -C "${WORKTREE_DIR}" checkout "${GIT_BRANCH}"
git -C "${WORKTREE_DIR}" reset --hard "origin/${GIT_BRANCH}"
fi
echo "[start] installing backend dependencies"
VENV_DIR="${WORKTREE_DIR}/.venv"
if [ ! -x "${VENV_DIR}/bin/python" ]; then
python3 -m venv "${VENV_DIR}"
fi
"${VENV_DIR}/bin/pip" install --no-cache-dir -r "${WORKTREE_DIR}/backend/requirements.txt"
echo "[start] installing frontend dependencies and building"
cd "${WORKTREE_DIR}/frontend"
LOCKFILE_PATH="${WORKTREE_DIR}/frontend/package-lock.json"
LOCKFILE_HASH_PATH="${WORKTREE_DIR}/.cache/frontend_lock.sha256"
mkdir -p "${WORKTREE_DIR}/.cache"
if command -v sha256sum >/dev/null 2>&1; then
CURRENT_LOCK_HASH="$(sha256sum "${LOCKFILE_PATH}" | awk '{print $1}')"
else
CURRENT_LOCK_HASH="$(shasum -a 256 "${LOCKFILE_PATH}" | awk '{print $1}')"
fi
LAST_LOCK_HASH=""
if [ -f "${LOCKFILE_HASH_PATH}" ]; then
LAST_LOCK_HASH="$(cat "${LOCKFILE_HASH_PATH}")"
fi
NEED_FRONTEND_BUILD=0
if [ "${FORCE_FRONTEND_REBUILD}" = "1" ]; then
NEED_FRONTEND_BUILD=1
elif [ ! -d "${WORKTREE_DIR}/frontend/node_modules" ]; then
NEED_FRONTEND_BUILD=1
elif [ ! -d "${WORKTREE_DIR}/frontend/dist" ]; then
NEED_FRONTEND_BUILD=1
elif [ "${CURRENT_LOCK_HASH}" != "${LAST_LOCK_HASH}" ]; then
NEED_FRONTEND_BUILD=1
fi
if [ "${NEED_FRONTEND_BUILD}" = "1" ]; then
echo "[start] npm registry: ${NPM_REGISTRY}"
npm config set registry "${NPM_REGISTRY}"
npm config set fetch-retries 2
npm config set fetch-retry-mintimeout 20000
npm config set fetch-retry-maxtimeout 120000
npm config set fetch-timeout 120000
npm config set progress false
echo "[start] installing frontend dependencies (timeout=${NPM_INSTALL_TIMEOUT_SEC}s)"
if ! run_npm_install_with_monitor "npm install --prefer-offline --no-audit --no-fund --loglevel=info"; then
echo "[start] npm install failed with ${NPM_REGISTRY}, fallback to ${NPM_FALLBACK_REGISTRY}"
npm config set registry "${NPM_FALLBACK_REGISTRY}"
run_npm_install_with_monitor "npm install --prefer-offline --no-audit --no-fund --loglevel=info"
fi
echo "[start] building frontend assets"
npm run build
echo "${CURRENT_LOCK_HASH}" > "${LOCKFILE_HASH_PATH}"
else
echo "[start] skip frontend install/build (lockfile unchanged)"
fi
echo "[start] launching backend:${BACKEND_PORT} and frontend:${FRONTEND_PORT}"
cd "${WORKTREE_DIR}/backend"
"${VENV_DIR}/bin/python" app.py &
BACKEND_PID=$!
cd "${WORKTREE_DIR}/frontend"
npm run preview -- --host 0.0.0.0 --port "${FRONTEND_PORT}" &
FRONTEND_PID=$!
cleanup() {
kill "${BACKEND_PID}" "${FRONTEND_PID}" 2>/dev/null || true
}
trap cleanup INT TERM EXIT
wait "${BACKEND_PID}" "${FRONTEND_PID}"