From 580f5bed16ae8e834bc8ec96d01c7dae9aac0059 Mon Sep 17 00:00:00 2001 From: LoveChengke Date: Tue, 6 Oct 2026 15:52:05 +0800 Subject: [PATCH] =?UTF-8?q?fix(dist):=20=E4=BA=A4=E5=8F=89=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=20Linux=20=E5=8C=85=E6=97=B6=20tar=20=E7=9A=84?= =?UTF-8?q?=E7=9B=98=E7=AC=A6=E5=86=92=E5=8F=B7=E5=9D=91=EF=BC=88=E8=BD=AC?= =?UTF-8?q?=E6=AD=A3=E6=96=9C=E6=9D=A0=20+=20--force-local=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 Windows 上跑 pnpm dist:server:linux 时,packLinuxTarball 传给 tar 的归档路径带盘符,GNU tar 会把 F: 当成主机名去解析远程归档,报 resolve failed 并以退出码 2 失败。现象很误导:包已经整套铺好,只差最后一步打 tar。反斜杠还会被当转义符。 改成路径统一转正斜杠并加 --force-local。该选项在 Linux 上无害(本地路径本来就该走本地分支),因此无条件加上、不做平台分支 —— Linux 上路径没有盘符,这个坑在 CI 与真机上永远复现不了,只在交叉构建时现形。已记入 AGENTS.md 第 5 节第 59 条与 README 的交付验收表。 --- AGENTS.md | 7 ++++++- README.md | 2 +- scripts/dist-server.mjs | 10 +++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 4f6ce20..b2f63dc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -779,7 +779,12 @@ database / update`。 schema engine,CLI 运行期按平台扫 `@prisma/engines/schema-engine-debian-openssl-3.0.x` 这类路径, 而交叉包里只有 `schema-engine-windows.exe`。交叉构建会写入 `.cross-built` 标记, `classhelper doctor` 据此提示(别改成静默),`upgrade` 替换程序文件时会按新包有无该标记来增删。 - 正式产物仍走 CI。 + 正式产物仍走 CI。**交叉构建时 `tar` 还有两个 Windows 专有坑**(`packLinuxTarball()`): + `-f F:\...tar.gz` 里的**盘符冒号**会被 GNU tar 当成 `主机:路径` 的远程归档写法,报 + `tar (child): Cannot connect to F: resolve failed`、退出码 2 —— 现象很误导:包已经整套铺好、 + 就差最后打 tar 那一步失败。反斜杠还会被当成转义符。所以那一步把路径统一转成**正斜杠** + 并加 `--force-local`。该选项在 Linux 上无害(本地路径本来就该走本地分支),因此无条件加上、 + 不做平台分支 —— **Linux 上路径没有盘符,这个坑在 CI 与真机上永远复现不了**,只在交叉构建时现形。 ⑤ **密码永远走 stdin,命令行一律不接受密码;调用服务端模块的工具必须压掉它的 stdout 噪声。** `classhelper password` 用 `read -s` 优先读 `/dev/tty`、无 tty 回退 stdin(这样既能交互, diff --git a/README.md b/README.md index 2e77e77..deff4c0 100644 --- a/README.md +++ b/README.md @@ -1251,7 +1251,7 @@ pnpm db:generate && pnpm --filter @classhelper/server db:deploy && pnpm db:seed | 安装程序完整生命周期(静默安装 → 启动 → 卸载) | 通过 ✅(安装到 `%LOCALAPPDATA%\Programs`,卸载干净) | | 便捷版(单文件)解包启动 | 通过 ✅(wrapper + 多个进程,主窗口正常) | | Docker / Nginx 部署样例 | 文件已提供;本机无 Docker 未实测 ⚠️ | -| Linux 一键安装 + `classhelper` 运维命令(`deploy/install.sh` / `deploy/classhelper` / `pnpm dist:server:linux`) | 脚本与文档已交付,本地静态检查通过(`bash -n`、ESLint 0 error);**Linux 实机验收待做** ⚠️(清单见 [linux-deploy.md](docs/linux-deploy.md) §10,`deploy/verify-linux.sh` 可自动跑) | +| Linux 一键安装 + `classhelper` 运维命令(`deploy/install.sh` / `deploy/classhelper` / `pnpm dist:server:linux`) | 脚本与文档已交付,本地静态检查通过(`bash -n`、ESLint 0 error);**Linux 实机验收待做** ⚠️(清单见 [linux-deploy.md](docs/linux-deploy.md) §10,`deploy/verify-linux.sh` 可自动跑)。2026-10-06 用交叉构建出了 1.1.2 的包并核对过内容(sha256 自洽、顶层目录与 `--strip-components=1` 对得上、`.cross-built` 标记在位、包内是新图标),但**没在 Linux 上跑过** —— 正式发布仍走 CI | ### 6.3 已知的时序 / 环境相关用例 diff --git a/scripts/dist-server.mjs b/scripts/dist-server.mjs index 4515843..eaf2236 100644 --- a/scripts/dist-server.mjs +++ b/scripts/dist-server.mjs @@ -787,7 +787,15 @@ function packLinuxTarball() { fs.rmSync(LINUX_TARBALL, { force: true }); const topDir = path.basename(staging); // tar 里套一层目录,安装器用 --strip-components=1 展开 log(`打包 ${path.relative(root, LINUX_TARBALL)} ...`); - run('tar', ['-czf', LINUX_TARBALL, '-C', path.dirname(staging), topDir]); + // GNU tar 在 Windows 路径上有两个坑(Linux 上路径没有盘符、没有反斜杠,永远踩不到, + // 只有交叉构建会碰到): + // ① `-f F:\...tar.gz` 里的**盘符冒号**会被当成 `主机:路径` 的远程归档写法, + // 报 `tar (child): Cannot connect to F: resolve failed` 并以退出码 2 失败; + // ② 反斜杠会被当成转义符。 + // 因此统一转成正斜杠,并加 `--force-local` 明确"这是本地文件"。 + // 该选项在 Linux 上无害(本地路径本来就该走本地分支),所以无条件加上,不做平台分支。 + const toPosix = (target) => target.replace(/\\/g, '/'); + run('tar', ['--force-local', '-czf', toPosix(LINUX_TARBALL), '-C', toPosix(path.dirname(staging)), topDir]); const digest = crypto.createHash('sha256').update(fs.readFileSync(LINUX_TARBALL)).digest('hex'); const shaFile = `${LINUX_TARBALL}.sha256`;