Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
125 commits
Select commit Hold shift + click to select a range
8093ed9
docs(design): 增加 navop 三产品彻底拆分设计稿
feigeCode Oct 28, 2026
312aa8e
refactor(navop_runtime): 按 database/redis/sftp 拆分可选特性
feigeCode Oct 28, 2026
d83316c
feat(core): 新增 TabItem::from_dyn 支持领域注册表构造 Tab
feigeCode Oct 28, 2026
dc3c7e9
feat(domain_api): 新增三产品共用的领域契约层
feigeCode Oct 28, 2026
e418125
feat(main): HomePage 实现 domain_api 的 TabHost/HomeContext 契约
feigeCode Oct 28, 2026
e12c794
refactor(extension-runtime): 将数据库与远程桌面能力改为可选特性
feigeCode Oct 28, 2026
62d38c9
refactor: Shell 侧扩展依赖关闭默认域特性,切断 terminal_view 到 db 的编译链
feigeCode Oct 28, 2026
4bc1361
refactor(ssh_form): 从 terminal_view 抽出 SSH 表单与主机密钥 UI
feigeCode Oct 28, 2026
7f2f5e2
docs(design): 记录三产品拆分的实施进度
feigeCode Oct 28, 2026
f7faece
fix(domain_api): HomeContext::notify 暴露 Window/App 以推送通知
feigeCode Oct 28, 2026
ca74756
feat(app_kit): 按产品身份配置数据目录与 keychain,修复 ssh_form 移动后的源码断言路径
feigeCode Oct 28, 2026
09d8bd1
feat(core): 运行时应用路径按产品身份切换便携/开发/缓存目录
feigeCode Oct 28, 2026
cf60c58
docs(design): 更新 app_kit 与身份路径切换进度
feigeCode Oct 28, 2026
fa0a70f
feat(apps): 新增 navop-shell 与 navop-data 可构建的骨架可执行包
feigeCode Oct 28, 2026
b0a84f7
docs(design): 记录 shell/data 骨架包进度
feigeCode Oct 28, 2026
6ef0f60
refactor(main): 实现 Product 并通过 app_kit::bootstrap 初始化,领域 init 支持错误传播
feigeCode Oct 28, 2026
dbdeb66
docs(design): 记录 Product 启动契约进度
feigeCode Oct 28, 2026
2c7ba28
feat(shell_domain): 迁入 KnownHosts 域并通过 DomainRegistry 打开 Tab
feigeCode Oct 28, 2026
1ba6df6
docs(design): 记录 KnownHosts 域迁移进度
feigeCode Oct 28, 2026
296b0f7
feat(apps): 两个独立应用改为 Product 启动,TabOpener 支持打开模式
feigeCode Oct 28, 2026
b2b3531
docs(design): 记录 Product 化 bins 与 TabOpener 模式参数
feigeCode Oct 28, 2026
7295e41
feat(data_domain): 迁入 Redis 单连接打开路径并接入 ConnectionRegistry
feigeCode Oct 28, 2026
b099e19
docs(design): 记录 data_domain Redis 迁移
feigeCode Oct 28, 2026
87b76d5
feat(data_domain): 覆盖 Redis 工作区标签与连接排序
feigeCode Oct 28, 2026
e19f700
docs(design): 记录 Redis 工作区迁移
feigeCode Oct 28, 2026
6a7f202
feat(data_domain): 迁入 Database 单连接与工作区打开路径
feigeCode Oct 28, 2026
fd1d631
docs(design): 记录 Database 打开路径迁移
feigeCode Oct 28, 2026
93b073e
feat(data_domain): 迁入 MongoDB 单连接与工作区打开路径
feigeCode Oct 28, 2026
de6ca16
docs(design): 记录 MongoDB 迁移
feigeCode Oct 28, 2026
f548b96
docs(design): 修正 MongoDB 提交号
feigeCode Oct 28, 2026
6f53fbe
refactor(shell_domain): 本地终端经领域注册表打开
feigeCode Oct 28, 2026
78cd7f0
docs(design): 记录本地终端迁移
feigeCode Oct 28, 2026
f0cb91d
refactor(shell_domain): SSH 终端经领域注册表打开,并增加应用壳事件桥
feigeCode Oct 28, 2026
082eb5f
docs(design): 记录 SSH 领域迁移与事件桥
feigeCode Oct 28, 2026
36b4818
refactor(shell_domain): SFTP 双栏视图经领域注册表打开
feigeCode Oct 28, 2026
d127c4c
docs(design): 记录 SFTP 领域迁移
feigeCode Oct 28, 2026
9a5455c
refactor(shell_domain): 端口转发连接经领域注册表打开
feigeCode Oct 28, 2026
f0bfaf0
docs(design): 记录端口转发领域迁移
feigeCode Oct 28, 2026
0209bdb
fix(domain_api): 惰性打开已存在的连接标签,避免重复建连
feigeCode Oct 28, 2026
658dd71
docs(design): 记录惰性打开修复
feigeCode Oct 28, 2026
5796eaf
refactor(shell_domain): RDP/VNC 连接经领域注册表打开
feigeCode Oct 28, 2026
d40a513
docs(design): 记录 RDP/VNC 领域迁移
feigeCode Oct 28, 2026
cb6e54b
refactor(shell_domain): 会话日志页面迁入 shell 领域
feigeCode Oct 28, 2026
b7fa5ff
docs(design): 记录会话日志领域迁移
feigeCode Oct 28, 2026
d393039
refactor(runtime_core): 抽出跨域共用的运行时工具层
feigeCode Oct 28, 2026
7a29ea9
refactor(runtime_database): 抽出数据库域 MCP 工具包
feigeCode Oct 28, 2026
ca1ce5d
docs(design): 记录 runtime_core/runtime_database 拆分
feigeCode Oct 28, 2026
e7d3d48
refactor(runtime_redis,runtime_sftp): 抽出 Redis 与 SFTP 域工具包
feigeCode Oct 28, 2026
0e6ecd7
docs(design): 记录 runtime_redis/runtime_sftp 拆分
feigeCode Oct 28, 2026
4a9c41b
refactor(main): 抽出 navop_app 库并新增 apps/navop 全量包
feigeCode Oct 28, 2026
ac6f696
docs(design): 记录 main lib 化与 apps/navop 全量包
feigeCode Oct 28, 2026
1f19032
chore: 对齐 main 抽 lib 后的构建与文档命令
feigeCode Oct 28, 2026
8480e16
test(packaging): 校验 navop 产品包与主包版本一致
feigeCode Oct 28, 2026
05195c8
feat(navop-shell): 装配可用的 Shell 主页
feigeCode Oct 28, 2026
f1b5dc4
docs(design): 记录 navop-shell 真实主页
feigeCode Oct 28, 2026
d572a14
refactor(domain_api): 端口转发运行时改为可选,供 Data 产品实现
feigeCode Oct 28, 2026
731d972
feat(navop-data): 装配 Database/Redis/MongoDB 主页
feigeCode Oct 28, 2026
d598077
docs(design): 记录 navop-data 主页
feigeCode Oct 28, 2026
205d97f
fix(app_kit): 独立产品启动时初始化应用路径
feigeCode Oct 28, 2026
abe0ddd
docs(design): 记录独立产品路径初始化
feigeCode Oct 28, 2026
406aebe
docs(design): 记录三产品启动冒烟结果
feigeCode Oct 28, 2026
8a30e7a
feat(extension-runtime): 增加 App 级数据库驱动安装流水线
feigeCode Oct 28, 2026
6b1efc0
feat(data_domain): 接管数据库驱动检查与安装提示
feigeCode Oct 28, 2026
b11e432
docs(design): 记录 DriverGate 与数据产品驱动安装
feigeCode Oct 28, 2026
5974b0d
feat(packaging): macOS 应用包按产品生成独立身份
feigeCode Oct 28, 2026
04fdc8c
feat(packaging): 增加 Shell/Data 的 Linux 包资源与独立打包脚本
feigeCode Oct 28, 2026
450a693
docs(design): 记录 macOS/Linux 三产品打包进度
feigeCode Oct 28, 2026
355be3c
ci(packaging): 增加 Shell/Data 的 macOS 与 Linux 构建矩阵
feigeCode Oct 28, 2026
63e7ae0
docs(design): 记录独立产品 CI 矩阵
feigeCode Oct 28, 2026
ed7614d
feat(packaging): 参数化 Shell/Data 的 Windows 安装器身份
feigeCode Oct 28, 2026
ff27a89
docs(design): 记录 Windows 安装器参数化
feigeCode Oct 28, 2026
7e371c6
ci(packaging): 支持按平台筛选与分支构建
feigeCode Oct 28, 2026
2c0ebe0
docs(design): 补充三产品拆分交付状态与已知限制
feigeCode Oct 28, 2026
0a5b4e4
feat(mcp): 增加三产品 Public MCP 宿主与独立发现身份
feigeCode Oct 29, 2026
969ad79
docs(design): 记录 Public MCP 三产品隔离
feigeCode Oct 29, 2026
cbb72d9
refactor(mcp): 下沉 Mongo/Redis live provider 并复用通用审批 UI
feigeCode Oct 29, 2026
037f1e6
docs(design): 记录 MCP 审批复用与 Mongo/Redis 工具下沉
feigeCode Oct 29, 2026
0c958dd
feat(mcp): 为独立产品注册 connection 管理工具集
feigeCode Oct 29, 2026
5badd38
docs(design): 记录 connection 工具集与 MCP 剩余项
feigeCode Oct 29, 2026
0c56a0b
feat(mcp): 工具集开关接入设置并增加逐项 UI
feigeCode Oct 29, 2026
bb6aac7
docs(design): 修正 MCP 工具集提交号
feigeCode Oct 29, 2026
7f4c661
feat(product-split): 独立产品补齐新建连接、基础设置与 Shell 连接类型覆盖
feigeCode Oct 29, 2026
aea8063
refactor(product-split): 三产品统一复用全量主页并按领域能力门控
feigeCode Oct 29, 2026
66bab9b
feat(product-split): 独立产品按领域包写入默认 MCP 工具集
feigeCode Oct 29, 2026
283466a
refactor(product-split): 主页按数据库/终端领域收敛入口
feigeCode Oct 29, 2026
6e074aa
refactor(product-split): 应用各自组装 Product,删除 main 的新建连接向导
feigeCode Oct 29, 2026
edbc7d3
refactor(product-split): HomePage 领域标签页统一走连接注册表
feigeCode Oct 29, 2026
4cd3aa8
docs(design): 记录 main 拆分第三轮进展与剩余路线
feigeCode Oct 29, 2026
4b599ab
refactor(product-split): 数据库/终端设置页下沉到领域包,main 只渲染注册表页面
feigeCode Oct 29, 2026
9124d6f
refactor(product-split): 领域初始化下沉到 Domain::init,公共壳按注册顺序调用
feigeCode Oct 29, 2026
f92a9e3
refactor(product-split): SSH 会话与 RDP 关停下沉到 shell_domain,公共壳按领域钩子退出
feigeCode Oct 29, 2026
72774cc
chore(product-split): 更新 shell_domain 新增 ssh/tracing 依赖后的 Cargo.lock
feigeCode Oct 29, 2026
9eb85af
refactor(product-split): 连接编辑改走通用表单宿主,删除 main 的领域表单构造
feigeCode Oct 29, 2026
a21ea7c
refactor(product-split): main 引入 database/shell 领域 feature 并下沉视觉与驱动资产
feigeCode Oct 29, 2026
664ac32
style: rustfmt 领域 feature 拆分改动
feigeCode Oct 29, 2026
128741f
refactor(product-split): main 引入 database/shell 领域 feature,独立产品按领域裁剪依赖
Oct 29, 2026
f96689b
style: rustfmt 领域 feature 拆分改动
Oct 29, 2026
2c474a4
docs(product-split): 增加三产品内部依赖图与生成脚本
Oct 29, 2026
a2c5cba
refactor(product-split): 本地终端与导航入口改走领域注册表,补 ShellExtras 契约
feigeCode Oct 30, 2026
e1955dc
refactor(product-split): 连接打开策略统一走领域注册表,本地终端与 WSL 能力下沉
feigeCode Oct 30, 2026
297ba53
refactor(product-split): 导入编辑器与工作台终端改走通用契约,main 去掉终端/表单领域依赖
feigeCode Oct 30, 2026
55ebda9
refactor(product-split): 端口转发运行时改由 shell 领域持有,主页通过 ShellExtras 获取
feigeCode Oct 30, 2026
3086cd6
refactor(product-split): 远程桌面窗口下沉 shell 领域,补齐领域钩子与三态编译
feigeCode Oct 30, 2026
46bf40d
refactor(product-split): 文件打开/标签复制/临时连接/键位改走领域契约,main 只余 3 处 cfg 领域引用
feigeCode Oct 30, 2026
012d5fe
refactor(product-split): 侧栏终端配色改由 ShellExtras 提供,main 只剩 2 处领域引用
feigeCode Oct 30, 2026
dcc4acd
refactor(product-split): 终端主题设置与活动资源改由领域提供,main 不再 use 领域 crate
feigeCode Oct 30, 2026
b0dc6b3
refactor(product-split): 裁剪 main 的领域依赖并加依赖隔离契约测试
feigeCode Oct 30, 2026
9a32158
test(release): 对齐目标包默认 feature 与 update 模块新路径
feigeCode Oct 30, 2026
cbfcf7f
fix(product-split): 修 Linux/Windows CI 编译失败
feigeCode Oct 30, 2026
6bcc309
fix(core): 无系统凭据库的平台上 KeyringServices 字段不再被读
feigeCode Oct 30, 2026
af3d3dc
fix(shell_domain): 补 Windows 凭据管理器依赖
feigeCode Oct 30, 2026
005d42f
chore(deps): 更新 Cargo.lock(shell_domain 新增 windows 依赖边)
feigeCode Oct 30, 2026
ce374cc
fix(main): Windows 单实例失败提示改用 &str 入参
feigeCode Oct 30, 2026
83adeb1
test(main): 对齐单实例失败提示的源码契约断言
feigeCode Oct 30, 2026
85d9b02
test(main): 增加独立产品依赖树隔离契约
feigeCode Oct 30, 2026
1b05537
Merge pull request #489 from feigeCode/refactor/product-split
feigeCode Oct 30, 2026
9e623eb
refactor(product-split): 产品装配移出 main,main 收敛为纯应用外壳
feigeCode Oct 30, 2026
e147f19
refactor(product-split): Mongo 原生驱动工厂下沉到 DataDomain
feigeCode Oct 30, 2026
2260fcc
refactor(product-split): 数据领域工具集改由领域契约提供,main 不再依赖 data_domain/mongod…
feigeCode Oct 31, 2026
579da78
feat(product-split): 托盘图标按产品加角标、退出提示带产品名
feigeCode Oct 31, 2026
e982a93
fix(redis): 键名含非法 UTF-8 时列表加载、读取与删除不再失败(#485)
feigeCode Oct 31, 2026
e98d181
fix(db_view): 复制为 UPDATE/DELETE 在选区不含主键时不再输出空字符串
feigeCode Oct 31, 2026
b3ef361
Merge remote-tracking branch 'origin/main' into dev
feigeCode Oct 31, 2026
806c307
Merge remote-tracking branch 'origin/dev' into dev
feigeCode Oct 31, 2026
87a6c0b
feat(update): 新增 beta 测试通道,测试版与稳定版分流
feigeCode Oct 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
46 changes: 45 additions & 1 deletion .github/RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,50 @@ A release pull request keeps the fast path after `main` is merged back into `dev
- `main` is the stable release branch and is protected: changes land via pull request, and CI must pass before merging.
- Cut a release by merging the validated work from `dev` into `main` — including the `main/Cargo.toml` / `Cargo.lock` version bump — then creating the `v*` tag on `main` with `script/release-tag.sh`. The version bump must ride the pull request because direct pushes to `main` are rejected (GH006); only the tag is pushed directly.

## Beta 发布通道

beta 通道把测试版与稳定版的更新清单彻底分开:稳定通道继续读写 `updates/latest.json`(老客户端唯一认识的地址),beta 只写 `updates/beta/latest.json`。beta tag 仍然是完整的 GitHub Release,只是会被显式标记为 prerelease。

> 首次使用前的前置条件:`release-trigger.yml` 用 `--ref main` 取 workflow 定义,所以 channel/prerelease 的改动必须已合入 `main`。否则跑的是 main 上的旧版 `release.yml`——不带 `--prerelease`、也不传 `channel`。

### 版本线约定

- beta 的 base 永远是**下一个稳定版**:`0.23.0-beta.1 → 0.23.0`。beta 稳定后就地转正,版本号去掉 `-beta.N`。
- 不要给已发布的稳定版开补丁 beta 线(例如 `0.22.1-beta`):那会让老客户端把测试版当成同一版本线的修复更新。
- 同一 base 的 beta 依次递增:`0.23.0-beta.1`、`0.23.0-beta.2`……每个 beta 都是独立 tag、独立 Release、独立安装包前缀 `releases/<tag>/`。

### 一条命令发 beta

beta 版本线走 `dev` 分支,前置条件是工作区干净、`dev` 已同步。脚本会改 `main/Cargo.toml` 与 `Cargo.lock`、写入 `CHANGELOG.md` 双语条目、提交、打 tag,并推送 `dev` 与 tag:

```bash
script/beta-release.sh --base 0.23.0 --notes-file /private/tmp/navop-v0.23.0-beta.1.md
script/beta-release.sh --next --notes-file /private/tmp/navop-v0.23.0-beta.2.md
```

- `--base X.Y.Z`:把版本设为 `X.Y.Z-beta.1`。
- `--next`:要求当前版本是 `X.Y.Z-beta.N`,递增为 `beta.N+1`。
- `--notes-file`:必填,内容规则与正常发布相同(中文 `更新内容` / `修复与优化` + 英文 `What's New` / `Fixes and Improvements` + CNB 镜像行);`release.yml` 会硬校验 `CHANGELOG.md` 里有 `## [v<tag>] - YYYY-MM-DD` 条目。
- `--dry-run`:只打印将要执行的步骤与最终 tag,不写任何文件、不提交、不打 tag、不推送。

### 发一个 beta 会发生什么

1. `git push cnb <tag>` 触发 `Release Trigger`。
2. `Release` 始终取 `main` 分支上的 workflow 定义运行,`prepare` job 按 tag 推导出 `channel=beta`。
3. 构建完成后创建/更新 GitHub Release,并追加 `--prerelease`。必须如此:否则 GitHub 的 `/releases/latest` 会把 beta 当成最新稳定版推给所有老客户端。
4. 派发 `upload-r2.yml` 时显式带 `-f channel=beta`:安装包照常上传到 `releases/<tag>/`,更新清单写到 `updates/beta/latest.json`。
5. 稳定清单 `updates/latest.json` 完全不动,所有走稳定通道的现有用户不受影响。

### 转正(promotion)

beta 验证稳定后,按正常发布流程把 `dev` 合进 `main`:在 release PR 里把 `main/Cargo.toml` / `Cargo.lock` 的版本从 `X.Y.Z-beta.N` 改成 `X.Y.Z`,整理 `CHANGELOG.md` 中该版本的条目,合并后在 `main` 上执行 `script/release-tag.sh vX.Y.Z`。这条稳定 tag 会把同一版本写回 `updates/latest.json`,beta 清单留在原地作为历史。

### 回滚

- beta 发布失败(tag 打错、changelog 缺条目):`git tag -d vX.Y.Z-beta.N`、`git push cnb :refs/tags/vX.Y.Z-beta.N`,必要时 `git reset --hard HEAD~1`。beta 不影响稳定清单,通常无须额外回滚。
- 万一稳定清单被污染(例如误用 beta tag 跑了 stable 通道):用一个已知正确的稳定 tag 重跑 `upload-r2.yml`(`-f tag=vX.Y.Z -f channel=stable`),脚本会幂等重建 `updates/latest.json` 与 `releases/<tag>/` 下的安装包。
- `node script/test-release-packaging.mjs` 里有一组回归断言(含假 aws 仿真),保证 beta tag 永远只写 `updates/beta/latest.json`。

## Changelog format

The newest entry belongs immediately after the `<!-- NAVOP_RELEASES -->` marker:
Expand Down Expand Up @@ -118,6 +162,6 @@ For a failed matrix job in the same workflow run, prefer **Re-run failed jobs**.
- A new release tag must contain a valid bilingual `CHANGELOG.md` entry before builds begin.
- GitHub Release notes and R2 updater `release_notes` are extracted from the same tagged changelog entry.
- Publishing uses `--clobber` only for newly built platform files and `sha256sums.txt`.
- R2 keeps only the newest release. The prune step runs after the upload has been verified object by object and never carries `always()`, so a failed upload leaves the existing objects alone; it refuses to delete anything when its own tag is missing from R2, and a non-stable tag (for example `v0.20.0-rc.1`) skips pruning so it cannot take the last stable installers with it.
- R2 keeps the newest stable release plus the beta installer that `updates/beta/latest.json` still points at, so beta-channel downloads keep working after a stable release. The prune step runs after the upload has been verified object by object and never carries `always()`, so a failed upload leaves the existing objects alone; it refuses to delete anything when its own tag is missing from R2, and a non-stable tag (for example `v0.20.0-rc.1`) skips pruning so it cannot take the last stable installers with it. Objects under `updates/` are never deleted.
- Legacy pre-changelog repairs preserve their existing GitHub Release body.
- All five primary platform builds belong to one matrix, so they start in parallel and a failed job can be rerun without rebuilding successful matrix jobs.
27 changes: 23 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ jobs:
tag: ${{ steps.release.outputs.tag }}
platform: ${{ steps.release.outputs.platform }}
build_mode: ${{ steps.release.outputs.build_mode }}
channel: ${{ steps.release.outputs.channel }}

steps:
- name: Validate release request
Expand All @@ -70,6 +71,9 @@ jobs:
echo "::error::Invalid release tag: $RELEASE_TAG"
exit 1
fi
# 发布通道:纯稳定版本号(vX.Y.Z)走 stable,其余(vX.Y.Z-beta.N /
# -rc.N 等预发布)走 beta。与 upload-r2.yml 里的推导规则保持一字不差。
if [[ "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then CHANNEL=stable; else CHANNEL=beta; fi
gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${RELEASE_TAG}" >/dev/null
if [ "$RELEASE_PLATFORM" != "all" ]; then
gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null
Expand All @@ -78,7 +82,9 @@ jobs:
echo "tag=$RELEASE_TAG"
echo "platform=$RELEASE_PLATFORM"
echo "build_mode=$BUILD_MODE"
echo "channel=$CHANNEL"
} >> "$GITHUB_OUTPUT"
echo "Release channel: $CHANNEL"

- name: Check out release tag
uses: actions/checkout@v7
Expand Down Expand Up @@ -353,10 +359,10 @@ jobs:
# is not worth a second shape to test and release.
cargo zigbuild \
--release \
-p main \
-p navop \
--target "${{ matrix.target }}.2.28"
else
cargo build --release -p main --target "${{ matrix.target }}"
cargo build --release -p navop --target "${{ matrix.target }}"
fi
test -x "target/${{ matrix.target }}/release/${{ matrix.binary }}"
# 体积回归在日志里可见(fat LTO + panic=abort 后约 107MiB)
Expand Down Expand Up @@ -417,7 +423,7 @@ jobs:
}
cargo build `
--release `
-p main `
-p navop `
@features `
--target $target

Expand Down Expand Up @@ -868,18 +874,28 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.prepare.outputs.tag }}
CHANNEL: ${{ needs.prepare.outputs.channel }}
run: |
set -euo pipefail
# 只有 beta 通道把 Release 标成 prerelease:稳定版修复时误标会让
# /releases/latest 跳过它,老客户端(只认该端点)就再也收不到更新。
# 空数组在 `set -u` 下的展开要显式兜底,见 ${arr[@]+"${arr[@]}"}.
prerelease_args=()
if [ "$CHANNEL" = "beta" ]; then
prerelease_args+=(--prerelease)
fi
if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
gh release edit "$RELEASE_TAG" \
--repo "$GITHUB_REPOSITORY" \
--title "Navop $RELEASE_TAG" \
${prerelease_args[@]+"${prerelease_args[@]}"} \
--notes-file release-notes.md
else
gh release create "$RELEASE_TAG" \
--repo "$GITHUB_REPOSITORY" \
--verify-tag \
--title "Navop $RELEASE_TAG" \
${prerelease_args[@]+"${prerelease_args[@]}"} \
--notes-file release-notes.md
fi

Expand All @@ -898,10 +914,13 @@ jobs:
# Dispatch R2 explicitly after the Release assets are present.
# Relying only on workflow_run is not reliable when this workflow
# itself is dispatched by release-trigger.yml.
# channel 必须显式传下去:beta 发布只能写 updates/beta/latest.json,
# 绝不能覆盖稳定清单 updates/latest.json。
gh workflow run upload-r2.yml \
--repo "$GITHUB_REPOSITORY" \
--ref main \
-f tag="$RELEASE_TAG"
-f tag="$RELEASE_TAG" \
-f channel="$CHANNEL"

gh workflow run sync-cnb-release-assets.yml \
--repo "$GITHUB_REPOSITORY" \
Expand Down
169 changes: 169 additions & 0 deletions .github/workflows/standalone-products.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
name: Standalone Products

on:
workflow_dispatch:
inputs:
tag:
description: "Tag to build, for example v0.22.0"
required: true
type: string
platform:
description: "Build one matrix label or all"
required: true
type: choice
default: all
options:
- all
- macos-arm64
- macos-x64
- linux-x64
- windows-x64
ref:
description: "Optional branch or SHA to build instead of the tag"
required: false
type: string

permissions:
contents: read

concurrency:
group: standalone-products-${{ inputs.tag }}
cancel-in-progress: false

env:
CARGO_TERM_COLOR: always

jobs:
prepare:
name: Prepare standalone build
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
version: ${{ steps.matrix.outputs.version }}
steps:
- name: Validate tag
env:
TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Invalid tag: $TAG"
exit 1
fi

- name: Build matrix
id: matrix
env:
TAG: ${{ inputs.tag }}
PLATFORM: ${{ inputs.platform }}
run: |
set -euo pipefail
version="${TAG#v}"
entries=(
'{"product":"navop-shell","label":"macos-arm64","os":"macos-latest","target":"aarch64-apple-darwin","app":"Navop Shell.app"}'
'{"product":"navop-shell","label":"macos-x64","os":"macos-15-intel","target":"x86_64-apple-darwin","app":"Navop Shell.app"}'
'{"product":"navop-shell","label":"linux-x64","os":"ubuntu-latest","target":"x86_64-unknown-linux-gnu","app":""}'
'{"product":"navop-shell","label":"windows-x64","os":"windows-latest","target":"x86_64-pc-windows-msvc","app":""}'
'{"product":"navop-data","label":"macos-arm64","os":"macos-latest","target":"aarch64-apple-darwin","app":"Navop Data.app"}'
'{"product":"navop-data","label":"macos-x64","os":"macos-15-intel","target":"x86_64-apple-darwin","app":"Navop Data.app"}'
'{"product":"navop-data","label":"linux-x64","os":"ubuntu-latest","target":"x86_64-unknown-linux-gnu","app":""}'
'{"product":"navop-data","label":"windows-x64","os":"windows-latest","target":"x86_64-pc-windows-msvc","app":""}'
)
selected=()
for entry in "${entries[@]}"; do
if [ "$PLATFORM" = "all" ] || [[ "$entry" == *"\"label\":\"${PLATFORM}\""* ]]; then
selected+=("$entry")
fi
done
if [ "${#selected[@]}" -eq 0 ]; then
echo "::error::No matrix entries for platform: $PLATFORM"
exit 1
fi
matrix="{\"include\":[$(IFS=,; echo "${selected[*]}")]}"
python3 -c 'import json,sys; json.loads(sys.argv[1])' "$matrix"
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"

build:
name: Build (${{ matrix.product }}, ${{ matrix.label }})
needs: prepare
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}
runs-on: ${{ matrix.os }}
timeout-minutes: 180
env:
ONETCLI_VERSION: ${{ needs.prepare.outputs.version }}

steps:
- name: Check out source
uses: actions/checkout@v7
with:
ref: ${{ inputs.ref || inputs.tag }}

- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
target: ${{ matrix.target }}
cache: false

- name: Install system dependencies
if: runner.os != 'Windows'
run: script/bootstrap

- name: Install Windows build dependencies
if: runner.os == 'Windows'
shell: pwsh
run: |
choco install nasm --no-progress --yes
$nasmDir = Join-Path $env:ProgramFiles "NASM"
$nasmDir | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
nasm -v

- name: Build product
run: cargo build --release -p "${{ matrix.product }}" --target "${{ matrix.target }}"

- name: Package macOS app
if: runner.os == 'macOS'
run: |
set -euo pipefail
chmod +x script/bundle-macos.sh script/bundle-macos-dmg.sh
script/bundle-macos.sh "${{ matrix.target }}" "${{ matrix.product }}"
script/bundle-macos-dmg.sh "${{ matrix.target }}" "${{ matrix.product }}"
tar -czf "target/${{ matrix.product }}-${ONETCLI_VERSION}-${{ matrix.target }}.tar.gz" \
-C target "${{ matrix.app }}"

- name: Package Linux tarball
if: runner.os == 'Linux'
run: |
set -euo pipefail
script/package-linux-product.sh "${{ matrix.product }}" "${{ matrix.target }}"

- name: Package Windows installer
if: runner.os == 'Windows'
shell: pwsh
run: |
node script/generate-windows-license.mjs
dotnet tool install --global wix --version 6.0.2
wix extension add -g WixToolset.UI.wixext/6.0.2
wix extension add -g WixToolset.BootstrapperApplications.wixext/6.0.2
New-Item -ItemType Directory -Force -Path package | Out-Null
Copy-Item "target/${{ matrix.target }}/release/${{ matrix.product }}.exe" "package/${{ matrix.product }}.exe"
./installer/windows/build-product-installer.ps1 `
-Product "${{ matrix.product }}" `
-Version "$env:ONETCLI_VERSION" `
-SourceDir package `
-OutDir dist `
-Arch x64

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.product }}-${{ matrix.label }}
if-no-files-found: error
path: |
target/*.dmg
target/${{ matrix.product }}-${{ matrix.target }}*.tar.gz
target/${{ matrix.product }}-*${{ matrix.target }}*.tar.gz
dist/*.msi
dist/*.exe
Loading
Loading