键盘快捷键

按 ← 或 → 切换章节

按 S 或 / 搜索本书

按 ? 显示快捷键帮助

按 Esc 关闭帮助

cjr 开发指南

cjr 使用 Rust 实现。一个 Cargo package 同时包含 CLI 和 library,用户接口限定为程序执行、inspect、list 和 cache clean。

修改命令行为时,同步更新中英文用户手册。通用能力优先使用标准库或维护中的依赖;版本识别、官方资产选择、cjv 适配和缓存策略由项目自身实现。

仓库保存源码、常规测试和可维护的文档。研究目录、SDK 下载、探测脚本、审计或验收报告及构建产物保留在本地忽略目录中。

问题和改进建议可以提交到 GitHub Issues。提交前读取最近的 commit 信息,保持仓库现有风格。

构建与测试

环境

需要 Rust 1.89 或更新版本、C 编译器和 CMake。Windows 使用 MSVC。TLS 依赖中的 aws-lc 会编译本地代码。

cargo build --release --locked

可执行文件位于 target/release/cjr,Windows 为 target/release/cjr.exe。Cargo.lock 固定完整依赖图;更新依赖时同时提交锁文件。

常规检查

cargo fmt --all -- --check
cargo clippy --all-targets --locked -- -D warnings
cargo test --all-targets --locked

测试覆盖二进制解析、命令参数、下载错误、缓存生命周期和进程状态。HTTP 场景使用 mockito,任意输入测试使用 proptest。常规测试使用本地夹具和模拟服务。

CI 在 Windows、Linux、macOS ARM64 和 Intel 上运行这些检查并构建 release,另在 Linux 检查最低 Rust 版本。网站任务构建 landing 页和文档。

实现约束

  • 平台 FFI 和必要的 unsafe 集中在私有 platform 模块,通过安全接口供业务模块调用,并说明调用前提。
  • 缓存变更和使用登记必须持有 StoreTransaction;cjv 所有的文件保持原样。
  • 下载退避使用 backon,遍历目录使用 walkdir,二进制解析使用 object。
  • 派生进程退出状态待确认时保留使用记录。清理失败时仍返回目标程序的退出状态。

架构

模块边界

模块职责
app、cli命令解析和执行编排
inspectPE、ELF、Mach-O 的版本与依赖解析
catalog通过官方清单匹配完整 SDK 版本与平台
cjv读取 cjv 配置、归档和受管安装
provision下载、校验和选择性解包
store不可变运行库快照、使用记录与清理
loader宿主兼容性、加载路径和依赖检查
process启动、信号、退出状态与子进程监督
platform必要的原生调用及其安全封装

运行流程

读取程序元数据后,cjr 检查宿主架构和依赖。需要运行库时,根据完整 SDK 版本选择具有官方摘要的资产,复用本地内容或下载归档,提取并发布运行库快照。最终依赖检查通过后登记使用记录,释放存储锁,再启动程序。

SDK 版本来自 PE .cjsdkv、ELF .cjmetadata 或 Mach-O __CJ_METADATA,__cjsdkversion。它标识具体的 SDK 发行版本,作为发行包的选择依据。所有解析必须校验边界,未知布局返回错误。

Mach-O 的 chained fixups 由 object 解析,inspect::macho::fixups 将 rebase 结果映射到版本表。支持 DYLD_CHAINED_PTR_64、64_OFFSET、ARM64E、ARM64E_USERLAND 和 ARM64E_USERLAND24,包括认证指针。解析以静态文件读取方式解码指针位字段;版本条目仅接受指向本文件字符串的 rebase 引用。链遍历有工作量上限,并检查文件与虚拟地址映射、页边界和重复条目。

缓存生命周期

StoreTransaction 持有存储排他锁。元数据、准备、发布、使用登记和清理通过同一把锁协调。各版本的首次准备串行执行,启动程序前释放锁。

运行库发布到 runtimes/<bundle-id>/,包含 receipt.json 和 payload/。内容清单记录文件摘要、权限和链接目标。发布通过同卷 rename 完成;已发布运行库保持原样,每次使用前重新校验。

下载由 reqwest 执行,重试由 backon 管理。归档校验 SHA-256 后,只提取 runtime 目录和许可通知;拒绝越界路径、重复条目、特殊文件及逃出 payload 的链接。

cjv 受管安装读取按 .lifecycle.lock、.lifecycle.lookup.lock 的顺序加锁。使用归档的来源标为 archive-verified,受管安装快照标为 cjv-managed。安装记录标识 cjv 管理的来源;与官方归档逐字节一致性的验证以归档摘要为准。

加载和进程

环境修改只作用于子进程。参数通过 OsString 直接传给进程接口。Windows 检查应用目录 DLL 遮蔽,Linux 检查 RPATH/RUNPATH 和预加载冲突,macOS 展开 dyld 的路径占位符并拒绝已识别的受限程序。

Windows 使用 Job Object,Linux 使用 subreaper。Unix 转发终止信号并恢复前台终端。派生进程退出状态待确认时保留运行库使用记录;同一主机重启后可以回收旧会话记录。

监督涉及进程级状态,同一进程内只允许一个监督实例。嵌入该 library 的宿主需协调进程级信号 disposition。缓存的信任边界是当前用户的本地文件系统;同一 UID 的进程可以修改缓存,网络文件系统的锁语义需由宿主另行适配。

文档与网站

网站沿用 cjv 的组织方式:web/ 是 landing 页,docs/user-guide/ 和 docs/dev-guide/ 分别维护用户手册与开发指南。每本书的 src/ 为中文,en/ 为英文。

landing 页使用 React、TypeScript 和 Vite。安装方式切换由 Radix Tabs 处理,翻译通过 react-i18next 提供。构建时由 React 预渲染双语 HTML,浏览器通过 hydration 接管交互。字体随站点打包,文档由 mdBook 生成。

本地开发

需要 Node.js 22.22.2+、24.15+ 或 26+,以及 npm 和 mdBook 0.5.4。建议使用当前 Node.js LTS。

cd web
npm ci
npm run dev

前端源码按职责组织:

路径职责
src/App.tsx页面组合
src/components/导航、安装方式、下载与文档入口、命令复制
src/config.ts发行资产和安装命令
src/i18n/翻译类型、语言检测和实例初始化
src/locales/中英文翻译资源
scripts/版本读取、React 预渲染、mdBook 构建与导航

npm run typecheck 执行严格 TypeScript 检查;npm run lint 和 npm run format 分别检查和整理代码。npm run test:unit 运行 Vitest 与 Testing Library 交互测试。

单独预览中文手册:

mdbook serve docs/user-guide

构建完整站点

cd web
npm run build
npm run preview

构建输出位于根目录 dist/,包含首页和四本书:

dist/
  index.html
  zh-CN/index.html
  en/index.html
  assets/
  book/user-guide/zh-CN/
  book/user-guide/en/
  book/dev-guide/zh-CN/
  book/dev-guide/en/

默认部署在域名根路径。在子路径部署时,例如 GitHub Pages 的 /cjr/,设置 SITE_BASE=/cjr/ 后构建。首页链接和静态资源将使用同一前缀。

部署时上传整个 dist/。node_modules/、dist/、.edgeone/ 和 mdBook 的 book/ 输出保留在本地忽略目录中。

修改文档时同步更新两种语言和各自的 SUMMARY.md。安装说明和下载按钮应指向已发布的安装脚本或二进制。

语言与版本

首页文案位于 web/src/locales/zh-CN.json 和 en.json,由 i18next 处理。显式语言 URL 优先,其次是旧链接的 ?lang=、已保存的选择和浏览器语言。其他语言回退到英文。

构建时使用与浏览器相同的 React 组件生成 /zh-CN/ 和 /en/ 的完整 HTML,下载和文档链接由静态 HTML 提供。每种语言独立初始化翻译实例,hydration 使用对应页面的语言。根路径按语言偏好跳转到相应语言页。页面标题、搜索摘要、无障碍标签和复制提示均使用翻译资源。下载版本从 Cargo.toml 读取,与二进制保持一致。

两种语言的文档使用相同章节文件名。构建过程为每章添加语言链接,切换时保留章节、移除各语言特有的标题锚点;首页链接保持所选语言。中文文档工具栏翻译位于 web/src/locales/book-zh-CN.json。

npm test 依次执行 Biome 检查、组件交互测试、TypeScript 检查、完整站点构建和产物测试。测试覆盖 tab 键盘操作、复制成功与失败反馈、双语 hydration、语言优先级、下载链接和章节切换。新增文案必须同时补齐两种语言,缺少翻译键会使构建失败。可以用 SITE_BASE=/cjr/ npm test 验证子路径部署。CI 和生产部署使用同一套命令。

生产部署

官网为 cjr.zxilly.com,托管在 EdgeOne Makers。项目 cjr-web 使用腾讯云中国站账户,部署区域为全球(含中国大陆)。

.github/workflows/website.yml 在 master 的网站、文档、版本或部署工作流变更后自动构建、测试并上传完整站点,也支持手动触发。发布工作流在 Release 正式发布后调用它,更新网站的版本和下载链接。生产部署串行执行,每次上传完成后再开始下一次部署。

GitHub 仓库配置:

类型名称内容
VariableEDGEONE_PROJECT_NAMEcjr-web
SecretEDGEONE_API_TOKEN中国站 Makers API Token

在 Makers 中绑定生产域名 cjr.zxilly.com,再按控制台提供的目标地址配置 Cloudflare CNAME,使用 DNS only。证书由 Makers 管理。DNS 和域名绑定配置一次后持续沿用。

手动部署已构建的站点:

npx --yes edgeone@1.6.47 login --site china --token "$EDGEONE_API_TOKEN"
npx --yes edgeone@1.6.47 makers deploy ./dist \
  --name cjr-web --area global --env production --skip-ai-gateway-sync

部署记录见 GitHub Actions。需要回退时,在 Makers 的部署列表中选择先前成功的生产部署。

发布版本

版本号由 Cargo.toml 的 package.version 定义。Clap 在编译时将它嵌入程序,cjr --version 和 cjr -V 均输出该版本;版本信息随二进制一同分发。

发布步骤

  1. 更新 Cargo.toml 和 Cargo.lock 中的包版本,同步网站包版本、中英文安装示例。
  2. 运行格式化、Clippy、测试及 web/ 下的 npm test,提交并推送改动。
  3. 创建与包版本一致的稳定标签,例如 git tag -a v0.0.1 -m "cjr v0.0.1",再运行 git push origin v0.0.1。

release.yml 首先检查标签与包版本,再调用常规 CI。通过后创建草稿 Release,使用 taiki-e/upload-rust-binary-action 构建、打包并上传四个平台的程序及 SHA-256 文件:

  • Windows x64:x86_64-pc-windows-msvc,ZIP。
  • Linux x64:x86_64-unknown-linux-musl,tar.gz。
  • macOS Apple Silicon:aarch64-apple-darwin,tar.gz。
  • macOS Intel:x86_64-apple-darwin,tar.gz。

归档命名为 cjr-v<版本>-<target>,包含可执行文件及中英文 README。macOS 最低版本为 13.0。发布任务逐个运行二进制的 --version,全部成功后由 softprops/action-gh-release 将草稿公开。失败时草稿保持未发布,可在 Actions 中重跑失败任务。已公开版本的标签保持固定,后续修复使用新版本号。

Action 固定到完整提交 SHA,并由 Dependabot 跟踪更新。工作流使用 GitHub 提供的 GITHUB_TOKEN,只在 Release 创建、上传和公开任务中授予写权限。

静态链接

.cargo/config.toml 为 Windows MSVC 和 Linux musl 目标启用 crt-static。Windows 将 C/C++ 运行时链接进程序,仍依赖 Windows 系统 DLL;Linux 产物采用完全静态链接,动态依赖表和 ELF 解释器项均为空。Rust 依赖与可打包的原生依赖静态链接。macOS 使用 /usr/lib/、/System/Library/ 下的系统库,由操作系统提供 libSystem 和系统框架。

发布工作流读取最终二进制的依赖表:拒绝 Windows 动态 CRT、Linux 动态依赖和 macOS 非系统库。此策略针对 cjr 本身;被启动的仓颉程序仍使用匹配的动态运行库。