#!/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.npmjs.org}" 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}"