跳转到主要内容
本文说明如何自行构建 GPROXY、各发布包包含什么、CI 对每次改动执行哪些检查,以及版本的发布和签名方式。如果只是想运行 GPROXY,直接从 Releases 下载预构建包更简单,选哪个包见发布包中的表格。

发布包

每个版本包含三个程序,各自提供便携包和安装包:
  • gproxy-*:CLI,内置 Web 控制台。
  • gproxy-headless-*:不带控制台的同一个 CLI,用于 CI 和小型服务器,见无前端 CLI。
  • gproxy-tauri-*:桌面与移动端应用,同样内嵌控制台。
发布工作流还会发布 serverless 可执行文件(gproxy-serverless-linux-<arch>-musl,x86_64 和 aarch64)、Cloudflare Workers 包(gproxy-edge-cloudflare.zip),以及 GNU 和 musl 容器镜像。应用包会生成托管在 GitHub 上的构建证明(attestation)。公开的发布附件只包括各个包、manifest.json 和 SHA256SUMS;单独的校验文件和构建证明属于 CI 内部输入。 各平台说明:
  • Linux:CLI DEB 把 gproxy 安装到 /usr/bin。GNU Linux 应用依赖发行版的 GTK 3 和 WebKitGTK 4.1。musl 构建面向 Alpine,只提供 APK,不提供 DEB。
  • Android CLI:Termux DEB 安装到 /data/data/com.termux/files/usr。Android CLI 使用 distribution/termux/ 中的源码配方和固定版本的 Termux 官方构建器构建。DEB 和 ZIP 都依赖 Termux 的 libc++、OpenSSL 和 CA 证书;ZIP 包含启动脚本、可执行文件以及记录安装和更新方法的 TERMUX.txt。这些构建禁用了自更新:用 APT 安装新版 DEB,或在已启用的软件仓库收录后执行 pkg upgrade gproxy。
  • Android 应用:APK 使用 ANDROID_SIGNING_* secrets 签名并验证。每个应用的 <target-triple>-tauri-apk 条目写入 Ed25519 签名的更新清单,与 CLI ZIP 的更新分开。旧的服务包装 APK 不再构建。
  • Windows:CLI MSIX 使用 Partner Center 分配的独立包身份 LeenHawk.GPROXYCLI,并提供控制台命令别名。应用依赖系统的 WebView2 Runtime。
  • macOS:CLI DMG 包含可执行文件和在终端中安装的说明。应用 ZIP 保留桌面资源,macOS 版包含完整的 .app。构建只做 ad-hoc 签名,尚未接入 Developer ID 签名和公证。
  • 鸿蒙:使用缓存的工具链镜像和固定版本的实验性 Tauri 分支构建,其他平台使用稳定版 Tauri。HAP 安装前需要自行签名,已在模拟器上验证。已实现持续后台运行,但能否生效取决于设备、系统版本和授予的权限;应用进程退出后代理随之停止。
nightly 附件的文件名固定不变,提交 SHA 记录在更新清单中。

构建环境

需要 stable Rust(edition 2024)、Go、Clang、Node.js 22.12+(推荐 24 LTS)和 pnpm。Linux 桌面构建还需要 webkit2gtk-4.1、gtk+-3.0、libsoup-3.0 开发包。其他平台的工具链由发布工作流配置。

CLI 与控制台

在仓库根目录执行:
pnpm --dir console build 会把构建好的控制台复制到 HTTP 宿主和 Tauri 宿主的嵌入目录,之后的 Rust 构建会把它编译进去。顺序不能颠倒:只执行 Rust 构建的全新检出没有控制台资源,/console 会返回 404。如果要使用单独构建的控制台,传入 --console-path console/dist。

Cargo feature

gproxy crate 的默认 feature 集合是 channels,memory,fs,bundled-vocabulary,embedded-console,postgres,mysql。 只带四个渠道、不带外部数据库驱动的精简构建:
该 crate 还有一个面向 serverless 平台的 gproxy-serverless 二进制,需要启用 postgres feature。

无前端 CLI

无前端 CLI 就是不启用 embedded-console 构建的 gproxy 二进制。它保留代理路由、管理 API、全部渠道、SQLite、PostgreSQL 和 MySQL 驱动、内存缓存、本地文件存储和内置词表,通过 CLI 或管理 API 配置。构建不需要 Node.js、pnpm 或桌面库:
不启用 embedded-console 时,即使检出目录中已有构建好的控制台,也不会嵌入二进制。--console=false 还会关闭对外部控制台目录的服务。自定义 feature 集合如果需要内嵌控制台,加上 embedded-console。 发布工作流独立于控制台构建生成以下无前端包: 从 Releases 下载:开发通道选 nightly,beta 选 staging,稳定版选对应版本。无前端安装的自更新会选择对应的无前端包。 在 Android 的 Termux 中安装并启动:
无前端包与完整版 gproxy 包提供同一个命令,安装时会替换后者。如果使用 ZIP,先安装运行库,再把 ZIP 解压到 Termux 主目录下的某个目录(Android 共享存储中的文件不能执行),然后在该目录中运行:
Windows 上用 gproxy.exe 代替 gproxy。Windows MSIX 的包身份为 LeenHawk.GPROXYHeadless,命令别名为 gproxy-headless.exe,不会替换 CLI 的 gproxy.exe。GPROXY Headless 也已上架 Microsoft Store(9ND7FQQMV566)。

桌面与移动端应用

构建控制台后执行:
全新实例会打开设置向导。窗口通过 IPC 管理实例,HTTP 监听端口供网关客户端调用。移动端构建请使用发布工作流中的平台工具链和打包脚本。 workspace 的默认成员不包含 Tauri 宿主,因此构建后端不需要桌面依赖;--workspace 会把它包含进来。 要在本地生成发布包,先构建控制台,再运行打包脚本。pnpm --dir console build 已经会执行 sync-to-embed.mjs,再执行一次也没有影响。
产物写入 dist/release/。

Cloudflare Workers

在仓库根目录执行:
worker-build 负责编译和优化 WebAssembly 模块;check 用 Wrangler 做一次部署预演,不会发布。配置和运行限制见边缘部署;构建带自有渠道的 Worker 见新增渠道。

CI 执行的检查

提交 PR 前请先在本地运行。CI 对每次改动都会执行,分为后端、桌面和 WebAssembly 三个任务。 后端(workspace 默认成员):
CI 还会对不属于默认成员的 serverless 二进制和仓库外渠道示例执行 lint:
桌面宿主:
WebAssembly:检查只覆盖支持该目标的库和宿主,不包括 CLI 和 Tauri。不要对整个 workspace 执行;这两者都会用到 tokio 的 net feature,而 mio 无法在该目标上编译。
CI 还会检查各 crate README 中注明可编译到 wasm 的非默认 feature 集合。由于 cargo 会在同一次调用内合并 feature,每个集合单独调用一次:
控制台与文档:
pnpm --dir console lint 包含翻译一致性检查(pnpm i18n:check)。修改 Rust DTO 后,执行 pnpm --dir console types 重新生成 TypeScript 类型。

发布

分支与更新通道

Release 工作流(.github/workflows/release.yml)在推送时发布:
  • dev 开发新功能,推送后只发布到 nightly(dev 更新通道)。
  • main 维护已发布的功能并修复 bug,推送后只发布到 staging(beta 更新通道)。
  • 正式版本 tag 从 main 创建,同时发布到 release 和 beta 通道,不会覆盖 dev 通道。
推送 dev 前执行 bash scripts/push-dev.sh。脚本会获取最新的 origin/main,把 dev rebase 到它上面,再以针对上一次已发布 dev 的显式 --force-with-lease 推送。用 git config core.hooksPath .githooks 启用本地推送检查。

发布版本

在最新的 main 之上执行 bash scripts/release.sh,其间只允许新增 chore(release): 提交。以下条件不满足时脚本会停止:
  • tag 与 workspace 版本一致,且存在 release-notes/v<版本>.md;
  • 已跟踪的工作区是干净的;
  • HEAD 包含最新的 origin/main,且其后的提交都是 chore(release): 提交。
对于正式版,脚本接着刷新依赖:用 pnpm update --latest 更新控制台的 npm 依赖,用 cargo upgrade 升级 Rust 注册表依赖约束(包括大版本和固定版本),再用 cargo update 刷新 Cargo.lock。RSA/getrandom、JNI/Tauri、Codex reqwest 和 WebSocket 配置的兼容桥接依赖保留上游要求的版本线,但兼容版本仍会通过 cargo update 更新。需要先安装辅助工具:cargo install cargo-edit --locked。如果刷新改动了已跟踪文件,脚本会在打 tag 之前停止:处理兼容性变更,执行上面的检查,提交并推送到 main,然后重新运行脚本。预发布 tag 不执行依赖刷新。 随后脚本创建 tag,并把 main 和 tag 在一次原子推送中推上去。tag 触发的工作流发布 release 和 beta 通道,main 在该 tag 提交上的运行不发布任何内容。完成后用 scripts/push-dev.sh 推送 dev。 工作流负责构建各个包、生成签名的更新清单并上传附件。构建或签名任一步失败,发布就不完整。 版本 tag 还会执行 scripts/publish-crates.sh,把 MIT 许可的库发布到 crates.io。其他 crate 通过 git 或路径依赖使用,见嵌入核心库。

签名与商店提交

缺少签名密钥或 Store 身份变量时,打包会失败。
  • 更新清单:使用 Ed25519 签名,自更新在安装前校验。
  • Android:APK 使用 ANDROID_SIGNING_* secrets 签名。
  • macOS:只做 ad-hoc 签名。
  • Windows:各包使用 Microsoft Store 的包身份。在 release 环境中配置四个变量:MS_STORE_IDENTITY_NAME、MS_STORE_DISPLAY_NAME、MS_STORE_IDENTITY_PUBLISHER、MS_STORE_PUBLISHER_DISPLAY_NAME;CLI 使用对应的 MS_STORE_CLI_* 变量。Headless 的包身份和显示名固定,Store 提交只需在共用的发布者变量之外再配置 MS_STORE_HEADLESS_PRODUCT_ID。正式版保留两种架构的 Store 提交包;设置 MS_STORE_PUBLISH_ENABLED 后,GitHub 发布成功会触发 Store 提交流程。Store 提交包不签名,由 Store 在分发时签名。
  • GitHub 上的 Windows 包:发布包不做代码签名。版本附带的 ZIP 和 MSIX 都没有签名,Windows 运行前可能会弹出警告,MSIX 需要开启旁加载才能安装。签名版本只能从 Microsoft Store 获取,也可以用 winget install --source msstore 安装。WinGet 社区源只接收已签名的 MSIX,发布包不签名期间不会有新版本。