From f6fe9af0a266e2ef7669c945fc0a27cb781c18f0 Mon Sep 17 00:00:00 2001 From: zijunchiang Date: Tue, 15 Sep 2026 00:44:54 +0800 Subject: [PATCH] =?UTF-8?q?fix(deploy):=20=E4=BF=AE=E5=A4=8D=20NODE=5FENV?= =?UTF-8?q?=20=E6=B1=A1=E6=9F=93=E5=AF=BC=E8=87=B4=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=AB=AF=E6=9E=84=E5=BB=BA=E5=A4=B1=E8=B4=A5=EF=BC=88tsc:=20no?= =?UTF-8?q?t=20found=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户实测:部署时构建管理端报 `sh: 1: tsc: not found`,且 npm 只装了 23 个包。 根因:脚本开头用 `set -a; . deploy/.env; set +a` 载入 .env,其中的 NODE_ENV=production 被导出到脚本自身环境;npm 见到它便会跳过 devDependencies,于是 typescript / vite / nest 等构建工具全部缺失。 本地实测完全复现: NODE_ENV=production npm ci → added 23 package,无 tsc env -u NODE_ENV npm ci --include=dev → added 53 package,tsc/vite 齐备 修复: - 新增 npm_install_with_dev():统一用 `env -u NODE_ENV npm ... --include=dev` - 管理端构建改用它;**API 构建回退路径存在同一隐患,一并修复** - 管理端改为在 $APP_DIR/web-server/admin 内构建(不再污染源码树、 也不要求源码目录可写),构建完删除该处 node_modules 释放磁盘 - 构建失败时给出可操作提示(改用发布包) 另修复一处静默的信息泄露: - 复制源码树时未排除 .env,本地开发用的 web-server/.env(含真实数据库 口令)会被复制进 /opt/opendance/web-server/.env;deploy/.env 才是权威配置, 多一个 dotenv 配置源只会造成排障困惑。已把 .env / .env.local 加入排除, 并验证 .env.example 不受影响(209 → 208 个文件) 实测: 旧行为 tsc 缺失、构建报 tsc: not found(与用户报错一致) 新行为 tsc/vite 齐备,vite build 成功产出 dist/index.html + 3 个 assets 复制排除:.env 不复制、.env.example 保留、node_modules 无泄漏 --- deploy/install.sh | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/deploy/install.sh b/deploy/install.sh index 48faa84..ac8de4c 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -52,6 +52,19 @@ as_service_user() { fi } +# 安装**含 devDependencies** 的依赖。 +# 必须显式处理:脚本开头用 `set -a; . deploy/.env` 载入了 .env,其中的 +# NODE_ENV=production 会被导出到本脚本环境;npm 见到它会自动跳过 +# devDependencies,于是 tsc / vite / nest 等构建工具全部缺失 +# (用户实测报错:`sh: 1: tsc: not found`,且只装了 23 个包)。 +npm_install_with_dev() { + if [ -f package-lock.json ]; then + as_service_user env -u NODE_ENV npm ci --include=dev --no-audit --no-fund --loglevel=error + else + as_service_user env -u NODE_ENV npm install --include=dev --no-audit --no-fund --loglevel=error + fi +} + # HTTP GET,输出响应体。curl → wget → node(node 必有,且 22+ 自带 fetch) http_get() { if command -v curl >/dev/null 2>&1; then @@ -138,8 +151,11 @@ esac # 说明:在 git 检出目录直接执行时,web-server/ 里可能已有 200 MB+ 的 # node_modules(数万个小文件)。原实现用 cp -r 全量复制,会长时间无输出, # 看起来像卡死。这里统一排除,运行期依赖稍后由 npm ci 重新安装。 +# .env / .env.local 一并排除:deploy/.env 才是权威配置,源码树里的本地 .env +# 若被复制进来,dotenv 会把它当成第二配置源,造成排障困惑。 COPY_EXCLUDES=(--exclude=node_modules --exclude=.git --exclude=storage - --exclude=dist-release --exclude=coverage --exclude=.gradle) + --exclude=dist-release --exclude=coverage --exclude=.gradle + --exclude=.env --exclude=.env.local) copy_tree() { local src="$1" dst="$2" @@ -152,7 +168,7 @@ copy_tree() { # 嵌套的 "./admin/node_modules"(实测会漏掉 admin 下的 122MB 依赖)。 # 因此每个名字都要同时给出顶层与任意层级的模式。 local tar_excludes=() - for pattern in node_modules .git storage dist-release coverage .gradle; do + for pattern in node_modules .git storage dist-release coverage .gradle .env .env.local; do tar_excludes+=(--exclude="./$pattern" --exclude="*/$pattern") done ( cd "$src" && tar "${tar_excludes[@]}" -cf - . ) | ( cd "$dst" && tar -xf - ) @@ -169,22 +185,26 @@ for item in web-server contracts; do done # ---- 管理端产物:两种目录布局都要支持 ---- -# 发布包: /admin/ (已构建) -# 源码检出:/web-server/admin/dist/ (需已构建,缺失则现场构建) +# 发布包: /admin/ (已构建,直接用) +# 源码检出:/web-server/admin/dist/ (已构建,直接用) +# 否则在 $APP_DIR/web-server/admin 现场构建 +# (在安装目录内构建,不污染源码树,也不要求源码目录可写) ADMIN_SRC="" if [ -d "$SRC_DIR/admin" ]; then ADMIN_SRC="$SRC_DIR/admin" elif [ -d "$SRC_DIR/web-server/admin/dist" ]; then ADMIN_SRC="$SRC_DIR/web-server/admin/dist" -elif [ -d "$SRC_DIR/web-server/admin" ]; then - warn "未找到管理端构建产物,正在构建 web-server/admin(需要 devDependencies)" - if [ -f "$SRC_DIR/web-server/admin/package-lock.json" ]; then - ( cd "$SRC_DIR/web-server/admin" && npm ci --no-audit --no-fund --loglevel=error ) +elif [ -d "$APP_DIR/web-server/admin" ]; then + warn "未找到管理端构建产物,正在 ${APP_DIR}/web-server/admin 构建(需要 devDependencies)" + if ( cd "$APP_DIR/web-server/admin" && npm_install_with_dev \ + && as_service_user env -u NODE_ENV npm run build ); then + ADMIN_SRC="$APP_DIR/web-server/admin/dist" + # 构建完即可丢弃管理端的 node_modules,避免长期占用磁盘 + rm -rf "$APP_DIR/web-server/admin/node_modules" else - ( cd "$SRC_DIR/web-server/admin" && npm install --no-audit --no-fund --loglevel=error ) + die "管理端构建失败。可改用发布包(内含已构建的 admin/): + ./scripts/package-release.sh 然后在目标机解压部署" fi - ( cd "$SRC_DIR/web-server/admin" && npm run build ) - ADMIN_SRC="$SRC_DIR/web-server/admin/dist" fi [ -n "$ADMIN_SRC" ] || die "找不到管理端:既没有 admin/ 目录,也没有 web-server/admin/" @@ -215,8 +235,8 @@ fi if [ ! -f dist/main.js ]; then warn "未找到构建产物 dist/main.js,改为在服务器上构建(需要 devDependencies)" - as_service_user npm install --no-audit --no-fund - as_service_user npm run build + npm_install_with_dev + as_service_user env -u NODE_ENV npm run build fi [ -f dist/main.js ] || die "构建产物缺失:$APP_DIR/web-server/dist/main.js" [ -f "$APP_DIR/admin/index.html" ] || die "管理端构建产物缺失:$APP_DIR/admin/index.html"