免安装包打包
August 10, 2026 · View on GitHub
SmartPerfetto 的免安装包不是单文件二进制。启动器负责拉起包内 Node.js 24
runtime、后端、预构建 Perfetto UI、固定版本 trace_processor_shell 和签名的
Android Internals Knowledge Pack。
当前维护的 release asset:
smartperfetto-v<version>-windows-x64.zip(Windows 10 / Windows Server 2016 或以上 x64)smartperfetto-v<version>-macos-arm64.zip(macOS 13.5 或以上 Apple silicon 设备)smartperfetto-v<version>-linux-x64.tar.gz(glibc 2.34 或以上 x64 Linux;不支持 Alpine Linux 等基于 musl 的发行版)
打包
npm run package:portable
单平台:
npm run package:windows-exe
npm run package:macos-app
npm run package:linux
输出:
dist/portable/smartperfetto-v<version>-windows-x64.zip
dist/portable/smartperfetto-v<version>-macos-arm64.zip
dist/portable/smartperfetto-v<version>-linux-x64.tar.gz
兼容 Windows 旧命令仍输出到:
dist/windows-exe/smartperfetto-v<version>-windows-x64.zip
发布
完整公开发布顺序见 发布手册。免安装包发布通常在 npm CLI 发布和 smoke 通过后执行。
正常公开发布中的 portable 步骤:
npm run version:set -- <version>
npm run version:sync -- --check
git add package.json package-lock.json backend/package.json backend/package-lock.json
git commit -m "chore: release v<version>"
git push origin main
npm --prefix backend run cli:pack-check
cd backend
npm publish --access public
cd ..
npm run package:portable
# 以下命令分别在归档匹配的 Windows x64、macOS arm64、Linux x64 上运行
node scripts/smoke-portable-archive.cjs --asset <final-archive> --target <target> \
--version <version> --commit <commit> --public-release \
--output-dir dist/portable/smoke-evidence/<target>
# 没有目标机器时,从默认分支对现有 draft 运行:
gh workflow run portable-exact-archive-smoke.yml \
-f release_id=<numeric-release-id> -f selection=all
gh run download <run-id> \
--name portable-smoke-evidence-release-<numeric-release-id> \
--dir <download-dir>
npm run release:portable -- <version> --skip-build --no-draft \
--release-commit <draft-target-full-sha> \
--smoke-evidence-dir <download-dir>/promotion-evidence \
--smoke-attestation <download-dir>/portable-smoke-attestation.json \
--smoke-run-id <run-id>
package:portable 会从 scripts/node-runtime-pin.env 读取明确的 Node.js
版本、三平台归档 SHA-256 和可执行内容摘要,而不是在构建时动态选择最新版本。
macOS 内容摘要只归一化受代码签名影响的 Mach-O 字段,因此 Developer ID
重新签名不能掩盖可执行内容变化;更新 Node runtime 必须评审并同步这些 pin。
脚本会构建三平台包并校验 schema v3 manifest,其中包含
distribution、channel、target、commit 和 signing mode。traceProcessor
同时记录上游固定文件的 sourceSha256 与签名后归档字节的 sha256,避免把
供应链来源校验和最终产物校验混为一谈。release:portable --skip-build
只复用刚刚为同一版本、同一 commit 构建出的包。公开 promotion 会逐 target
验证 smoke summary 的原生 host、归档文件名、size、SHA256、commit、health、
runtime probe 和生命周期收据;任一证据缺失或与待上传字节不一致都会停止发布。
每次 smoke 的 --output-dir 必须是尚不存在的新路径;成功只会原子写入
smoke-summary.json,失败另写 smoke-failure.json,因此重跑要换新目录,不能覆盖
先前证据。
hosted workflow 只接受包含当前 schema-v2 lifecycle smoke contract 的 release
commit,并按不可变 release ID/asset ID 下载。下载后和 smoke 后都会重新读取 GitHub
release;release commit 自带 verifier 和默认分支固定 SHA 的 verifier 会各验证一次。
windows-linux 或单平台 selection 只生成 partial 诊断证据,不能用于公开发布;
只有 selection=all 成功且 macOS 是 Developer ID 签名、公证、staple 后的 final zip,
combined artifact 中的 promotion-evidence/ 才可能用于 promotion。必须按成功的
run ID 下载整个 combined artifact,并把其中的 promotion-evidence/、同级
portable-smoke-attestation.json 和该 run ID 一起传给发布命令;发布脚本会重新读取
Actions run/唯一 combined artifact,并用 GitHub artifact SHA-256 校验下载的 zip,
再逐字节绑定本地证据,不能手工拼接单个 job artifact。
发布脚本始终先创建或复用 draft,上传后逐项校验 target commit、标题、asset
名称、大小和 GitHub sha256: digest。--no-draft 是严格的 promotion-only 路径:
draft 和三份 asset 必须已经存在;它不会创建 release、编辑标题/target、上传或
--clobber asset,只会在发布前后比较 release ID 以及每个 asset 的 ID、状态、
名称、大小和 digest,然后改变 draft 标志。
如果 smoke/promotion gate 在 draft 构建后又有加固提交,可从更新后的 clean gate
checkout 运行,并增加 --release-commit <draft-target-full-sha>;脚本只接受 gate
commit 的祖先,包、证据和远端 target 仍全部绑定该 release SHA。
--no-draft 必须同时提供默认三个平台,不允许发布部分平台集合。已公开 release
是只读的:脚本只验证完整三平台集合,完全一致时幂等退出,不会 clobber、编辑或
替换任何 asset。没有刚构建过同版本同 commit 包时,不要使用 --skip-build。
仅构建/上传某个平台的 draft 候选(不能单独 promotion):
npm run release:portable -- <version> --targets macos-arm64
npm run release:windows-exe -- <version>
公开发布不要使用 --allow-dirty。如果 npm 发布后发现大 bug,修复后必须发布
新的 patch 版本,不要复用已经发布到 npm 的旧版本号。
macOS 签名和公证
未设置签名变量时,脚本会生成 ad-hoc signed app,避免 macOS 把 bundle 判定为 damaged;但 ad-hoc 签名不会通过 Gatekeeper 公证检查,只适合本地测试或需要用户 手动 Control-click → Open 的 draft 包。正式公开 macOS 包必须设置:
export SMARTPERFETTO_MACOS_SIGN_IDENTITY="Developer ID Application: ..."
export SMARTPERFETTO_MACOS_NOTARY_PROFILE="notarytool-keychain-profile"
npm run release:portable -- <version> --targets macos-arm64
设置签名身份后脚本会 codesign --options runtime 并做 strict verify;设置 notary
profile 后会通过 xcrun notarytool submit --wait 提交,并对 .app staple 后重新
生成 final zip。打包器还会用 notarytool info 复核同一 submission 为 Accepted,
并只把精简的 NOTARIZATION-RECEIPT.json 放入 final zip。notary profile 是保存在
本机钥匙串里的 notarytool 凭据别名,不是 provisioning profile;API 私钥不得
进入仓库或发布日志。
打包器按 Mach-O 文件头而不是扩展名/可执行位发现嵌套原生二进制并逐个签名。
重签已有上游签名的 Node/Claude runtime 时只保留原有 identifier 和 entitlements;
不能给任意未签 Mach-O 注入 JIT entitlement,也不能用 codesign --force --deep
代替 inside-out 签名。final zip 校验器会逐个验证 Mach-O 签名和 Node/Claude
必需的 runtime entitlement。
用户数据目录
Windows 用户操作以 Windows 配置与运行指南 为准; 本节只定义打包与运维路径契约。
- Windows:
%LOCALAPPDATA%\SmartPerfetto是 data root,直接包含backend/、providers/、uploads/、user/、logs/和env,没有额外的data/层。 - macOS:
~/Library/Application Support/SmartPerfetto和~/Library/Logs/SmartPerfetto。 - Linux:
${XDG_DATA_HOME:-~/.local/share}/smartperfetto和${XDG_STATE_HOME:-~/.local/state}/smartperfetto/logs。
AI 分析推荐在 UI 里配置 Provider profile。需要 env 凭证时,在对应用户数据目录
创建 env 文件后重启启动器。
Windows 新包首次启动时,会自动发现符合版本目录命名的旧包,安全复制旧包的
package-local data/ 到 %LOCALAPPDATA%\SmartPerfetto,写入迁移回执后原子切换;
旧目录保持不变。复制过程拒绝 symlink、reparse point 和非普通文件。无法自动
发现时使用:
SmartPerfetto.exe --migrate-from "C:\path\to\old-package"
显式迁移应在第一次标准启动、目标目录不存在时运行。目标已存在时命令会报错, 不会合并或覆盖,来源和目标均保留。自动 sibling 发现只选择严格低于当前包版本的 最高版本;无法解析当前包版本时保守跳过。
需要真正随包移动的数据时,显式设置 SMARTPERFETTO_PORTABLE_MODE=1;该模式继续
使用包内 data/ / logs/ 并禁用自动和显式迁移。需要测试或运维覆盖整个 portable
data root 时使用 SMARTPERFETTO_PORTABLE_DATA_DIR;它同样禁用迁移。Launcher 会从
该 root 派生 backend、Provider、uploads 和 user 路径;不要用
SMARTPERFETTO_BACKEND_DATA_DIR 代替 portable root。
验证
脚本会校验包结构、版本、manifest、Node runtime、目标平台 native 依赖、
trace_processor_shell pin,以及 Knowledge Pack lock/manifest/database/license
的版本和哈希。交叉编译、结构和静态签名校验不证明目标系统能启动。公开发布采用
build-once:在各目标平台解压即将上传的同一份最终归档做 smoke,通过后不再重新
构建;macOS 必须测试公证、staple 后重新生成的 final zip。当前兼容下限是 Windows
10 / Windows Server 2016 及以上 x64、macOS arm64 13.5+ 和 Linux x64 glibc
2.34+;static verifier 会扫描包内全部 Mach-O/ELF,拒绝任何高于 manifest/
Info.plist 声明的原生依赖。
在归档声明的匹配 OS/arch 上运行统一 smoke 命令:
node scripts/smoke-portable-archive.cjs \
--asset "<final-archive>" \
--target "<windows-x64|macos-arm64|linux-x64>" \
--version "<version>" \
--commit "<release-commit>" \
--public-release \
--output-dir "<evidence-dir>"
仅在提交前本机验证未提交代码时,可显式添加 --allow-dirty;它不能与
--public-release 组合,生成的结果也不能用于 promotion。公开发布仍必须从 exact
clean commit 重新 build once 并 smoke。
该命令先执行安全的归档路径/link 校验和 static verifier,再从同一归档启动 launcher。
它使用隔离数据和日志目录、显式 127.0.0.1 health、包内 Node/Claude/OpenCode 与
最小 trace processor 查询,并通过 launcher 的 --shutdown-file 进行可审计的优雅
退出。--lifecycle-receipt 记录进程隔离方式、子进程 PID、退出码、是否强制升级和
端口释放;Windows 必须建立 kill-on-close Job Object,macOS/Linux 服务使用独立
进程组。失败时保留 launcher/backend/frontend 日志。它会拒绝在与 --target
不匹配的宿主上运行。
Windows smoke 还会实际加载 better-sqlite3 与 sodium-native,运行本地 Provider
Create/Activate/Get/Cleanup 生命周期,并用包内后端代码验证 DPAPI SecretStore 的
put/get/reopen;这些是默认 Provider 路径与 enterprise SecretStore 的两条独立证据。
--public-release 生成公开发布证据,并在 macOS 上额外验证 Developer ID、Gatekeeper、
notarization staple 与 Accepted notary receipt;仅验证草稿包时可以省略该参数。
包内 launcher 优先使用后端端口 3000、前端端口 10000。如果默认端口已被占用,
launcher 会自动选择下一个可用端口,并打印实际访问 URL。只有需要固定端口时才设置
SMARTPERFETTO_BACKEND_PORT 或 SMARTPERFETTO_FRONTEND_PORT;显式配置的端口不可用时会快速失败。
- 启动包内 launcher。
- 打开 launcher 打印的前端 URL,通常是 http://127.0.0.1:10000。
- 检查 launcher 打印的后端 health URL,通常是 http://127.0.0.1:3000/health。
- 上传一条小 trace,确认后端日志中启动了对应平台的
trace_processor_shell。 - 在包内 CLI 或后端运行
smp knowledge-pack status --format json,确认 bundled/active Pack 可解析且未撤回。 - 执行包内 Node、Claude 和 OpenCode 的版本命令(存在时)。
- 正常停止 launcher,确认子进程退出且前后端端口已经释放。
Windows、macOS 或 Linux 任一最终归档缺少目标平台 smoke 时,GitHub release 应保持 draft。只有用户明确接受并在 release/交付说明中公开未测试平台时才允许降级发布, 且不能称为全平台验证完成。