Compare commits

...
Author SHA1 Message Date
zlwuandCursor 2550e9713d fix: 纳入 mobile pnpm-lock 并简化 CI 安装,保证与本地依赖树一致
删除无效 pnpm-workspace.yaml;CI 用 frozen-lockfile + 跳过 puppeteer 下载。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-24 18:29:00 +08:00
zlwuandCursor 4e1ca614d4 fix: mobile CI 使用 hoisted node_modules,修复 webpack DefinePlugin 找不到
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-24 18:20:30 +08:00
zlwuandCursor 291de33d54 fix: mobile CI 跳过 puppeteer Chromium 下载,避免 install 长时间卡住
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-24 18:11:59 +08:00
zlwuandCursor 902b04a21a docs: 同步 mobile CI 安装说明(无 lockfile / 无效 workspace)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-24 17:24:52 +08:00
zlwuandCursor ba6a6af131 fix: mobile CI 改用非 frozen install(pnpm-lock 未纳入仓库)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-24 17:24:40 +08:00
zlwuandCursor 5ab54912a5 fix: mobile CI 临时移走无效 pnpm-workspace.yaml 后再 install
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-24 17:17:01 +08:00
zlwuandCursor d6af2f1892 fix: mobile CI 用 --registry 指定镜像,避免 pnpm config 因 workspace 报错
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-24 17:09:16 +08:00
zlwuandCursor dcd29040de ci: Drone 暂跳过 web,仅 tag 打包 backend 与 mobile-h5
测试机内存不足以完成 Vite 生产构建,web 后续另议。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-24 17:01:06 +08:00
zlwuandCursor d6769846f5 fix: CI web 构建堆提升至 5120MB,避免低内存优化后仍 OOM
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-24 15:40:39 +08:00
zlwuandCursor 9d93a373ae perf(web): 降低生产构建内存峰值,并启用 CI 低内存打包模式
关闭按需 Element Plus 样式与构建期 dts、限制 rollup 并行、CI 跳过 gzip;
新增 npm run build:ci,供 Drone 在小内存机器上打包。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-24 15:30:38 +08:00
11 changed files with 15027 additions and 63 deletions

No files matched your search

+11 -21
View File
@@ -1,5 +1,6 @@
# 仅 tag 触发:顺序打包 backend / web / mobile-h5 # 仅 tag 触发:顺序打包 backend / mobile-h5
# 产物:/root/hzdyjs-releases/<tag>/{backend,web,mobile-h5}-<tag>.* # web 暂不在 CI 构建(测试机内存不足以跑 Vite 生产包),后续另议
# 产物:/root/hzdyjs-releases/<tag>/{backend,mobile-h5}-<tag>.*
# Maven 缓存:宿主机 /root/maven-repo → 容器 /maven-repo(由 runner DRONE_RUNNER_VOLUMES 注入) # Maven 缓存:宿主机 /root/maven-repo → 容器 /maven-repo(由 runner DRONE_RUNNER_VOLUMES 注入)
# 发版目录:/root/hzdyjs-releases → /hzdyjs-releases(同上,勿在本文件重复挂载) # 发版目录:/root/hzdyjs-releases → /hzdyjs-releases(同上,勿在本文件重复挂载)
@@ -35,23 +36,6 @@ steps:
- cp -v ruoyi-admin/target/ruoyi-admin.jar "/hzdyjs-releases/${DRONE_TAG}/backend-${DRONE_TAG}.jar" - cp -v ruoyi-admin/target/ruoyi-admin.jar "/hzdyjs-releases/${DRONE_TAG}/backend-${DRONE_TAG}.jar"
- ls -lh "/hzdyjs-releases/${DRONE_TAG}/backend-${DRONE_TAG}.jar" - ls -lh "/hzdyjs-releases/${DRONE_TAG}/backend-${DRONE_TAG}.jar"
- name: package-web
# 无 package-lock(被 gitignore);Node20 需 --legacy-peer-deps 规避 npm edgesOut
image: node:20-bookworm
environment:
NPM_CONFIG_REGISTRY: https://registry.npmmirror.com
commands:
- set -eu
- echo "==> web tag=${DRONE_TAG} npm=$(npm -v) node=$(node -v)"
- mkdir -p "/hzdyjs-releases/${DRONE_TAG}"
- cd web
- npm install --legacy-peer-deps --no-audit --no-fund
# 本前端生产构建实测需约 6G+ 堆;测试机靠扩容 swap(/swap-hzdyjs)硬扛
- node --max-old-space-size=6144 node_modules/vite/bin/vite.js build --mode production
- test -f dist/index.html
- tar -C dist -czf "/hzdyjs-releases/${DRONE_TAG}/web-${DRONE_TAG}.tar.gz" .
- ls -lh "/hzdyjs-releases/${DRONE_TAG}/web-${DRONE_TAG}.tar.gz"
- name: package-mobile-h5 - name: package-mobile-h5
image: node:20-bookworm image: node:20-bookworm
environment: environment:
@@ -63,8 +47,14 @@ steps:
- corepack enable - corepack enable
- corepack prepare pnpm@9 --activate - corepack prepare pnpm@9 --activate
- cd mobile - cd mobile
- pnpm config set registry https://registry.npmmirror.com # 仓库内 pnpm-workspace.yaml 无 packages 字段,pnpm9 会直接失败;单包工程移走即可
- pnpm install --frozen-lockfile - mv pnpm-workspace.yaml pnpm-workspace.yaml.ci-bak 2>/dev/null || true
# pnpm-lock.yaml 被 gitignore,不能 --frozen-lockfile
# 跳过 puppeteer 下载 Chromium(H5 构建不需要,否则会卡很久)
- export PUPPETEER_SKIP_DOWNLOAD=1 PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
# 无 lockfile 时 pnpm 默认隔离布局会导致 uni 找不到 webpack/lib/DefinePlugin;改用 hoisted
- printf '%s\n' 'shamefully-hoist=true' 'node-linker=hoisted' >> .npmrc
- pnpm install --no-frozen-lockfile --registry https://registry.npmmirror.com
- pnpm run build - pnpm run build
- test -d dist/build/h5 - test -d dist/build/h5
- tar -C dist/build/h5 -czf "/hzdyjs-releases/${DRONE_TAG}/mobile-h5-${DRONE_TAG}.tar.gz" . - tar -C dist/build/h5 -czf "/hzdyjs-releases/${DRONE_TAG}/mobile-h5-${DRONE_TAG}.tar.gz" .
@@ -1,18 +1,19 @@
# 杭州第一技师 — Drone Tag 三端打包设计 # 杭州第一技师 — Drone Tag 打包设计
## 背景 ## 背景
在测试机既有 Gogs + Drone(`101.35.245.122:18088`)上,为仓库 `hzdyjs-xg/all-project` 增加 **仅 tag 触发** 的自动打包,产出后端 / Web / 移动端 H5 三份独立产物,并复用宿主机 Maven 本地仓库以加速 Java 构建。 在测试机既有 Gogs + Drone(`101.35.245.122:18088`)上,为仓库 `hzdyjs-xg/all-project` 增加 **仅 tag 触发** 的自动打包。因测试机约 3.6G 内存不足以完成 web Vite 生产构建(实测约 36 分钟后 OOM),**CI 当前只打包 backend + mobile-h5**;web 暂缓,后续另议。
## 目标 ## 目标
- Push Git tag → 自动顺序构建三端并落盘 - Push Git tag → 自动顺序构建 backend、mobile-h5 并落盘
- 产物可按 tag 并存,便于回溯 - 产物可按 tag 并存,便于回溯
- Maven 依赖缓存在宿主机,不因容器销毁而丢失 - Maven 依赖缓存在宿主机,不因容器销毁而丢失
## 非目标 ## 非目标
- 不自动部署到业务机 - 不自动部署到业务机
- CI 暂不打包 web(可本地 `npm run build:prod` / `build:ci`)
- 不打微信小程序(`build:mp-weixin`);移动端按 `pnpm run build`(H5) - 不打微信小程序(`build:mp-weixin`);移动端按 `pnpm run build`(H5)
- 不合成单一总包 - 不合成单一总包
@@ -30,7 +31,6 @@
``` ```
/root/hzdyjs-releases/<tag>/ /root/hzdyjs-releases/<tag>/
backend-<tag>.jar backend-<tag>.jar
web-<tag>.tar.gz
mobile-h5-<tag>.tar.gz mobile-h5-<tag>.tar.gz
``` ```
@@ -42,13 +42,9 @@
`mvn clean package -Pprod -Dmaven.test.skip=true -Dmaven.compiler.parameters=true -Dmaven.repo.local=/maven-repo` `mvn clean package -Pprod -Dmaven.test.skip=true -Dmaven.compiler.parameters=true -Dmaven.repo.local=/maven-repo`
源文件:`backend/ruoyi-admin/target/ruoyi-admin.jar` → `backend-<tag>.jar` 源文件:`backend/ruoyi-admin/target/ruoyi-admin.jar` → `backend-<tag>.jar`
2. **web**(`node:20-bookworm`) 2. **mobile**(`node:20-bookworm` + corepack/pnpm)
`npm install` → `npm run build:prod` CI 先移走无效 `pnpm-workspace.yaml`,再
(`web/package-lock.json` 被 gitignore,仓库无 lock,不能用 `npm ci`) `pnpm install --no-frozen-lockfile`(`pnpm-lock.yaml` 被 gitignore)→ `pnpm run build`(H5)
打包 `web/dist` → `web-<tag>.tar.gz`
3. **mobile**(`node:20-bookworm` + corepack/pnpm)
`pnpm install --frozen-lockfile` → `pnpm run build`(即 H5)
打包 `mobile/dist/build/h5` → `mobile-h5-<tag>.tar.gz` 打包 `mobile/dist/build/h5` → `mobile-h5-<tag>.tar.gz`
## Maven 公共缓存 ## Maven 公共缓存
@@ -72,11 +68,11 @@
## 失败策略 ## 失败策略
任一步失败则整次构建失败;不部分覆盖同 tag 目录中已有文件以外的内容时,实现上可先 `mkdir -p` 再逐步写入——若中途失败,该 tag 目录可能不完整,以 Drone 构建状态为准。 任一步失败则整次构建失败;实现上可先 `mkdir -p` 再逐步写入——若中途失败,该 tag 目录可能不完整,以 Drone 构建状态为准。
## 验收 ## 验收
1. 推送测试 tag(如 `ci-pkg-test-YYYYMMDDHHMM`) 1. 推送测试 tag(如 `ci-pkg-test-YYYYMMDDHHMM`)
2. Drone 构建 success 2. Drone 构建 success
3. 三文件均存在于 `/root/hzdyjs-releases/<tag>/`,且 jar/tar 非空 3. `backend-*.jar` 与 `mobile-h5-*.tar.gz` 均存在于 `/root/hzdyjs-releases/<tag>/`,且非空(无 web 包)
4. 第二次同仓库构建时 Maven 日志出现大量依赖来自本地仓库(明显快于首次) 4. 第二次同仓库构建时 Maven 日志出现大量依赖来自本地仓库(明显快于首次)
+14958
View File
File diff suppressed because it is too large. Load diff
-5
View File
@@ -1,5 +0,0 @@
allowBuilds:
'@parcel/watcher': set this to true or false
core-js: set this to true or false
fsevents: set this to true or false
puppeteer: set this to true or false
+3 -2
View File
@@ -8,8 +8,9 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite serve --mode development", "dev": "vite serve --mode development",
"build:prod": "node --max-old-space-size=8192 node_modules/vite/bin/vite.js build --mode production", "build:prod": "node --max-old-space-size=6144 node_modules/vite/bin/vite.js build --mode production",
"build:dev": "node --max-old-space-size=8192 node_modules/vite/bin/vite.js build --mode development", "build:ci": "CI=true VITE_LOW_MEM=1 VITE_BUILD_COMPRESS= node --max-old-space-size=5120 node_modules/vite/bin/vite.js build --mode production",
"build:dev": "node --max-old-space-size=6144 node_modules/vite/bin/vite.js build --mode development",
"preview": "vite preview", "preview": "vite preview",
"lint:eslint": "eslint", "lint:eslint": "eslint",
"lint:eslint:fix": "eslint --fix", "lint:eslint:fix": "eslint --fix",
+22 -3
View File
@@ -5,6 +5,9 @@ import path from 'path';
export default defineConfig(({ mode, command }) => { export default defineConfig(({ mode, command }) => {
const env = loadEnv(mode, process.cwd()); const env = loadEnv(mode, process.cwd());
const isBuild = command === 'build';
// CI / 小内存机器:限制并行、跳过 gzip 插件(产物外层已 tar.gz)
const lowMem = process.env.CI === 'true' || process.env.VITE_LOW_MEM === '1';
return { return {
// 部署生产环境和开发环境下的URL。 // 部署生产环境和开发环境下的URL。
// 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上 // 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上
@@ -17,7 +20,7 @@ export default defineConfig(({ mode, command }) => {
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
}, },
// https://cn.vitejs.dev/config/#resolve-extensions // https://cn.vitejs.dev/config/#resolve-extensions
plugins: createPlugins(env, command === 'build'), plugins: createPlugins(env, isBuild),
server: { server: {
host: '0.0.0.0', host: '0.0.0.0',
port: Number(env.VITE_APP_PORT), port: Number(env.VITE_APP_PORT),
@@ -54,6 +57,23 @@ export default defineConfig(({ mode, command }) => {
] ]
} }
}, },
esbuild: {
legalComments: 'none'
},
build: {
sourcemap: false,
// 控制台 gzip 体积统计会再压缩一遍全部产物,大项目很吃内存
reportCompressedSize: false,
chunkSizeWarningLimit: 2000,
// esbuild 比 terser 省内存、更快
minify: 'esbuild',
cssMinify: 'esbuild',
target: 'es2020',
// 小内存 / CI:串行读文件,显著降低 transform 峰值
rollupOptions: {
maxParallelFileOps: lowMem ? 1 : 2
}
},
// 预编译 // 预编译
optimizeDeps: { optimizeDeps: {
include: [ include: [
@@ -65,8 +85,7 @@ export default defineConfig(({ mode, command }) => {
'echarts', 'echarts',
'vue-i18n', 'vue-i18n',
'@vueup/vue-quill', '@vueup/vue-quill',
'image-conversion', 'image-conversion'
'element-plus/es/components/**/css'
] ]
} }
}; };
+5 -4
View File
@@ -1,20 +1,21 @@
import AutoImport from 'unplugin-auto-import/vite'; import AutoImport from 'unplugin-auto-import/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
export default (path: any) => { export default (path: any, isBuild = false) => {
return AutoImport({ return AutoImport({
// 自动导入 Vue 相关函数 // 自动导入 Vue 相关函数
imports: ['vue', 'vue-router', '@vueuse/core', 'pinia'], imports: ['vue', 'vue-router', '@vueuse/core', 'pinia'],
eslintrc: { eslintrc: {
enabled: true, // 生产构建不重写 eslint 全局声明,避免每次打包全量扫描
enabled: !isBuild,
filepath: './.eslintrc-auto-import.json', filepath: './.eslintrc-auto-import.json',
globalsPropValue: true globalsPropValue: true
}, },
resolvers: [ resolvers: [
// 自动导入 Element Plus 相关函数ElMessage, ElMessageBox... (带样式) // 自动导入 Element Plus 相关函数ElMessage, ElMessageBox... (带样式)
ElementPlusResolver() ElementPlusResolver({ importStyle: false })
], ],
vueTemplate: true, // 是否在 vue 模板中自动导入 vueTemplate: true, // 是否在 vue 模板中自动导入
dts: path.resolve(path.resolve(__dirname, '../../src'), 'types', 'auto-imports.d.ts') dts: isBuild ? false : path.resolve(path.resolve(__dirname, '../../src'), 'types', 'auto-imports.d.ts')
}); });
}; };
+5 -3
View File
@@ -2,16 +2,18 @@ import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import IconsResolver from 'unplugin-icons/resolver'; import IconsResolver from 'unplugin-icons/resolver';
export default (path: any) => { export default (path: any, isBuild = false) => {
return Components({ return Components({
resolvers: [ resolvers: [
// 自动导入 Element Plus 组件 // 自动导入 Element Plus 组件
ElementPlusResolver(), // 全局已引入 element-plus/dist/index.css,关闭按需样式,减少打包模块数
ElementPlusResolver({ importStyle: false }),
// 自动注册图标组件 // 自动注册图标组件
IconsResolver({ IconsResolver({
enabledCollections: ['ep'] enabledCollections: ['ep']
}) })
], ],
dts: path.resolve(path.resolve(__dirname, '../../src'), 'types', 'components.d.ts') // 生产构建不重写声明文件,避免把全部组件信息留在内存里
dts: isBuild ? false : path.resolve(path.resolve(__dirname, '../../src'), 'types', 'components.d.ts')
}); });
}; };
+8 -4
View File
@@ -1,16 +1,20 @@
import compression from 'vite-plugin-compression'; import compression from 'vite-plugin-compression';
export default (env: any) => { export default (env: any) => {
const { VITE_BUILD_COMPRESS } = env; // CI 可通过空值或空格关闭:VITE_BUILD_COMPRESS=
const raw = String(env?.VITE_BUILD_COMPRESS ?? '').trim();
const plugin: any[] = []; const plugin: any[] = [];
if (VITE_BUILD_COMPRESS) { if (raw) {
const compressList = VITE_BUILD_COMPRESS.split(','); const compressList = raw.split(',').map((s: string) => s.trim()).filter(Boolean);
if (compressList.includes('gzip')) { if (compressList.includes('gzip')) {
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件 // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
plugin.push( plugin.push(
compression({ compression({
ext: '.gz', ext: '.gz',
deleteOriginFile: false deleteOriginFile: false,
// 小于 10KB 的分包压缩收益很小,跳过可明显缩短收尾时间
threshold: 10 * 1024,
verbose: false
}) })
); );
} }
+3 -3
View File
@@ -1,8 +1,8 @@
import Icons from 'unplugin-icons/vite'; import Icons from 'unplugin-icons/vite';
export default () => { export default (isBuild = false) => {
return Icons({ return Icons({
// 自动安装图标库 // 构建时禁止临时 npm install,避免打包过程访问外网卡住
autoInstall: true autoInstall: !isBuild
}); });
}; };
+3 -5
View File
@@ -1,5 +1,4 @@
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue';
import vueDevTools from 'vite-plugin-vue-devtools'; // 临时禁用以解决localStorage兼容性问题
import createUnoCss from './unocss'; import createUnoCss from './unocss';
import createAutoImport from './auto-import'; import createAutoImport from './auto-import';
@@ -13,12 +12,11 @@ import path from 'path';
export default (viteEnv: any, isBuild = false): [] => { export default (viteEnv: any, isBuild = false): [] => {
const vitePlugins: any = []; const vitePlugins: any = [];
vitePlugins.push(vue()); vitePlugins.push(vue());
// vitePlugins.push(vueDevTools()); // 禁用以解决localStorage兼容性问题
vitePlugins.push(createUnoCss()); vitePlugins.push(createUnoCss());
vitePlugins.push(createAutoImport(path)); vitePlugins.push(createAutoImport(path, isBuild));
vitePlugins.push(createComponents(path)); vitePlugins.push(createComponents(path, isBuild));
vitePlugins.push(createCompression(viteEnv)); vitePlugins.push(createCompression(viteEnv));
vitePlugins.push(createIcons()); vitePlugins.push(createIcons(isBuild));
vitePlugins.push(createSvgIconsPlugin(path)); vitePlugins.push(createSvgIconsPlugin(path));
vitePlugins.push(createSetupExtend()); vitePlugins.push(createSetupExtend());
return vitePlugins; return vitePlugins;