diff --git a/README.md b/README.md index 777f18b..1b84e26 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ DOCKER_BUILDKIT=1 docker build --network=host \ docker run --rm -it \ -p 14620:14620 \ -p 14621:14621 \ + -v media_crawler_runtime:/app/runtime \ --env-file backend/.env \ -e GIT_REPO_URL=https://git.rc707blog.top/rose_cat707/media_crawler.git \ -e GIT_BRANCH=main \ @@ -95,4 +96,8 @@ docker run --rm -it \ - `GIT_BRANCH`:拉取分支(默认 `main`) - `WORKTREE_DIR`:容器内代码目录(默认 `/app/runtime`) - `NPM_REGISTRY`:前端依赖安装镜像源(默认 `https://registry.npmmirror.com`) +- `NPM_FALLBACK_REGISTRY`:`NPM_REGISTRY` 失败时回退源(默认 `https://registry.npmjs.org`) +- `FORCE_FRONTEND_REBUILD`:强制每次重装前端依赖并重建(默认 `0`,仅 lockfile 变更时才安装构建) +- `NPM_INSTALL_TIMEOUT_SEC`:前端依赖安装超时秒数(默认 `900`) +- `NPM_PROGRESS_INTERVAL_SEC`:安装过程心跳日志间隔秒数(默认 `15`) diff --git a/docker/start.sh b/docker/start.sh index bb18f06..7a51065 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -7,6 +7,32 @@ 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 @@ -27,11 +53,52 @@ fi echo "[start] installing frontend dependencies and building" cd "${WORKTREE_DIR}/frontend" -echo "[start] npm registry: ${NPM_REGISTRY}" -npm config set registry "${NPM_REGISTRY}" -npm ci --no-audit --no-fund -echo "[start] building frontend assets" -npm run build +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"