Wujies 无界平台
打包与上架

打包应用

本文是 1.x 的归档文档(冻结于 1.x)。该分支只接收勘误修正, 内容基本不再变动;文中的源码路径、配置项与版本号均以 1.x 为准,不建议用于新项目。

新项目请查看 5.x 最新文档。已在 1.x 上运行的项目,本文仍是可靠依据。

这一页只讲打包这一段:在插件仓库根用 wujies/wujies-plugins/package.ps1 把一个应用打成一个可上传的 zip,以及它顺手产出的另外两样东西(暂存目录、.package-info.json)。

打包器只有这一个,对所有应用通用:它的事实来源是插件的 app.json(脚本里 $appCode = $manifest.code、$appVersion = $manifest.version、$backendRel = $manifest.assets.backend),没有一处写死应用名。想改「打什么、叫什么」改清单;想改「这一次怎么打」用参数。

签名算法、上传与发布、部署落位各有一页:密钥与签名、上架与发布、应用生命周期。这一页只保证「命令能照着敲、产物能照着核对」。


最短路径

# 工作目录 = 插件仓库根
cd wujies\wujies-plugins

powershell -NoProfile -ExecutionPolicy Bypass -File package.ps1 -Plugin wujies-gallery

产物是默认输出目录 <插件仓库的上一级>\.plugin-dist\<code>\<version>\ 下的三个东西。注意这个目录在插件仓库之外(本机是 C:\Users\TX\Desktop\ruoyi\wujies\.plugin-dist\gallery\1.0.5\):

产物名字怎么来用途
gallery-1.0.5.zip<清单 code>-<清单 version>.zip上传给平台的整包
gallery-1.0.5.package-info.json同上,后缀不同走控制台表单手工登记版本时对照的元信息(大小 / MD5 / SHA256 / 打包时间)
gallery-1.0.5/同上暂存目录:zip 就是从它打出来的,signature.txt 也在它里面
为什么默认落点在仓库外
  • 在仓库外 ⇒ 不会被任何仓库的 git 看到,也就不可能被误提交(.gitignore 之外的第二道保障);
  • 在上一级 ⇒ 不会被插件仓库自己的 clean / 归档动作顺手删掉(与 build-with-local-temp.ps1 把临时目录放到仓库上一级同一个判据);
  • 再按 <code>\<version> 分一层 ⇒ 一个仓库里有多个应用,每个版本一个独立目录,归档与对比都按目录操作。

历史目录 <插件仓库>/dist-packages/ 没有被删,只是不再往里写(里面可能还有你要用的旧包,清理是人的决定)。它仍在 .gitignore 里。

包名里的版本来自清单,不是 POM

gallery-1.0.5 里的 1.0.5 来自 wujies/wujies-plugins/wujies-gallery/app.json 的 version。插件 POM 的版本是另一码事,见后端 jar 的名字怎么来的。

-Plugin 实际上必须传:不传时脚本会自动选用插件仓库根下唯一含 app.json 的子目录(Resolve-PluginDir),而当前有 11 个(wujies-enterprise / exam / finance / gallery / hello / im / mall / member / pay / quote / social),自动选用这条路走不到,脚本会报错并列出候选。


开发包(-Dev)

日常联调、或者只想把包展开看看结构时,用开发包:

# 工作目录 = 插件仓库根
.\package.ps1 -Plugin wujies-demo -Dev

产物落在 .plugin-dist\<code>\dev\(而不是 <code>\<version>\):

.plugin-dist\demo\dev\
├── demo-1.0.0.zip
├── demo-1.0.0.package-info.json
└── demo-1.0.0\               ← 暂存目录,zip 的内容就是它
与正式包的区别正式包开发包(-Dev)
签名有 signature.txt没有
输出目录.plugin-dist\<code>\<version>\.plugin-dist\<code>\dev\
package-info.jsondev: falsedev: true、signed: false
用途上传、发布、上架、安装本机联调、展开核对、warn/off 部署安装

三条必须知道的约束:

  1. -Dev 与 -SigningKey 互斥。 传了 -Dev 又传私钥会直接报错;要签名就去掉 -Dev。
  2. enforce 模式下不能上传。 宿主当前 app-market.security.mode=enforce,未签名包会在 POST /app/package/parse 阶段被拒。开发包不是"绕过签名",只是"不签名"。
  3. 它仍然会完整执行构建与组装。 前端构建、后端 Maven、迁移脚本拷贝、打包一样不少, 只是少了签名这一步。
比开发包更快的联调方式

开发包仍然是 zip。改一行后端代码时,最快的路径是只 mvn package,然后在 「应用上架 → 插件开发」里热加载 target/*.jar —— 不必打 zip、不必上传、不必重启宿主。 见可视化开发台与快速开始。


发布一个版本(release.ps1)

上面那条命令只回答「把一个包打出来」。要回答「发布一个版本」—— 打完包还能在 git 上留下一个可回溯、可下载的版本点 —— 用同一个目录下的 release.ps1:

# 工作目录 = 插件仓库根
cd wujies\wujies-plugins

.\release.ps1 -Plugin wujies-hello              # 打包 → 打 tag → 推 tag
.\release.ps1 -Plugin wujies-hello -DryRun      # 干跑:跑完所有守卫 + 打印计划,什么都不做
.\release.ps1 -Plugin wujies-hello -NoPush      # 只在本机打 tag,确认后再自己推

两个脚本不是替换关系:

脚本回答的问题可重复?
package.ps1「把一个包打出来」可以,产物随时被覆盖,不留版本痕迹
release.ps1「发布一个版本」一次只发一个,发完不可变(tag 不能被覆盖)

版本号只有一个来源:源 app.json 的 version

⚠️ release.ps1 刻意没有 -Version 参数。

原因是一个真实踩过的坑:-Version 只改写包内那份 app.json、不动源文件(见下文「参数交互」第 2 条)。长期这么用就会出现「源 app.json 写 1.0.1,而实际已经发到 1.0.4」的漂移 —— 包可以重打,tag 一旦打错就指向了与内容不符的版本,事后没法补救。

所以发布流程唯一认源 app.json 的 version,要发新版本先改清单再提交:

# 1) 改 <插件>/app.json 的 version(包内 jar 名要跟着改,脚本会拦下不自洽的)
# 2) 提交(守卫要求工作区干净,所以这一步必须在发布之前做完)
# 3) 发布
.\release.ps1 -Plugin wujies-hello

tag 约定:<appCode>-v<version>

例:hello-v1.0.2、mall-v1.0.5、enterprise-v1.0.2(appCode 就是清单的 code)。

  • 一个仓库里有多个应用,tag 必须能区分应用 —— 前缀用 appCode,它本来就是全平台的应用身份(app_version.app_code、前端目录名、包名前缀都用它);
  • 中间那个 -v 是分隔符,不是装饰:应用目录名自带 -(wujies-hello),没有它就没法把「应用名」与「版本号」一刀切开,列版本、比大小、机器解析都要靠猜。

于是「按应用列出所有版本」是一条确定的命令:

git tag -l 'hello-v*'                          # hello 的全部已发布版本
git tag -l --sort=version:refname 'hello-v*'   # 按版本号排序
git show hello-v1.0.2                          # 这一版发的到底是什么

用附注 tag(git tag -a)而不是轻量 tag:说明里写了应用名 / 版本 / 包文件名 / 大小 / sha256 / md5 / 是否签名 / 分支 / 提交 / 构建时间,git show <tag> 就能核对整包有没有被换过。

前置守卫(每条都拦,失败就明确退出,绝不半途发布)

守卫不满足时
插件目录存在、app.json 能解析报错并列出试过的路径
assets.backend 与 version 自洽(包内 jar 名以 -<version>.jar 结尾)报错,并指出该改清单的哪一处
在 git 工作区、当前分支是 master(-Branch 可改)报错,确要在该分支发布就显式传
工作区干净(git status --porcelain 为空)报错并列出脏文件
git 身份(user.name / user.email)配全报错,附配置命令
该版本的 tag 还不存在报错,提示升版本号
版本号没有倒退(不比该应用已有最新 tag 更旧)报错 —— 这条专门拦「源 app.json 忘了改」的漂移;维护分支的补丁版用 -AllowOlderVersion 放行
签名私钥存在报错 + 给出 -Keygen 命令
为什么「工作区干净」是硬要求

tag 指向的是 HEAD 那个提交,而构建用的是工作区里的文件。工作区脏 ⇒ 打出来的包与 tag 指向的内容不一致,而这个不一致没有任何地方会告诉你。

私钥不存在时不做自动 keygen

package.ps1 会自动生成,release.ps1 刻意不:宿主是 security.mode=enforce,自动生成的新钥宿主并不认识,打出来的包在上传时照样被拒 —— 那是个「看着打包成功」的废包,比直接失败更浪费时间。要生成请显式 .\package.ps1 -Keygen,并同步公钥 + 重建宿主,见密钥与签名。

没有 remote 怎么办

不算失败(拆仓库初期很常见)。tag 照样建在本地,脚本打印:

未配置 remote ⇒ tag 已建在本地;配置 remote 后执行:
    git -C "<插件仓库>" remote add origin <仓库地址>
    git -C "<插件仓库>" push origin hello-v1.0.2

有 remote 时默认推 origin;只有一个 remote 就用它;有多个又没有 origin 会报错并列出候选,用 -Remote <名字> 指定。

release(带 zip 附件的下载页)怎么发

⚠️ 脚本刻意没有用 API 自动创建 release。 Gitee 与 GitHub 的 release API 形状差得多,本机又没有 token 可验,写出来就是没验证过的代码。脚本改为把确定的部分说死:

手工创建 release(可选,给"供下载"用)
  要传的文件  : <...>\.plugin-dist\hello\1.0.2\hello-1.0.2.zip
  入口        : https://gitee.com/impressivel/wujies-plugins/releases/new
  选 tag      : hello-v1.0.2

入口地址从 remote URL 推出来(gitee.com / github.com 两种都认),剩下「点发布」那一下交给人。想用 API 自动化就自己接,token 一律走环境变量占位符(GITEE_TOKEN / GITHUB_TOKEN),不要写进脚本或文档。

发布完还要做的最后一件事:传到市场

打包与 tag 都不动宿主磁盘,市场里也不会自动出现新版本。release.ps1 会把这几步打在最后:

步动作入口 / 接口权限
①上传市场入口「应用上架 → 应用管理 → 版本管理 → 安装包上传」,即 POST /app/package/parse(multipart file=,可选 appId)app:console:add
②保存版本POST /app/versionapp:console:add
③发布POST /app/version/{versionId}/publishapp:console:edit
④上架POST /app/console/{appId}/status?status=1app:console:edit
⑤部署 / 安装控制台「部署」或租户侧「安装」app:console:edit / app:install:add

①要传的就是 release.ps1 打出来的那个 zip,一个字节都别改:宿主 mode=enforce 会在这一步验签(未签名 / 验签失败直接拒)。上传接口会把解析出的 packageSha256 回给你,与脚本打印的 sha256 对一下就能确认没传错文件。详见上架与发布。

别用 -SkipBuild 发布:它会跳过构建、只重新组装现有产物,tag 说明里的 sha256 反映的是旧产物。


命令与参数

参数默认值说明
-Plugin不传:脚本所在目录含 app.json 就用它;否则插件仓库根下唯一的含清单子目录;再否则报错列候选插件目录。可传名字(相对插件仓库根,如 wujies-gallery)或相对/绝对路径
-FrontendProject<插件仓库>/frontend/<code>-app前端工程目录(源码)。相对基准是插件仓库的 frontend/(工程根可用 WUJIES_FRONTEND_DIR 改),不是 plus-ui
-FrontendDistapp-market-dist/remote-apps/<code>前端产物落点,相对基准是脚本探测到的宿主前端仓库目录(即 plus-ui/)。与 -FrontendProject 是两件事
-BackendJar扫 <插件>/target 下最新的 jar(排除 *sources* / *javadoc* / original-*)直接指定后端 jar,跳过 target 扫描。用于「jar 是别处构建的」或 target 下有多个 jar
-MigrationPath<插件>/migrations(存在才打)建表/改表 SQL 的位置,可以是目录也可以是单个 .sql。相对路径按「原样 → 相对插件目录 → 相对平台目录 → 相对仓库根」依次试;都找不到直接报错并列出试过的基准
-OutDir<插件仓库的上一级>\.plugin-dist\<code>\<version>(-Dev 时是 <code>\dev;可被 WUJIES_OUT_DIR / WUJIES_PLUGIN_DIST 覆盖)zip、.package-info.json 与暂存目录的输出目录。在插件仓库外
-Version清单的 version覆盖本次打包的版本号,只改包内那份清单,源头文件不动
-Dev关打开发包:不签名、输出到 .plugin-dist\<code>\dev。与 -SigningKey 互斥;enforce 模式下不能上传
-SigningKey不传时若探测到本机私钥则自动使用(见下)私钥路径(PKCS#8 PEM)。传了就在打 zip 之前给暂存目录签名,产出包根 signature.txt
-SigningPublicKey-SigningKey 同目录的 public.pem签名后自查用的公钥;同目录没有 public.pem 就跳过自查(只提示)
-HostYml<平台仓库>/wujies-admin/src/main/resources/application.yml宿主 yml 位置,供 -SyncPublicKey 与公钥比对使用
-Keygen关只生成密钥,不构建不打包。默认落点见下面的警告,建议总是显式带 -SigningKey <目录>\private.pem
-SyncPublicKey关把公钥写进宿主 yml 的 public-key: 块(写完提醒需重建宿主 + 重启)
-NoSign关显式不签名:给 mode=warn/off 的部署打测试包,或故意验证「未签名会被拒」。与 -SigningKey 互斥
-SkipFrontend关跳过前端构建与组装(纯后端应用用)
-SkipBuild关跳过构建,用现有产物重新组装(改清单、重打 zip 时用,几秒)
-MvnPath自动探测:PATH 上的 mvn → C:\Program Files\JetBrains\**\mvn.cmd显式指定 mvn 可执行文件
-JavaHome自动探测满足最低版本的 JDK显式指定 JDK。最低版本 $script:MinJavaRelease = 17,与两个仓库 pom 的 <java.version>17</java.version> 一致
三种调用写法
# ① 指定插件(推荐)
powershell -NoProfile -ExecutionPolicy Bypass -File package.ps1 -Plugin wujies-gallery

# ② 不传 -Plugin —— 插件仓库根下只有一个含 app.json 的插件时才可用
powershell -NoProfile -ExecutionPolicy Bypass -File package.ps1

# ③ 脚本被复制进插件目录时的写法
powershell -NoProfile -ExecutionPolicy Bypass -File wujies-gallery\package.ps1

脚本实际跑的四个步骤

1/4 构建前端远程产物
    工作目录 = <插件仓库>/frontend
    命令     = pnpm --filter <前端工程 package.json 的 name> run build
               (读不到 name 时用默认 filter @wujies-plugins/<code>-app)
    注入     = WUJIES_UI_DIR(宿主前端仓库,别名 @/utils 等指向它)
               WUJIES_FRONTEND_DIST(产物落点,即 -FrontendDist 的父目录)

2/4 构建后端插件 jar
    工作目录 = <插件仓库>(不是插件目录)
    命令     = mvn -B -pl <插件目录名> -am package -DskipTests

3/4 组装包目录(暂存目录 <OutDir>/<code>-<version>/)

    (签名插在这一步之后、第 4 步之前)

4/4 Compress-Archive 打 zip,算 MD5 / SHA256,写 <code>-<version>.package-info.json

两个刻意的做法,改脚本时别当成笔误:

  • 前端走 pnpm --filter,不直接 node .../vite.js:wujies-plugins/pnpm-workspace.yaml 把 frontend/*-app 注册成工作区包,vite 被提升到仓库根的 node_modules,写死相对路径会失效。前端工程的 package.json 里 name 形如 @wujies-plugins/gallery-app。
  • 后端走 mvn -pl <模块名> -am,不用 mvn -f <插件>/pom.xml:插件之间会互相依赖(mall 依赖 member / pay / im,全是 provided),单模块构建时这些兄弟构件只能去远程仓库找,本地没装就直接失败。-am 让 Maven 在反应堆内把上游一起建掉。

打包前脚本还会做几项自检,-SkipBuild / -SkipFrontend 时不该拦人的检查会跳过:

自检触发条件报错信息要你做什么
pnpm 在不在真要构建前端时corepack enable pnpm 或 npm i -g pnpm
依赖装过没有同上node_modules/vite/bin/vite.js 不存在(仓库挪过目录会让 pnpm 的绝对路径软链失效)→ 回 frontend/ 重跑 pnpm install(非交互环境要先设 $env:CI = 'true')
前端工程配置在不在同上找不到 vite.config.ts → 用 -FrontendProject 或加 -SkipFrontend
JDK / mvn 能不能解析清单声明了 assets.backend 时(-SkipBuild 也照样解析)见 -JavaHome / -MvnPath
签名工具类在不在要签名时平台没构建过 → mvn -pl wujies-admin -am -B install -DskipTests

产物落在哪、长什么样

三样产物的落点

输出目录的决定顺序(参数永远最优先):

优先级来源含义
1-OutDir <路径>精确的输出目录
2$env:WUJIES_OUT_DIR精确的输出目录(历史变量,原样保留)
3$env:WUJIES_PLUGIN_DIST产物根 —— 脚本还会再补 <code>\<version>(-Dev 时补 <code>\dev;CI 建议用这个)
4默认<插件仓库的上一级>\.plugin-dist

⚠️ 第 2 与第 3 条语义不同:WUJIES_OUT_DIR 就是最终目录;WUJIES_PLUGIN_DIST 是根、还要补两层。

落点谁决定说明
<插件仓库的上一级>\.plugin-dist\<code>\<version>\(-Dev 时 <code>\dev\)上面的优先级表在插件仓库外,所以 git 看不到它;历史目录 <插件仓库>/dist-packages/ 已不再写入,但仍在 .gitignore 里
<输出目录>\<code>-<version>/暂存目录,同名 zip 的来源每次重打同一版本会先整个删掉再重建;打完不会自动清理,所以可以展开核对,PackageSignerMain sign 也是对它操作
<输出目录>\<code>-<version>.zipCompress-Archive -Path "<暂存目录>\*"包根就是包内容;同名 zip 会先删再生成
gallery-1.0.5/
├── app.json                                     清单
├── signature.txt                                分离式签名(-SigningKey / 自动发现私钥时才有)
├── backend/wujies-gallery-plugin-1.0.5.jar      文件名必须等于清单的 assets.backend
├── frontend/index.js                            远程 ESM 入口(`*.map` 已被剔除)
│   └── assets/...
└── migrations/                                  只收顶层 `*.sql`,没有就不建这个目录
    ├── 01-create-gallery-tables.sql
    ├── 02-create-gallery-website-config.sql
    └── 03-create-gallery-seed-data.sql

包根只允许 app.json、signature.txt 与 backend/、frontend/、migrations/ 三个目录,多一个文件就会被安全检查拒掉 —— 白名单与拒绝清单见安全校验模式。

元信息文件

<输出目录>\<code>-<version>.package-info.json 的字段与用途(下面是已打出的一份真实内容):

{
  "appCode": "gallery",
  "appName": "图床管理",
  "version": "1.0.5",
  "fileName": "gallery-1.0.5.zip",
  "packageSize": 206011,
  "packageMd5": "93e2f4d08074b8a5e882384240e823d0",
  "packageSha256": "0263a55a63e48150fbfe2e3d90cc874228b6c7822a55a943f6da2b2ae9290993",
  "builtAt": "2026-10-01 09:50:59"
}
字段说明
appCode / appName / version来自(可能被 -Version 覆盖过的)包内清单
fileName / packageSizezip 文件名与字节数
packageMd5 / packageSha256小写十六进制的整包摘要。部署时会逐项校验,不匹配直接拒绝
builtAt打包时间(yyyy-MM-dd HH:mm:ss)

走上传接口(POST /app/package/parse)时这几个摘要由后端算好返回,不用手填;只有走控制台表单手工登记版本时才需要对照这个文件。接口细节见上架与发布。

想自己再核一遍 zip 结构:

Add-Type -AssemblyName System.IO.Compression.FileSystem
$z = [System.IO.Compression.ZipFile]::OpenRead("..\.plugin-dist\gallery\1.0.5\gallery-1.0.5.zip")
$z.Entries | Select-Object FullName, Length
$z.Dispose()

后端 jar 的名字怎么来的

这里有两个版本号,最容易搞混:

名字出处例子
Maven 构建产物<插件>/target/<插件 artifactId>-<插件仓库父 POM 版本>.jar。父 POM 是 wujies-plugins/pom.xml 里的 wujies-plugins-parent,<version>1.0.0</version>wujies-gallery/target/wujies-gallery-plugin-1.0.0.jar
包内名字backend/<artifactId>-<清单 version>.jarbackend/wujies-gallery-plugin-1.0.5.jar

也就是说:Maven 打出来的 jar 名与应用版本无关,包内名字由打包器改成「应用版本」——这样 assets.backend 的声明才自洽。平台根 pom 的 <revision>(5.6.2)只用于 cn.wujies:* 依赖坐标(wujies-plugins/pom.xml 的 <platform.version>),不参与这两个文件名。

配套两条:

  • 清单里的 assets.backend 必须与包内那份一致。-Version 时脚本会同步改写包内清单的 version 与 assets.backend,并打印「包内清单已改写: version=…, assets.backend=…」。
  • 没传 -Version 但两者不一致时只 Write-Warning(不是错误):assets.backend 声明的文件名是 X,但按插件 artifactId 与应用版本推导应为 Y。这类错几乎总是「改了 version 忘了改 assets.backend」,会在很远的地方(部署时报「找不到后端产物」)才炸出来,所以在打包时就说清。

参数交互(会踩的几种组合)

1. 签名:-SigningKey / 自动发现 / 自动 keygen / -NoSign

宿主当前是 app-market.security.mode=enforce(见 wujies-platform/wujies-admin/src/main/resources/application.yml),不签名的包在上传时就会被拒。所以脚本的默认行为是:

情形行为
传了 -SigningKey 且文件存在用它签名 → 立刻用公钥 verify 自查 → 顺手比对宿主 yml 里的 public-key
没传 -SigningKey按顺序找本机私钥:$env:WUJIES_SIGNING_KEY → <插件仓库>/keys/private.pem → <平台仓库>/plugins/keys/private.pem → <平台仓库>/keys/private.pem(Find-SigningKey)。找到就用,并打印一行提示
都没找到不会自动生成新钥(新钥和宿主配的公钥对不上,生成了也只会得到一堆被拒的包),只打 Write-Warning 说清「上传/部署会被拒绝」,并列出找过的位置
传了 -SigningKey 但文件不存在、且路径以 private.pem 结尾自动 keygen 到该目录(目录一起建),接着签名 —— 「第一次用」的最短路径
传了路径但不以 private.pem 结尾报错并提示改法(keygen 固定产出 private.pem / public.pem)
-NoSign清空 $SigningKey,且自动发现那段显式排除 -NoSign。与 -SigningKey 同传直接报错
-Keygen 不传 -SigningKey 时的默认落点是平台仓库

package.ps1 的 -Keygen 分支里,默认值是 <平台仓库>\plugins\keys\private.pem;而平台仓库下当前没有 plugins/ 目录,这会新建一个。插件仓库的 keys/(已在 .gitignore)才是私钥的惯用位置,所以请总是写全:

powershell -NoProfile -ExecutionPolicy Bypass -File package.ps1 -Keygen -SigningKey keys\private.pem

-Keygen 遇到目录里已有 private.pem 会直接拒绝(换钥让旧钥签过的包全部作废,必须是显式操作)。密钥算法、-SyncPublicKey、换钥三步见密钥与签名。

2. -Version 只改包内清单

-Version 1.0.5 改写的是包内那份 app.json(并同步改写 assets.backend、把包内 jar 改名),源 app.json 保持不动 —— 打完包仓库是干净的。要让它成为长期版本,自己改清单的 version。

两个约束:

  • 格式校验是 ^[0-9]+(\.[0-9]+){0,3}([-+][0-9A-Za-z.-]+)?$,不合法直接报错;
  • 后端清单校验比打包脚本严(要求 x.y.z),见上架与发布。打包能过不等于上传能过。

覆盖版本号时脚本用 .NET 写文件而不是 Set-Content,因为必须不带 BOM(后端用 Jackson 解析包内清单,开头的 BOM 会让它直接报错;PowerShell 5.1 的 Set-Content -Encoding UTF8 会写 BOM)。不覆盖版本号时是原样 Copy-Item,保留作者写的格式与顺序。

3. -SkipBuild / -SkipFrontend / 纯前端应用

形态怎么打注意
前后端都有直接打最常用
只改清单/重打 zip-SkipBuild跳过两条构建命令,但仍会解析 JAVA_HOME 与 mvn(这两个探测在构建分支之外),前端产物也必须已经存在,否则报「找不到前端产物: …\index.js —— 请先不带 -SkipBuild 跑一次」
纯后端-SkipFrontend清单没写 assets.frontend 却不加这个参数会报「纯后端插件请显式传 -SkipFrontend」
纯前端清单只写 assets.frontend合法形态(wujies-finance 就是):包内只有 app.json + frontend/,跳过 JDK / Maven 探测与后端构建;但签名仍要 JDK,脚本会在签名前按需补上 JAVA_HOME

清单里 assets.backend 与 assets.frontend 两样都没有才会报「包里没有任何产物」。

4. 打包器要能定位平台仓库和宿主前端仓库

脚本不靠「往上数几层」推路径,而是按候选探测(两个仓库可能不在同一棵目录树里):

要找什么探测顺序找不到时
平台仓库(用于宿主 yml、签名工具类)$env:WUJIES_PLATFORM_DIR → 脚本位于 <平台>/plugins/ 下(历史位置)→ 从插件仓库逐级向上、扫各祖先的直接子目录找 wujies-platform/pom.xml直接报错并给出三种改法。⚠️ 报错里第 3 条建议的 -NoSign 并不能绕过这一步:-HostYml 的解析在 -NoSign 处理之前,探测不到平台仓库时脚本在那之前就抛了
宿主前端仓库(plus-ui)$env:WUJIES_UI_DIR → 逐级向上、按 wujies-admin、plus-ui 两个名字找含 package.json 的目录(判据用 package.json,所以平台里的 Java 模块 wujies-platform/wujies-admin/ 会被自然排除)不是致命错误(-SkipFrontend 时不需要它),真要读前端产物时后续步骤报明确错误
前端产物落点与后端落位目录是两个键

-FrontendDist 的默认值落在宿主前端仓库(plus-ui)仓库根下的 app-market-dist/remote-apps/<code>。后端把包内 frontend/ 释放到哪,由 app-market.deploy.frontend-dir 决定(yml 里是 ../wujies-admin/app-market-dist/remote-apps)。两者必须指向宿主 dev server / 静态服务真正提供 /remote-apps/** 的那个物理目录,否则会出现「构建出来的产物装不上、部署下来的产物跑不起来」。这两条路径的关系见配置项索引与前端部署与 nginx。


迁移脚本的打包规则

情况行为
不传 -MigrationPath用 <插件>/migrations;目录不存在就静默跳过并打印「没有迁移脚本目录,跳过」
指向目录只拷该目录顶层的 *.sql(文件名排序即执行顺序,用 01- / 02- 前缀)
指向文件只拷这一个;不以 .sql 结尾直接报错
路径不存在直接报错,并列出试过哪几个基准目录(不会静默跳过)
显式指定的目录里没有 .sql告警,并且不留下空的 migrations/ 目录(免得后端以为有迁移可跑)
子目录里还有 .sql告警:迁移只扫顶层,这些脚本不会被打包、也不会被执行
子目录那条告警是刻意加的

漏打包的脚本不会让升级报错,表现是「升级成功了但表没变」—— 属于最难查的一类问题,所以脚本提前说。

建表脚本进包还有一个非打包层面的用途:它是「彻底删除」时 DROP 数据表的唯一依据(后端从脚本里解析 CREATE TABLE 的表名)。包里没有脚本 → 那个应用一张表都删不掉。见应用生命周期。

迁移脚本的命名约定与四种文件形态(建表 / 种子数据 / 字典 / 站点配置)见数据库迁移与种子数据与站点配置。


常见错误对照表

症状根因怎么核对
上传报「应用包内未找到 app.json」zip 里多套了一层 <code>-<version>/打 zip 必须用 "<暂存目录>\*" 而不是暂存目录本身。上传侧的 extractManifest 对多套一层有兼容,但产物白名单是按 zip 根目录判定的,多一层会被判成「不允许的顶层目录」
部署时报「找不到后端产物」assets.backend 与包内 jar 名不一致展开 zip 看 backend/ 下真实的文件名;打包时的 Write-Warning 就是这件事
菜单点开是「远程视图不可用」产物目录名不等于 appCode四处必须同名:wujies-plugins/frontend/<code>-app 的 vite.config.ts 的 outDir、-FrontendDist 默认值、后端落位目录、AppRuntimeController 拼出的入口 URL
安装成功但前端整棵树没落地清单把 assets.frontend 写成了裸文件名落位侧 AppPackageDeployService.dirPrefix() 按最后一个 / 取目录前缀,裸文件名得到空串,isNotBlank(frontendPrefix) 直接判 false。写 frontend/index.js(其余应用都是这种);⚠️ 历史样本:hello 与 demo-app 都曾写成 "frontend": "index.js",2026-10-03 已全部改正(wujies/wujies-plugins/wujies-hello/app.json 现为 frontend/index.js)—— 所以在此之前打出来的旧包若重新安装,仍然会重现这个症状
上传/部署被拒,说未签名宿主是 mode=enforce带 -SigningKey,或让脚本自动发现 <插件仓库>/keys/private.pem;mode 见安全校验模式
开发包上传被拒,说未签名用了 -Dev开发包本来就不签名;上传安装请去掉 -Dev,或改用 warn/off 的测试部署
打包时弹「找不到签名工具类」平台没构建过mvn -pl wujies-admin -am -B install -DskipTests;插件依赖的是 cn.wujies:*:<platform.version> 的已发布构件,平台必须先 install(见 wujies-plugins/pom.xml 的说明)
前端构建报 Cannot find module '...\vite\bin\vite.js'仓库挪过目录,pnpm 的软链指向旧路径回 frontend/ 重跑 pnpm install(非交互环境先设 $env:CI = 'true')
构建后端报 Could not find artifact cn.wujies.plugins:wujies-<x>-plugin用了单模块构建,兄弟插件构件不在反应堆里走仓库根、用 -pl <模块名> -am(脚本已经是这样,别改成 mvn -f <插件>/pom.xml)

相关阅读

目录