Files
crossdesk/apps/cli/docs/npm-distribution.md
T

199 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# npm 分发与平台兼容性
## 使用方式
完整版本发布后的统一入口如下。构建产物或部分平台包上传成功,不代表入口包和全部平台依赖已经可用;发布者须完成本文的注册表检查。
```sh
npx @chunkdash/crossdesk-cli@latest doctor --json
npx @chunkdash/crossdesk-cli@latest 123456789
npm install -g @chunkdash/crossdesk-cli
crossdesk-cli 123456789
```
需要 Node.js 22 或以上和 npm。启动器根据 Node 的 `process.platform` / `process.arch` 选择同版本的 `@chunkdash` 平台包,直接启动原生 C++ 程序,继承终端、工作目录、环境变量和命令参数。安装过程不下载外部可执行文件、不执行 postinstall、不在用户机器编译。不要使用 `--omit=optional`,否则对应平台包不会安装。
入口包名为 `@chunkdash/crossdesk-cli`,`bin` 声明的终端命令仍为 `crossdesk-cli`。如已全局安装旧的无 scope 入口,先执行 `npm uninstall -g crossdesk-cli`,再安装 scoped 入口,避免两个包争用同一个命令。原来的 `crossdesk-cli@0.1.0` 是独立的旧包,改名不会更新它或自动迁移已有安装。
平台包仅提供原生二进制,不声明 `bin` 命令。`npm install @chunkdash/crossdesk-cli-linux-x64` 不会让 shell 出现 `crossdesk-cli`;普通用户应安装上面的通用入口。入口包的本地安装将命令放在 `node_modules/.bin`,全局安装才会放入 npm 的全局命令目录。
## 平台与终端
| 平台 | 原生包 | 终端适配与运行边界 |
| --- | --- | --- |
| macOS Apple Silicon | `@chunkdash/crossdesk-cli-darwin-arm64` | POSIX TTY;具体最低系统版本写入包内 `build-info.json` |
| macOS Intel | `@chunkdash/crossdesk-cli-darwin-x64` | POSIX TTY;最低系统版本同上 |
| Linux x64 | `@chunkdash/crossdesk-cli-linux-x64` | glibc、libstdc++、zlib;所需 GLIBC / GLIBCXX 版本写入 `build-info.json` |
| Linux arm64 | `@chunkdash/crossdesk-cli-linux-arm64` | 同上 |
| Windows x64 | `@chunkdash/crossdesk-cli-windows-x64` | Windows 10 或以上,原生 Console / ConPTY 与 VT 输出 |
`apps/cli/npm/platforms.json` 的 `packageName` 是打包脚本、入口包依赖和启动器共同使用的名称来源。Windows 的内部目标键仍为 `win32-x64`,包内 `os` 仍为 `win32`;只将 npm 包名写为 `windows-x64`,不能把 Node 平台标识也改成 `windows`。Scope 用于明确组织归属,不保证免于 npm 的名称或反垃圾审核。
这些是构建与分发目标,不代表均已实机通过。当前完整构建与 npm 运行验证只覆盖 macOS arm64;CLI 主程序与平台 I/O 已通过 Windows x64 与 Linux x64、arm64 的交叉编译检查。其他平台的完整 MiniRTC 链接、实际视频连接、键鼠、终端恢复仍需对应机器验收。
普通终端使用 ANSI 渲染,按能力选择 TrueColor / 256 色 / 基础色;Kitty 图形协议是可选增强。macOS Terminal、iTerm2、Linux 常用终端、Windows Terminal 和 VS Code 集成终端属于目标场景。SSH 应分配 PTY(`ssh -t`);tmux / screen 使用 ANSI。不支持 `TERM=dumb` 或把交互观看输出重定向到文件;非交互宿主使用 `bridge --stdio`。Alpine / musl 当前没有预编译包。Windows 独立 mintty 窗口可能不提供 Console / ConPTY,应使用 Windows Terminal 承载 Git Bash。
CI 的 macOS 构建配置指定 `--target_minver=13.0`,但最终必须检查实际产物及依赖。当前开发机的本地二进制最低要求为 **macOS 27.0**,不应将这个本地产物宣称为旧 macOS 通用版。
## 生成包
先按 [构建说明](terminal-client.md) 构建 release。macOS 打包前需要签名,CI 使用 ad-hoc 签名;本地可执行以下命令(Intel 将 `arm64` 换成 `x86_64`):
```sh
codesign --force --sign - build/macosx/arm64/release/crossdesk-cli
```
然后生成 npm 包:
```sh
npm --prefix apps/cli run pack:npm
```
脚本读取 `apps/cli/package.json` 与 `apps/cli/xmake.lua` 的版本,检查二者一致;确认源码中的 MiniRTC 日志脱敏补丁已应用,再检查当前平台二进制格式、CPU、动态链接依赖,并运行离线 `doctor --json`。macOS 只接受系统动态库,Linux 只接受列明的基础系统运行库,Windows 拒绝未随产品处理的 DLL 和动态 MSVC 运行库。Windows 在含 `dumpbin.exe` 的 Visual Studio 开发者环境中运行打包。脚本预先检查两个输出文件是否存在,并在临时目录中完成两个包后再写入输出目录;写入失败时清理本次已写入的包,已有同名产物不会被覆盖。
默认从 `build/<platform>/<arch>/release/` 读取原生程序,将两个 `.tgz` 写入 `build/npm/`。可指定路径:
```sh
npm --prefix apps/cli run pack:npm -- --binary /absolute/path/crossdesk-cli --out-dir /absolute/path/output
```
每台构建机生成当前平台的原生包与相同版本的通用启动包。以 macOS arm64、版本 0.1.0 为例,文件名为 `chunkdash-crossdesk-cli-darwin-arm64-0.1.0.tgz` 和 `chunkdash-crossdesk-cli-0.1.0.tgz`;npm 将 `@scope/name` 的 tarball 文件名写作 `scope-name-version.tgz`。五个 CI artifact 各含一份入口包,最终需要五个平台包和一份入口包,共六个 tarball。
`apps/cli/npm/platforms.json` 集中定义五个平台;脚本校验平台包名合法且不重复,启动包通过精确版本的 `optionalDependencies` 让 npm 仅安装匹配系统和 CPU 的原生包。生成的六个包都包含作者、源码仓库、主页、问题反馈地址,以及面向 npm 官方注册表的公开发布配置。原生包包含二进制、运行要求、SHA-256,以及当前控制端和已应用修补的 MiniRTC 源码快照。外部 Xmake 依赖的许可证、对应源码和重链接材料需要随正式版本一并提供,参见 [源码来源](source-provenance.md)。源码快照已含 MiniRTC,解包后无需运行 `prepare-deps.sh`;在 `source/` 目录按上述构建说明显式关闭桌面、开启 CLI,再构建 `crossdesk-cli`。源码包仅包含 CLI 及其依赖,不包含桌面或移动应用。
`apps/cli/package.json` 的 `private: true` 表示源码目录不是发布产物。只发布打包脚本生成的 tarball;生成的包不带 private 标记。
## 本地安装验证
以 macOS arm64 为例,用生成包的绝对路径,在工程外的临时目录执行:
```sh
npx --yes --offline \
--package /absolute/path/chunkdash-crossdesk-cli-darwin-arm64-0.1.0.tgz \
--package /absolute/path/chunkdash-crossdesk-cli-0.1.0.tgz \
-- crossdesk-cli doctor --json
```
也可先 `npm install --ignore-scripts` 安装这两个本地 tarball,再使用安装目录中的 `crossdesk-cli`。验证交互观看需要真实 TTY 与授权的被控端,doctor 不会建立远程连接。
Pi 等宿主应直接启动原生程序以可靠控制进程和专用密码描述符。全局安装后,用 `crossdesk-cli --print-binary-path` 获取路径,再将该路径设为 `CROSSDESK_CLI`;不要把整条 npx 命令放进此变量。
## CI 与发布
`.github/workflows/build-cli.yml` 配置五个平台的原生 release 构建、doctor 和打包,并上传 tarball。普通 push、PR 和手动构建只生成 artifact;`.github/workflows/release-cli.yml` 复用同一构建流程,再汇总并发布六个 npm 包。`cli-v*` 标签只进入 CLI 发布工作流,不重复触发独立 CLI 构建或桌面发布。更新 MiniRTC 引用时,必须先将对应提交推送到子模块远端,再推送 CrossDesk,否则全新检出和 CI 会在拉取子模块时失败。
### GitHub Actions 自动发布
| 操作 | 结果 |
| --- | --- |
| 推送普通提交 / 提交 PR | 构建五个平台、上传 artifact,不发布 npm |
| Actions → Publish CLI to npm → Run workflow,保持 `dry_run` 勾选 | 完整构建并执行六个包的发布预演,不写入 npm |
| 推送 `cli-v0.1.1` | 校验源码版本为 `0.1.1`,构建并发布到 `latest` |
| 推送 `cli-v0.2.0-rc.1` | 校验源码版本为 `0.2.0-rc.1`,构建并发布到 `next` |
| 在对应 `cli-v*` 标签上手动运行并取消 `dry_run` | 执行真实发布;分支上不允许关闭预演 |
准备下一版时同步修改 `apps/cli/package.json` 的 `version` 与 `apps/cli/xmake.lua` 的 `set_version`,提交并推送代码后创建同版本标签。例如源码已更新为 `0.1.1` 时:
```sh
git tag cli-v0.1.1
git push origin cli-v0.1.1
```
已经上传过 `0.1.0` 的包不能被新构建覆盖;下一次原生程序发布应使用新版本。手动预演使用 `npm pack --dry-run` 加上内容校验,不调用 `npm publish` 或交换 OIDC 凭据;可对分支运行,也允许检查已发布的版本,但不验证 npm 发布权限。标签运行始终校验版本一致性。GitHub 的手动运行入口需要该工作流先进入默认分支。
发布 job 只下载当前运行的五个 `npm-*` artifact,并保留独立目录。`release-npm.cjs` 校验包名、版本、平台、入口依赖、原生程序 SHA-256 和 tarball SHA-512;五份入口包的文件内容必须一致(允许 Windows CRLF 差异),实际采用 Linux x64 构建生成的入口,保留 Unix 启动权限。真实上传前先检查所有包的注册表状态,同版本内容不同或目标 dist-tag 已指向更新版本就停止。
上传顺序为五个平台包 → 等待其精确版本、完整性及 dist-tag 可查询 → 入口包 → 最终确认。npm 返回异步处理时,每 15 秒查询一次,每批最多等待 15 分钟;不会因查询不到版本就重复上传。真实发布带 provenance,使用 OIDC 短期凭据,只有发布 job 获得 `id-token: write`,整个流程不需要 `NPM_TOKEN`。发布 job 串行执行,不中断正在上传的版本。
如果发布 job 失败,优先使用 GitHub 的 **Re-run failed jobs**,复用本次已成功构建的 artifact。已存在且完整性、目标 dist-tag 一致的包会跳过;同版本不同内容、仍为 `0.0.0-stage` 的占位包、403 或网络错误都会停止并给出原因。等待 npm 处理完成或修复权限后再重跑,不要直接重跑全部构建;重新编译可能产生不同字节。如果已有版本的 dist-tag 与本次目标不一致,需要先人工检查并调整标签。本工作流不自动修改已有版本的 dist-tag,也不提供 `next` 到 `latest` 的推广操作。
### npm 账号一次性绑定
在下面六个包各自的 npm **Settings → Trusted Publisher** 中选择 **GitHub Actions**,不是在 npm 账号菜单中授权 GitHub:
- `@chunkdash/crossdesk-cli`
- `@chunkdash/crossdesk-cli-darwin-arm64`
- `@chunkdash/crossdesk-cli-darwin-x64`
- `@chunkdash/crossdesk-cli-linux-arm64`
- `@chunkdash/crossdesk-cli-linux-x64`
- `@chunkdash/crossdesk-cli-windows-x64`
六个包使用同一组配置:
| npm 字段 | 值 |
| --- | --- |
| Organization or user | `kunkundi`(GitHub 仓库所有者,不是 npm scope) |
| Repository | `crossdesk` |
| Workflow filename | `release-cli.yml`(只填写文件名) |
| Environment name | `npm` |
| Allowed actions | 勾选 **Allow npm publish**;本流程不需要 **Allow npm dist-tag** |
工作流使用 GitHub-hosted runner、Node.js 24 和 npm 12.2.1。GitHub 仓库的 `npm` environment 名称必须与 npm 配置完全一致,可在仓库 Settings → Environments 中限定只有 `cli-v*` 标签能部署;如设置此限制,分支预演也会被该环境规则拦截,应选择对应标签预演。可按团队需要设置 required reviewers;这是 GitHub 的发布审批,与 npm 2FA 不同。
新建 Trusted Publisher 后应在 **2 天内完成首次真实发布**,否则配置过期,需要删除后重建;预演不算成功发布。因此建议先推送工作流并完成预演,再创建 npm 信任配置,随后发布一个新版本。设置时 npm 可能要求当前账号完成浏览器 / 2FA 认证,不能由 GitHub Secrets 替代。若 scoped 入口包还没创建,需要先按下文从本地发布一次,再配置它的 Trusted Publisher。该绑定只解决身份认证,不豁免 npm 的包名与反垃圾审核。
### 构建缓存
CLI 构建分别缓存 Xmake 工具和已安装的原生依赖。原生依赖使用 `XMAKE_PKG_INSTALLDIR` 指定的 runner 临时目录,不缓存工程的 `.xmake` 配置或 `build` 产物。缓存 key 包含 runner 类型、目标架构、镜像版本、Xmake 版本以及构建配置、依赖配方和补丁的内容;不包含运行编号,也不因 CLI C++ 源码、README 或 npm 元数据修改而失效。镜像更新可能包含编译器/SDK 更新,因此会重新生成缓存;依赖配置变化时只接受新的完整 key,不回退使用旧配方的库。
`Restore native dependencies` 会显示缓存是否命中。各平台的 `xmake f` 成功安装依赖后立即执行 `Save native dependencies`,后续 CLI 编译、签名或打包失败也能在下一次复用;依赖安装失败时不会保存不完整缓存。Linux 仅对镜像中缺失的系统工具执行 APT 更新和安装。首次运行、缓存被清理、镜像或依赖配置变化仍需下载/编译依赖;缓存命中不代表跳过 CLI 自身构建和打包。
### 本地首次发布与故障恢复
首次使用 `@chunkdash` 前,确认当前 npm 账号属于该组织且有目标包的发布权限。本地发布需要已启用的 2FA 或符合 npm 要求的发布凭据;组织 owner 身份不代替发布认证。
```sh
npm whoami --registry=https://registry.npmjs.org/
npm org ls chunkdash --json --registry=https://registry.npmjs.org/
```
先重新构建五个平台的包;旧的 `crossdesk-cli-<平台>` tarball 不会自动变成 scoped 包,仅重命名文件也不会改变包内名称。已经发布的旧平台包保持原样,新入口只依赖 `@chunkdash` 包。每次版本发布同步更新 `apps/cli/package.json` 和 `apps/cli/xmake.lua`;若某个目标包的版本已经发布,不能用新的内容覆盖该版本。
如果五个 `@chunkdash/crossdesk-cli-*` 平台包的 `0.1.0` 已经发布,此次仅将入口从 `crossdesk-cli` 迁移到 `@chunkdash/crossdesk-cli`,可以沿用新入口的 `0.1.0` 并依赖原来的五个平台版本。确认注册表中各平台版本的完整性后,只发布新的 `chunkdash-crossdesk-cli-0.1.0.tgz`;不要重发已有平台版本。后续原生程序更新仍需整套提升版本并重新构建。
下载并解出同一提交、同一版本的六个 tarball,在其目录内执行发布。以下以 0.1.0 为例,先检查打包内容,再逐个发布五个平台包到 `next`:
```sh
(
set -e
for target in darwin-arm64 darwin-x64 linux-arm64 linux-x64 windows-x64; do
npm publish "./chunkdash-crossdesk-cli-${target}-0.1.0.tgz" \
--dry-run --ignore-scripts --access public --tag next --registry=https://registry.npmjs.org/
done
npm publish ./chunkdash-crossdesk-cli-0.1.0.tgz \
--dry-run --ignore-scripts --access public --tag next --registry=https://registry.npmjs.org/
)
```
`--dry-run` 只做本地发布预演,不能证明包名会被注册表接受。检查通过后发布平台包:
```sh
(
set -e
for target in darwin-arm64 darwin-x64 linux-arm64 linux-x64 windows-x64; do
npm publish "./chunkdash-crossdesk-cli-${target}-0.1.0.tgz" \
--ignore-scripts --access public --tag next --registry=https://registry.npmjs.org/
done
)
```
若 npm 返回正在处理(HTTP 202),等待对应的确切版本可查询,不要立即重复上传。遇到 `403 Package name triggered spam detection` 时停止,保留已成功的包,向 [npm 支持](https://www.npmjs.com/support)提供包名、仓库地址、版本及错误日志申请复核;不要自动重试整轮发布。恢复时先查询各包的确切版本,核对注册表的 `dist.integrity` 与 `npm pack <本地.tgz> --dry-run --json --ignore-scripts` 的 `integrity`,只补发未成功的包;同版本内容不同则停止并提升整套版本。
全部原生包可查询、完整性一致后,才发布通用入口:
```sh
(
set -e
for target in darwin-arm64 darwin-x64 linux-arm64 linux-x64 windows-x64; do
npm view "@chunkdash/crossdesk-cli-${target}@0.1.0" version dist.integrity \
--registry=https://registry.npmjs.org/
done
npm publish ./chunkdash-crossdesk-cli-0.1.0.tgz \
--ignore-scripts --access public --tag next --registry=https://registry.npmjs.org/
)
```
再确认 `@chunkdash/crossdesk-cli@0.1.0` 的依赖和完整性,并在五个平台用 `npx @chunkdash/crossdesk-cli@0.1.0 doctor --json` 做安装验证;交互连接仍需实机验收。验收后按需将六个包的 `latest` 指向该版本。不要只凭 `--tag next` 假定首次发布的包不会出现 `latest`,应查询实际 `dist-tags`。缺失平台包时启动器明确报错,不下载别的平台程序或回退为源码编译。
依据:[npm bin / optionalDependencies / os / cpu](https://docs.npmjs.com/cli/v11/configuring-npm/package-json/)、[npm scopes](https://docs.npmjs.com/about-scopes/)、[npm publish](https://docs.npmjs.com/cli/v11/commands/npm-publish/)、[Trusted Publishing](https://docs.npmjs.com/trusted-publishers/)、[npx](https://docs.npmjs.com/cli/v11/commands/npx/)、[GitHub runner 平台](https://docs.github.com/en/actions/reference/runners/github-hosted-runners)、[Windows Console 模式](https://learn.microsoft.com/en-us/windows/console/getconsolemode)。