# LocalBuild **Repository Path**: isslin/LocalBuild ## Basic Information - **Project Name**: LocalBuild - **Description**: 使用本地环境构建项目(支持java、vue);发布到linux服务器,推荐测试服务器玩;项目全程由【AI】完成功能流程,测试验收; - **Primary Language**: NodeJS - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2026-08-26 - **Last Updated**: 2026-09-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README > ⚠️ **注意** > > 注意哈,本项目全程AI编写,AI测试 # LocalBuild > 本地项目打包 → SSH 上传 → 远程命令,一条流水线跑完。 界面用 Vue 3 + Vite + Element Plus,后端 Node.js 负责跑本地 `mvn`/`npm` 和 SSH(浏览器做不了这两件事)。 配置和历史记录存 SQLite(Node 22.5+ 内置 `node:sqlite`,**无需任何原生编译**)。 --- ## 一、环境要求 | 依赖 | 版本 | 用途 | 检查命令 | |---|---|---|---| | Node.js | **≥ 22.5**(推荐 24) | 跑后端和前端构建 | `node -v` | | npm | ≥ 9 | 装依赖 | `npm -v` | | JDK | 8 / 11 / 17 / 21+ | 被 Maven 调用来编译 | `java -version` | | Maven | 3.6+ | 打包(项目自带 `mvnw` 时可不装) | `mvn -v` | > **为什么 Node 要 22.5 以上**:数据存储用的是 Node 内置的 `node:sqlite`,这个模块从 22.5 才有。 > 好处是不需要 `better-sqlite3` 那种原生编译(Windows 上要装 Visual Studio 才编得过)。 > Node 24.14.0 内置的是 SQLite 3.51.2;想用更新的 SQLite,直接升 Node 即可。 > 跑 `npm run doctor` 可以查看当前版本,并逐条验证本项目用到的 SQLite 能力。 一条命令全部检查: ```bash node -v && npm -v && java -version && mvn -v ``` JDK / Maven 没装或不在 PATH 里也能启动,只是打包会失败 —— 启动后首页「本机环境」会直接标红告诉你缺什么。 --- ## 二、安装项目 下载项目文件 ```bash # 1. 进入项目目录 cd <项目所在目录> # 2. 安装依赖(根目录一次装完 server 和 web 两个 workspace) npm install ``` 装完 `node_modules` 约 130 MB(大头是 Element Plus 和 Vite)。**全程不需要编译原生模块**,不会出现 node-gyp 报错。 看到类似输出就算成功: ``` added 177 packages, and audited 180 packages in 42s ``` > 如果 `npm install` 很慢,可以换国内镜像: > `npm install --registry=https://registry.npmmirror.com` --- ## 三、启动项目 三种模式,按需要选一种。 ### 模式 A:开发模式(改代码即时生效) ```bash npm run dev ``` 它会同时拉起两个进程: | 进程 | 地址 | 说明 | |---|---|---| | 后端 | http://127.0.0.1:7300 | `node --watch`,改后端代码自动重启 | | 前端 | http://127.0.0.1:7301 | Vite 开发服务器,热更新;已配好 `/api` 和 `/ws` 代理 | **浏览器会自动打开 http://127.0.0.1:7301** —— 开发时请访问这个端口(7301),不要访问 7300。 终端会看到后端启动横幅: ``` LocalBuild 服务已启动 API http://127.0.0.1:7300/api WS ws://127.0.0.1:7300/ws 数据目录 D:\cloud_code\this_build\data(默认位置) 界面 开发模式请访问 Vite 端口(默认 7301) ``` 只想单独起一个: ```bash npm run dev:server # 只起后端 npm run dev:web # 只起前端 ``` ### 模式 B:生产模式(日常使用推荐) ```bash npm run serve ``` 等价于 `npm run build && npm start`:先把前端打包成静态文件(`web/dist`),再由后端一起托管。 **只有一个端口,访问 http://127.0.0.1:7300 即可。** 前端已经构建过、只想重启后端: ```bash npm start ``` ### 模式 C:双击启动(Windows,日常最省事) 不想敲命令:双击项目根目录的 **`启动.bat`** 即可。它会自动检查 Node.js、 首次运行时安装依赖并构建前端、启动服务并打开浏览器;如果服务已经在运行, 就只打开浏览器页面,不会重复启动。 想从桌面启动:双击一次 **`创建桌面快捷方式.bat`**,桌面会生成「LocalBuild」快捷方式。 ### 停止服务 终端里按 `Ctrl + C`。后端会等连接关闭后退出(最多 3 秒),正在跑的打包子进程不会被自动杀掉。 --- ## 四、验证启动成功 ```bash curl http://127.0.0.1:7300/api/health # {"ok":true,"uptime":3.1,"dataDir":"D:\\cloud_code\\this_build\\data"} ``` 浏览器打开界面后,看首页「本机环境」那张表:Java 和 Maven 都显示出版本号就一切正常; 显示红色「未检测到」说明 PATH 里找不到,打包会失败(可以在项目里单独指定 JAVA_HOME,或把「打包命令」写成完整命令行绕过)。 --- ## 五、配置端口与数据目录 全部通过环境变量控制,没有配置文件要改: | 变量 | 默认值 | 说明 | |---|---|---| | `PORT` 或 `JDS_PORT` | `7300` | 后端端口 | | `JDS_HOST` | `127.0.0.1` | 监听地址(见下方安全说明,别随便改) | | `JDS_DATA_DIR` | **项目目录下的 `data/`** | SQLite 库 + 加密密钥存放位置 | | `JDS_NO_MIGRATE` | 未设置 | 设为 `1` 跳过旧数据目录的自动迁移 | | `JDS_MAX_LOG_LINES` | `20000` | 单次打包日志落库上限,超出只实时显示不入库 | | `JDS_CMD_TIMEOUT` | `300` | 远端命令默认超时(秒) | | `JDS_LOG_ENCODING` | Windows 为 `gbk` | 子进程输出解码兜底编码 | 不同终端设置环境变量的写法不一样(Windows 用户注意): ```bash # Git Bash / macOS / Linux JDS_DATA_DIR=D:/my-data PORT=7400 npm start # PowerShell $env:JDS_DATA_DIR="D:/my-data"; $env:PORT="7400"; npm start # CMD set JDS_DATA_DIR=D:/my-data && set PORT=7400 && npm start ``` **数据存在哪**:默认就在项目目录下的 `data/`,里面只有两样东西 —— `studio.db`(所有配置和记录)和 `secret.key`(加密 SSH 密码用的密钥)。 启动横幅和**设置页顶部的「数据存储位置」卡片**都会显示当前实际用的目录。 **备份 / 换机器**:把整个 `data/` 目录拷走即可, 但 `studio.db` 和 `secret.key` **必须成对**,只拷数据库的话 SSH 密码解不开。 > 1.0 早期版本默认存在用户目录 `~/.java-deploy-studio`。现在默认改到项目下, > 首次启动时如果新位置还是空的、旧位置有数据,会**自动复制过来**(原目录保留不删), > 启动日志和设置页都会给出提示。用 `JDS_DATA_DIR` 显式指定目录时不会触发迁移。 > > 排查配置"消失":`node scripts/where-is-my-data.js` 会只读扫描所有可能的数据目录, > 列出每个库里的项目 / SSH 服务器 / 命令,并告诉你该用哪个目录启动。 --- ## 六、启动常见问题 **`端口 7300 已被占用`** 后端启动时会直接报这个并退出。换端口即可:`PORT=7400 npm start`。 想知道谁占了:`netstat -ano | findstr :7300`,再 `taskkill /PID /F`。 **前端 7301 端口也被占用** Vite 配的是 `strictPort: false`,会自动换到 7302、7303…… 看终端里 Vite 打印的实际地址。 **页面打开是空白 / 接口 404** 开发模式请访问 **7301**(Vite),不是 7300。访问 7300 会看到一段提示文字,告诉你前端还没构建。 生产模式则要先 `npm run build`,否则后端找不到 `web/dist`。 **界面右上角小圆点是红色** 那是 WebSocket 状态指示灯,红色表示实时通道断了(一般是后端重启了),会自动重连,变绿即恢复。 实时日志依赖它,红色时打包日志不会实时刷新。 **打包一直失败,日志里 `找不到可执行文件 mvn`** PATH 里没有 mvn。要么装好 Maven,要么在「项目管理」里把「打包命令」填成 mvn 的绝对路径加参数(如 `C:\tools\maven\bin\mvn.cmd clean package`), 或者用项目自带的 `mvnw`(打包命令留空时自动优先使用 wrapper)。 **`ExperimentalWarning: SQLite is an experimental feature`** Node 对 `node:sqlite` 的标注,可以忽略,不影响使用。 --- ## 七、五分钟上手 启动好之后,按这个顺序配置: 1. **SSH 服务器**:填主机 / 用户名 / 密码或私钥 → 点「测试连接」,通了会显示远端 whoami、内核版本、java 版本。 按需配置「授权目录」,之后的文件管理和上传都会被限制在授权目录内。 2. **项目管理**:新增项目,用「浏览」挑本地项目根目录(能识别 `pom.xml` / `package.json`),按需填「打包命令」完整命令行 (留空按项目类型自动判断:Maven 项目有 `mvnw` 优先用 wrapper,否则用 PATH 里的 `mvn`)。 3. **一键部署**:选项目 + 服务器 → 第 1 步勾选要上传的产物(同时决定打包哪些模块)→ 按需调整打包命令、 远端目录、上传前/后命令 → 「开始执行」。右侧实时看到时间线推进和完整日志。 --- ## 功能对照 | README 需求 | 实现位置 | |---|---| | 集成 SSH 管理,连接配置 | 「SSH 服务器」页:CRUD + 连接测试 + 授权目录管控 + 文件管理(浏览/新建/删除/下载/手动上传);密码 AES-256-GCM 加密存储 | | 项目管理(选本地路径,在此目录打包) | 「项目管理」页:目录选择器、完整打包命令行、JAVA_HOME、默认服务器与远端目录;列表带**项目类型**标签(Java / Vue / React…,前端框架从 `package.json` 依赖认出来) | | SSH 上传前/后执行命令的管理 | 「命令管理」页:按阶段(上传前/上传后/手动)维护脚本,可限定项目/服务器,支持超时与「失败即停」 | | 用本地环境的 java、mvn 打包 | 打包命令是一条完整命令行,留空按项目类型自动判断(`mvnw`/`gradlew` wrapper 优先,其次 PATH);可按项目覆盖 JAVA_HOME | | 界面显示打包的实时信息、日志 | WebSocket 推送每一行输出,日志面板支持过滤、只看警告/错误、自动滚动、复制、下载 | | 打包完成后自动扫描所有 jar,tree 结构 | 扫描各模块 `target/`(Gradle 看 `build/libs/`)和前端输出目录(`dist` 等),按目录组织成可勾选的树 | | 选择对应的 jar,上传到对应目录 | 打包详情页勾选产物 → 选服务器和远端目录 → 上传,可顺带执行上传后命令 | | 打包前就选好待上传的 jar(扫描项目结构得出) | 解析 `pom.xml` 模块树推算产物路径(支持 ``、父版本继承),标记为「待打包」供提前勾选;同模块的旧产物自动隐藏,不会新旧版本并排重复 | | 只打包选中的模块 | 勾选产物后自动反推所属 Maven 模块,打包命令追加 `-pl <模块> -am` 只构建这些模块(判断不了归属时自动回退全量),界面上能看到实际执行的完整命令 | | 打包记录 / 上传记录 / 命令执行记录 | 独立记录页,支持分页、按项目/服务器/状态过滤、关键字搜索、一键清空;另有「操作日志」页记录所有配置变更 | | 完成项目的重启 | 上传后命令阶段执行重启脚本,完整 stdout/stderr 落库可查 | 额外做的: - **部署方案**:常用配置存成方案,「一键部署」菜单进去默认就是方案列表,可新增 / 编辑 / 删除 / 一键运行 (也可以从部署记录里「重跑」某次历史部署)。 - **一键复制**:部署方案 / SSH 服务器 / 项目 / 命令都能一键生成副本,名称自动加「- 副本」后缀并自动编号; 服务器副本连同加密密码 / 私钥口令一起复制,开箱即用。 - **一个产物一个目标目录**:多个 jar 可以分别传到不同的远端目录,支持批量设目录和上传时改名; 每个产物的最终落地路径实时预览,部署预检会把所有用到的目录一次性检查完。 - **文件名子目录(默认开启)**:文件产物自动落到「目标目录/文件名(去扩展名)/」下面, 例如 `xxx.jar` → `目录/xxx/xxx.jar`,不同产物各归各的目录,互不覆盖;不需要可一键关掉。 - **本地环境管理**:JDK / Maven / Gradle / Node / npm / Git 每个都能配**多条路径** (比如 JDK 8 和 JDK 21 都加进来,点一下切换),界面上把**系统默认的绝对路径和版本一并显示** 出来做对照;用不上的工具(比如 Gradle)可以在顶部取消勾选不显示。 生效的那条会以 `JAVA_HOME`/`MAVEN_HOME` + PATH 前置的方式注入打包进程。 - **自定义项目标题**:左上角标题和浏览器标签页可以改成自己的叫法。 - **部署前预检**:连上目标机后先 `mkdir -p` 目标目录、检查可写与剩余空间,问题在传文件之前暴露。 - **部署输出不会丢**:部署是「点下去就开跑」的,浏览器订阅 WebSocket 总会晚一步 —— 所以部署输出和打包日志一样**落库**,界面先订阅、再用 `afterSeq` 把订阅之前那几十秒补齐, 按 `seq` 去重排序。打包阶段的 mvn 输出也一并转到部署面板里显示,部署记录里还能回看和下载。 打包命令在部署创建时就解析成完整命令行(含默认兜底和模块裁剪),记录里一开始就能看到。 - **别名上传 + 原子切换(默认开启)**:先把产物传成 `demo-api-260101222359.jar` 这样带日期时间的别名, 传完再由**内置命令**判断正式名是否已存在、存在就把旧的改名,最后把别名改成正式名。 整个传输过程里服务读到的一直是旧文件,不会出现「读到半个 jar」。格式固定为「原名-到秒的时间」。 - **覆盖前备份**:远端已有同名文件时,别名模式下把旧文件**改名**成 `xxx.jar.bak.20260825084449` (本地时间)留着,省一次全量拷贝;关掉别名上传时退回 `cp -p` 复制。 - **失败即停**:任一步失败则中止,后续步骤标记为「已跳过」,时间线上一眼看出卡在哪。 - **取消正在执行的任务**:打包杀进程树,部署在当前步骤结束后停止。 - **中文日志不乱码**:Windows 控制台输出 GBK 时自动回退解码,并强制 JVM 用 UTF-8 输出。 - **操作日志**:项目 / 服务器 / 命令 / 部署方案的增删改,打包 / 部署 / 上传的发起取消清空, 全部落库(含接口耗时和成功/失败结果),「操作日志」页可查可清空。 --- ## 界面 ``` 执行 ── 总览 统计、最近动态(打包/部署混排)、打包趋势、本机环境自检、本机 CPU/内存 └─ 一键部署 ★ 核心页:默认展示已保存的部署方案(可新增/编辑/删除/复制/一键运行); 进入编辑态后按业务流程 6 步配置:选产物 → 打包 → 部署 → 上传前 → 上传 → 上传后, 右侧实时时间线与日志 配置 ── 项目管理 本地项目(Java / 前端) ├─ SSH 服务器 目标机器 + 授权目录;详情进「文件管理」:浏览/新建/删除/下载/手动上传 └─ 命令管理 可复用脚本 记录 ── 打包记录 → 详情页:实时日志 + 产物树 + 直接上传 ├─ 部署记录 → 详情页:时间线 + 完整输出(含打包日志,可下载)+ 上传明细 + 命令明细 + 配置快照 ├─ 上传记录 文件、远端路径、别名、大小、速度、备份路径、来源(手动上传/一键部署) ├─ 命令执行记录 完整 stdout / stderr / exit code └─ 操作日志 配置变更与任务操作,含耗时和结果 ↑ 记录页都有「清空全部」,正在执行的任务会跳过 系统 ── 本地环境 每个工具可配多条路径并切换,可隐藏用不上的工具 └─ 设置 项目标题、数据存储位置、历史记录清理(按天保留) ``` 左上角的项目标题可以改:**设置 → 界面 → 项目标题**,填「订单系统发布台」之类, Logo 和浏览器标签页会同时生效(留空恢复默认)。 每个版本的详细变更见 [CHANGELOG.md](CHANGELOG.md)。 --- ## 部署流水线的执行顺序 一条业务流程,配好一次走完: ``` ① 打包 (可关闭)在项目目录跑 mvn/gradle/npm,实时推日志,成功后扫描产物; 勾选了产物时自动模块裁剪(-pl 选中模块 -am),判断不了归属回退全量 ↓ ② 连接 SSH 连上目标机,整个部署全程复用这一条连接 ↓ ③ 远端预检 mkdir -p 目标目录、检查可写、看剩余空间 ↓ ④ 上传前命令 按顺序执行,失败可中止(一般用来停服务、备份) ↓ ⑤ 上传产物 逐个 SFTP 上传,带实时进度;可选覆盖前备份、可选别名上传 + 原子切换、 可选文件名子目录 ↓ ⑥ 上传后命令 一般在这里重启服务 ↓ ⑦ 完成 汇总本次上传了几个产物、执行了几条命令、总耗时 ``` 每一步在时间线上都是独立节点,状态、耗时、输出全部落库;某一步失败时, 它之后的步骤记为「已跳过」,一眼看出流程停在哪。同一阶段有多条时标题会带 `2/3` 这样的序号(上传前命令、上传产物、上传后命令),日志里也带 `[上传前 2/3]` 前缀。 「连接 SSH」和「远端预检」单独成步的意义:连不上服务器、目标目录不存在又建不出来 这类问题,会在**任何文件被上传之前**就失败退出,而不是传到一半才报错; 两步分开还能一眼分清是「连不上机器」还是「机器连上了但目录不能用」。 只打包不上传的任务没有这两步(也就不会去连服务器),「完成」那步会直接点亮。 ### 别名上传 + 原子切换(默认开启) 第 ⑤ 步(上传产物)默认就是这么传的(「一键部署 → 2. 部署」里可以关掉): ``` 1) 传成别名 /opt/app/demo-api-260101222359.jar ← 传输期间正式名一动不动 2) 内置命令 判断 /opt/app/demo-api.jar 是否存在 3) 存在 → 把旧的改名成 demo-api.jar.bak.20260101222359 4) 换上去 mv demo-api-260101222359.jar → demo-api.jar ``` 别名格式是**固定**的:`原名-yyMMddHHmmss` + 原扩展名(`demo-api.jar` → `demo-api-260101222359.jar`, 目录产物 `dist` → `dist-260101222359`)。别名只在「传完到切换」这几秒内存在,不需要起名字, 所以不做成可配置的。同一次部署的所有产物共用一个时间戳,一眼能看出是同一批传上去的。 第 2~4 步是本工具**内置的一条 sh 脚本**,在同一条 SSH 连接上一次跑完,不用自己去命令管理里配。 这么做解决三个实际问题: - **不会读到半个 jar**:`mv` 在同一个目录(同一文件系统)内是原子的, 服务任何时刻打开正式名拿到的都是完整文件;直接往正式名上写就没这个保证。 - **旧版本不是被覆盖,是被改名**:改名不占额外磁盘、不用等一次全量拷贝, 出事只要把 `.bak.时间戳` 改回来就回滚了。关掉「覆盖前备份」时也是先改名、 **切换成功后**才删旧的 —— 任何时刻磁盘上都有一份可用的。 - **权限不丢**:别名是新建文件,权限来自 umask;切换前会把旧文件的权限位 (`stat` 读出来)套到新文件上,755 的启动 jar 不会变成 644。 产物自己指定了 `mode` 时以指定的为准。 目录产物(前端 `dist`)同样支持:先传到同级的 `dist-260101222359/`, 再整体换名成 `dist/`(旧目录改名保留)。换上来的是全新目录, 所以不需要再勾「上传前清空」,也不会残留上个版本带 hash 的静态文件。 关掉别名上传就是老行为:直接往正式名上写,覆盖前按需 `cp -p` 备份一份。 失败时不留垃圾:传输中断或切换失败,别名文件/目录会被清掉,正式名保持原样; 切换到一半失败(旧的已改名、新的没换上)会自动把旧名字改回去。 接口上它就是部署配置里的一个字段(不传等于开启),`/api/deploys`、`/api/uploads` 都认: ```json { "alias": { "enabled": false }, "artifacts": [ { "relPath": "demo-api/target/demo-api.jar", "alias": false } ] } ``` 产物里单独写 `"alias": false` 就让这一个直接写正式名(不跟随全局开关)。 ### 产物传到哪个路径 文件和目录都是「放到目标目录下面」,和 `scp` 的直觉一致: | 产物 | 远端目录 | 目录名/文件名 | 最终路径 | |---|---|---|---| | `demo-api.jar` | `/opt/app` | 留空 | `/opt/app/demo-api.jar` | | `demo-api.jar` | `/opt/app` | `app.jar` | `/opt/app/app.jar` | | `dist/`(目录) | `/server/temp/ui` | 留空 | `/server/temp/ui/dist/` | | `dist/`(目录) | `/server/temp/ui` | `html` | `/server/temp/ui/html/` | 「文件名子目录」开启时,文件产物再往下套一层以文件名(去扩展名)命名的子目录: `demo-api.jar` + `/opt/app` → `/opt/app/demo-api/demo-api.jar`。 想让 `dist` 里的内容**直接铺在** `/server/temp/ui` 里(不套一层子目录), 就把目录填 `/server/temp`、目录名填 `ui`。 --- ## 接口速览 | 方法 | 路径 | 说明 | |---|---|---| | GET/POST/PUT/DELETE | `/api/servers` | SSH 服务器 CRUD | | POST | `/api/servers/:id/test` | 连接测试(`:id` 用 `draft` 可测未保存的表单) | | GET | `/api/servers/:id/browse?dir=` | 远端目录浏览(文件管理) | | GET/POST/PUT/DELETE | `/api/projects` | 项目 CRUD | | GET | `/api/projects/:id/scan` | 扫描模块 + 已有产物 + 推算产物 + 目录树 | | GET | `/api/projects/browse/local?dir=` | 本地目录浏览 | | GET/POST/PUT/DELETE | `/api/commands` | 命令 CRUD | | POST | `/api/commands/run` | 立即在某台机器上执行 | | GET | `/api/commands/runs/list` | 命令执行记录(分页/过滤) | | POST | `/api/builds` | 触发打包,立即返回 buildId | | GET | `/api/builds/:id` | 打包详情(含产物与目录树) | | GET | `/api/builds/:id/logs` | 历史日志(`afterSeq` 可断点续取) | | POST | `/api/builds/:id/cancel` | 取消打包 | | POST | `/api/deploys` | 启动部署(body 为完整流水线配置) | | GET | `/api/deploys/:id` | 部署详情(步骤/上传/命令/配置快照) | | GET | `/api/deploys/:id/logs` | 部署输出(`afterSeq` 补齐订阅之前的行);`logs.txt` 下载 | | POST | `/api/deploys/:id/rerun` | 用同样配置重跑 | | GET/POST | `/api/uploads` | 上传记录 / 直接上传已有产物 | | POST | `/api/builds/clear` \| `/api/deploys/clear` \| `/api/uploads/clear` \| `/api/commands/runs/clear` | 清空对应记录(跳过正在执行的) | | GET/POST/DELETE | `/api/pipelines` | 部署方案 | | GET | `/api/logs` | 操作日志(分页/过滤);`POST /api/logs/clear` 清空 | | GET | `/api/system/env` \| `/stats` | 环境自检 / 首页统计 | | GET | `/api/system/toolchain` | 各工具的系统默认、所有候选路径、当前生效项 | | POST/PUT/DELETE | `/api/system/toolchain/:tool/entries[/:id]` | 增删改候选路径(同一工具可配多条,如多版本 JDK) | | PUT | `/api/system/toolchain/:tool/active` | 切换生效项(`id: null` 表示用系统默认) | | PUT | `/api/system/toolchain/visible` | 设置界面上显示哪些工具(用不上的可隐藏) | | POST | `/api/system/toolchain/:tool/verify` | 验证候选路径能否执行并返回版本 | | GET/PUT | `/api/system/settings/:key` | 通用配置(如 `app_title` 项目标题) | WebSocket `/ws`,按频道订阅:`global`(列表刷新)、`build:`(打包日志)、 `deploy:`(部署时间线与输出)、`command:`(单条命令输出)。 --- ## 测试 仓库自带端到端集成测试(100 多项断言),会真实跑一遍完整流程: ```bash JDS_DATA_DIR=./data node server/src/index.js & # 先起后端 npm run test:e2e ``` SSH 部分不需要你准备服务器——测试脚本用 `ssh2` 在本机起一台真实的 SSH/SFTP 服务器 (命令交给本机 `sh` 执行,SFTP 落到临时目录),因此走的是和真机一致的协议路径。 覆盖内容:pom 产物推算、真实 mvn 打包、WebSocket 日志推送、SSH 连接测试、 完整部署(打包→连接→预检→前置命令→上传→后置命令→完成)、步骤时间线(骨架顺序、 多条时的序号、连接/预检/完成三步各自的 detail、阶段横幅在日志里有序出现)、 上传字节一致性校验、覆盖备份、 别名上传与原子切换(默认启用、别名落地、切换后无残留、旧文件改名保留、不备份时切换后才删、 正式名不存在的分支、可关闭)、目录产物(传到 `<远端目录>/dist`、整体换名、旧目录保留、无残留)、 失败中止与步骤跳过、手动命令执行、分页与关键字查询、方案保存与运行。 `sandbox/demo-project` 是测试用的 Maven 多模块示例(父 pom + 两个子模块, 其中一个用 `` 改了产物名,专门用来验证产物推算)。 --- ## 安全说明 这是给开发者本机用的工具,默认只监听 `127.0.0.1`,**没有登录鉴权**。 它能读本地任意目录、在远端执行任意命令 —— 不要把 `JDS_HOST` 改成 `0.0.0.0` 暴露到不可信网络。SSH 密码用 AES-256-GCM 加密存库,但密钥就在同一个数据目录里 (`secret.key`),能读到该目录的人就能解密:这是防止明文顺手泄露,不是抗本机攻击。 上传与命令执行带有危险命令拦截,文件管理被限制在各服务器的授权目录内(未配置则不限制)。