chore(build): complete unsigned macOS packaging

This commit is contained in:
QILIN YOU
2026-05-02 22:53:06 +08:00
parent a722d897b4
commit 039cccd390
10 changed files with 243 additions and 331 deletions
+63
View File
@@ -0,0 +1,63 @@
name: Publish macOS Packages
on:
workflow_dispatch:
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: macos-15-intel
- arch: arm64
runner: macos-15
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install Wails CLI
run: go install github.com/wailsapp/wails/v2/cmd/wails@v2.11.0
- name: Add Go bin to PATH
run: echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
- name: Build unsigned macOS package
run: bash publish/mac/publish-mac.sh --arch ${{ matrix.arch }}
- name: Validate app bundle
run: |
set -euo pipefail
app_path="$(ls -1d publish/output/AntBrowser-*-macos-${{ matrix.arch }}.app | head -n 1)"
zip_path="$(ls -1 publish/output/AntBrowser-*-macos-${{ matrix.arch }}.zip | head -n 1)"
echo "Validating: $app_path"
test -x "$app_path/Contents/MacOS/ant-chrome"
test -x "$app_path/Contents/MacOS/bin/xray"
test -x "$app_path/Contents/MacOS/bin/sing-box"
plutil -lint "$app_path/Contents/Info.plist"
ditto -x -k "$zip_path" /tmp/ant-browser-macos-validate
test -x "/tmp/ant-browser-macos-validate/$(basename "$app_path")/Contents/MacOS/ant-chrome"
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ant-browser-macos-${{ matrix.arch }}
path: |
publish/output/AntBrowser-*-macos-${{ matrix.arch }}.app
publish/output/AntBrowser-*-macos-${{ matrix.arch }}.zip
+6
View File
@@ -46,6 +46,12 @@ chrome/*
!/bin/linux-arm64/
!/bin/linux-arm64/xray
!/bin/linux-arm64/sing-box
!/bin/darwin-amd64/
!/bin/darwin-amd64/xray
!/bin/darwin-amd64/sing-box
!/bin/darwin-arm64/
!/bin/darwin-arm64/xray
!/bin/darwin-arm64/sing-box
# Publish artifacts
publish/staging/
+1
View File
@@ -3,5 +3,6 @@
## 1.1.0 - 2026-03-19
- 完善 Linux 支持:补齐 Linux 环境下的开发、打包、安装、启动与运行链路,并持续修复安装版启动与退出稳定性问题。
- 补齐 macOS unsigned 内测支持:新增原生 macOS `.app` / `.zip` 打包、Darwin 运行时校验、Application Support 用户状态目录和 macOS 发布工作流。
- 新增 SOCKS 代理测试支持:SOCKS 代理能力已进入测试阶段,后续会继续验证稳定性与兼容性。
- 实验性支持接口触发浏览器:支持通过接口启动浏览器实例,便于后续接入自动化流程。
+18 -4
View File
@@ -1,9 +1,9 @@
# Ant Browser
> 面向多账号隔离、代理绑定和本地环境管理的桌面浏览器工具(Windows / Linux)。
> 面向多账号隔离、代理绑定和本地环境管理的桌面浏览器工具(Windows / Linux / macOS unsigned)。
[![Release](https://img.shields.io/github/v/release/black-ant/Ant-Browser?sort=semver)](https://github.com/black-ant/Ant-Browser/releases)
[![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Linux-blue)](https://github.com/black-ant/Ant-Browser/releases)
[![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Linux%20%7C%20macOS-blue)](https://github.com/black-ant/Ant-Browser/releases)
[![Issues](https://img.shields.io/github/issues/black-ant/Ant-Browser)](https://github.com/black-ant/Ant-Browser/issues)
## 推荐内核项目
@@ -54,6 +54,7 @@ Ant Browser 适合以下场景:
### 1.1.0 · 2026-03-19
- 完善 Linux 支持:补齐 Linux 环境下的开发、打包、安装、启动与运行链路,并持续修复安装版启动与退出稳定性问题
- 补齐 macOS unsigned 内测构建链路:支持在原生 macOS 主机上打包 `.app` / `.zip`,并将用户状态目录放到 `~/Library/Application Support/ant-browser`
- 新增 SOCKS 代理测试支持:SOCKS 代理能力已进入测试阶段,后续会继续验证稳定性与兼容性
- 实验性支持接口触发浏览器:支持通过接口启动浏览器实例,便于后续接入自动化流程
@@ -126,6 +127,7 @@ Ant Browser 适合以下场景:
- 操作系统:
- Windows 10 / 1164 位)
- Linuxamd64 / arm64
- macOSamd64 / arm64,当前为 unsigned 内测包)
- 建议内存:8 GB 及以上
- 建议磁盘空间:2 GB 以上
@@ -135,15 +137,16 @@ Ant Browser 适合以下场景:
2. 安装版直接运行 `AntBrowser-Setup-*.exe`
3. 便携版解压后运行 `ant-chrome.exe`
4. Linux 包下载后可直接安装 `ant-browser_<version>_<arch>.deb`,或解压 `tar.gz` 后运行 `ant-chrome`
5. macOS unsigned 包解压后运行 `AntBrowser-<version>-macos-<arch>.app`;如被 Gatekeeper 拦截,请对本机测试包执行 `xattr -dr com.apple.quarantine <app路径>` 后再打开
### 从源码运行
1. 开发默认使用 `master` 分支;该分支不带测试用户数据,适合作为日常开发基线。
2. 如需带测试库的演示环境,请切换到 `user_data` 分支。
3. Windows 统一执行 `bat\dev.bat`;默认是稳定模式,如需前端 HMR 联调使用 `bat\dev.bat live`,如需受限内存复现使用 `bat\dev.bat limited`
4. Windows 运行时使用 `bin/xray.exe``bin/sing-box.exe`Linux 运行时使用 `bin/linux-<arch>/xray``bin/linux-<arch>/sing-box`
4. Windows 运行时使用 `bin/xray.exe``bin/sing-box.exe`Linux 运行时使用 `bin/linux-<arch>/xray``bin/linux-<arch>/sing-box`macOS 运行时使用 `bin/darwin-<arch>/xray``bin/darwin-<arch>/sing-box`
5. 运行时文件采用“仓库固定 + 哈希校验”,校验清单在 `publish/runtime-manifest.json`,固定来源清单在 `publish/runtime-sources.json`
6. 如需刷新 Linux 运行时,执行 `python3 tools/runtime/sync-runtime.py`(会按固定来源下载、校验归档并更新 manifest)。
6. 如需刷新 Linux / macOS 运行时,执行 `python3 tools/runtime/sync-runtime.py --target <target>`(会按固定来源下载、校验归档并更新 manifest)。
开发模式说明:
@@ -163,6 +166,17 @@ bash publish/linux/publish-linux.sh --arch arm64
详细说明见 [publish/linux/README.md](publish/linux/README.md)。
### macOS unsigned 发布打包(源码)
macOS 发布脚本位于 `publish/mac/`,必须在原生 macOS 主机上执行,且目标架构需与主机架构一致。
```bash
bash publish/mac/publish-mac.sh --arch amd64
bash publish/mac/publish-mac.sh --arch arm64
```
脚本会生成 unsigned `.app``.zip`,适合 PR 验证与内部测试。详细说明见 [publish/mac/README.md](publish/mac/README.md)。
### 准备浏览器内核
代理运行时已经随仓库提供,你只需要准备浏览器内核。
+13 -1
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
goruntime "runtime"
"strings"
"gopkg.in/yaml.v3"
@@ -406,7 +407,7 @@ func DefaultConfig() *Config {
},
Browser: BrowserConfig{
UserDataRoot: "data",
DefaultFingerprintArgs: []string{"--fingerprint-brand=Chrome", "--fingerprint-platform=windows"},
DefaultFingerprintArgs: defaultFingerprintArgsForOS(goruntime.GOOS),
DefaultLaunchArgs: []string{"--disable-sync", "--no-first-run"},
DefaultProxy: "",
StartReadyTimeoutMs: 3000,
@@ -445,6 +446,17 @@ func DefaultConfig() *Config {
}
}
func defaultFingerprintArgsForOS(goos string) []string {
platform := "windows"
switch strings.ToLower(strings.TrimSpace(goos)) {
case "darwin":
platform = "mac"
case "linux":
platform = "linux"
}
return []string{"--fingerprint-brand=Chrome", "--fingerprint-platform=" + platform}
}
// Save 保存配置到文件
func (c *Config) Save(configPath string) error {
data, err := yaml.Marshal(c)
+37
View File
@@ -3,6 +3,7 @@ package config
import (
"os"
"path/filepath"
"runtime"
"testing"
)
@@ -74,6 +75,42 @@ browser: {}
}
}
func TestDefaultFingerprintArgsForOS(t *testing.T) {
t.Parallel()
tests := map[string]string{
"windows": "--fingerprint-platform=windows",
"linux": "--fingerprint-platform=linux",
"darwin": "--fingerprint-platform=mac",
"freebsd": "--fingerprint-platform=windows",
}
for goos, want := range tests {
got := defaultFingerprintArgsForOS(goos)
if len(got) != 2 {
t.Fatalf("%s: unexpected args length: got=%v", goos, got)
}
if got[1] != want {
t.Fatalf("%s: platform arg mismatch: got=%q want=%q", goos, got[1], want)
}
}
}
func TestDefaultConfigUsesCurrentOSFingerprintPlatform(t *testing.T) {
t.Parallel()
cfg := DefaultConfig()
want := defaultFingerprintArgsForOS(runtime.GOOS)
if len(cfg.Browser.DefaultFingerprintArgs) != len(want) {
t.Fatalf("默认指纹参数数量不符: got=%v want=%v", cfg.Browser.DefaultFingerprintArgs, want)
}
for i := range want {
if cfg.Browser.DefaultFingerprintArgs[i] != want[i] {
t.Fatalf("默认指纹参数不符: got=%v want=%v", cfg.Browser.DefaultFingerprintArgs, want)
}
}
}
func TestLoadPreservesExplicitConfig(t *testing.T) {
t.Parallel()
+8 -1
View File
@@ -12,6 +12,13 @@ Linux (new):
- `bin/linux-arm64/xray`
- `bin/linux-arm64/sing-box`
macOS (unsigned internal builds):
- `bin/darwin-amd64/xray`
- `bin/darwin-amd64/sing-box`
- `bin/darwin-arm64/xray`
- `bin/darwin-arm64/sing-box`
Runtime hashes are pinned in `publish/runtime-manifest.json`.
Pinned upstream archive sources are tracked in `publish/runtime-sources.json`.
Use `python3 tools/runtime/sync-runtime.py` to refresh Linux runtimes safely.
Use `python3 tools/runtime/sync-runtime.py --target <target>` to refresh runtime files safely.
+1 -1
View File
@@ -8,7 +8,7 @@
<key>CFBundleExecutable</key>
<string>{{.OutputFilename}}</string>
<key>CFBundleIdentifier</key>
<string>com.wails.{{.Name}}</string>
<string>com.antbrowser.desktop</string>
<key>CFBundleVersion</key>
<string>{{.Info.ProductVersion}}</string>
<key>CFBundleGetInfoString</key>
+5
View File
@@ -0,0 +1,5 @@
# Browser Core Placeholder
Place downloaded browser cores under this directory when running from source.
Published macOS and Linux packages include this placeholder only. Actual browser core binaries are user-managed and should live under the writable `chrome/` directory in the runtime state root.
+91 -324
View File
@@ -1,360 +1,127 @@
# macOS Publish Plan
# macOS Publish
## Purpose
This directory contains the unsigned macOS packaging flow for Ant Browser.
This document defines the macOS packaging plan for Ant Browser.
The macOS package is intended for internal testing and PR validation first. It deliberately does not perform Apple Developer ID signing or notarization.
The goal is to turn the current codebase into a macOS build that can:
## Targets
- build on a native macOS machine
- launch from `/Applications`
- keep user-writable state outside the `.app` bundle
- bundle required proxy runtime binaries
- avoid breaking existing Windows and Linux packaging flows
- `darwin/amd64`
- `darwin/arm64`
## Current Entry Command
Output artifacts:
The initial internal-build script can be invoked on a real Mac with:
- `publish/output/AntBrowser-<version>-macos-<arch>.app`
- `publish/output/AntBrowser-<version>-macos-<arch>.zip`
## Runtime Policy
macOS publish uses repository-pinned runtime files and hash verification.
Pinned upstream source lock file:
- `publish/runtime-sources.json`
Required files:
- `bin/darwin-amd64/xray`
- `bin/darwin-amd64/sing-box`
- `bin/darwin-arm64/xray`
- `bin/darwin-arm64/sing-box`
Hashes are validated by:
- `tools/runtime/verify-runtime.sh`
- `publish/runtime-manifest.json`
Recommended way to refresh runtime files:
```bash
python3 tools/runtime/sync-runtime.py --target darwin-amd64
python3 tools/runtime/sync-runtime.py --target darwin-arm64
```
If you replace runtime files manually, update manifest hashes:
```bash
python3 tools/runtime/update-runtime-manifest.py --target darwin-amd64
python3 tools/runtime/update-runtime-manifest.py --target darwin-arm64
```
If runtime file or hash is missing/mismatched, publish will fail.
## Commands
Build on a native macOS host that matches the target architecture:
```bash
bash publish/mac/publish-mac.sh --arch amd64
bash publish/mac/publish-mac.sh --arch arm64
```
For the first iteration, `arm64` is the recommended target.
The first command is for Intel Macs. The second command is for Apple Silicon Macs.
This is a plan document only. It does not mean macOS packaging is already implemented.
The script:
## Current Status
1. verifies the host OS and architecture
2. verifies pinned Darwin runtime hashes
3. installs frontend dependencies
4. builds frontend assets
5. runs `wails build -platform darwin/<arch>`
6. assembles an unsigned `.app`
7. bundles `xray` and `sing-box` under `Contents/MacOS/bin`
8. writes a `.zip` artifact with the app bundle as the top-level item
The repository already has:
## Writable State Layout
- Windows packaging flow
- Linux packaging flow
- partial Darwin runtime compatibility in backend code
- initial `publish/mac/publish-mac.sh` scaffold for internal test builds
- initial `publish/config.init.mac.yaml` template
- committed Darwin runtime binaries under `bin/darwin-amd64/` and `bin/darwin-arm64/`
- Darwin runtime entries in `publish/runtime-manifest.json`
- Darwin runtime source lock entries in `publish/runtime-sources.json`
Installed `.app` bundles should be treated as read-only. When Ant Browser runs from:
The repository does not yet have:
- `Ant Browser.app/Contents/MacOS`
- `Ant Browser.app/Contents/Resources`
- signing / notarization flow
The repository now includes the first macOS writable-state implementation for app bundle roots:
- when the app root is inside `.app/Contents/MacOS` or `.app/Contents/Resources`
- writable state is redirected to `~/Library/Application Support/ant-browser`
- `bin/` stays in the app bundle
- config, chrome, and data move to the user state root
## Current Implementation Note
The current initial macOS packaging scaffold intentionally places helper binaries and seed files under:
- `Ant Browser.app/Contents/MacOS/bin`
- `Ant Browser.app/Contents/MacOS/config.yaml`
- `Ant Browser.app/Contents/MacOS/chrome/README.md`
This is not the prettiest final bundle layout, but it matches the current runtime path resolution and avoids a larger refactor in Phase 1.
After the first internal build is stable, the bundle layout can be reviewed and moved toward `Contents/Resources` if needed.
## Why macOS Looks More Complex
macOS is not difficult because of Wails alone. The real complexity comes from four areas:
1. Installed `.app` bundles under `/Applications` should be treated as read-only.
2. User data must not be written inside the `.app` bundle.
3. External helper binaries such as `xray` and `sing-box` must exist for Darwin and must be bundled correctly.
4. Public distribution usually requires code signing and notarization, otherwise Gatekeeper may block launch.
## Recommended Scope
### Phase 1: Internal Test Build
Target:
- `darwin/arm64` first
- output `.app` and `.zip`
- unsigned build is acceptable for internal testing
Why:
- Apple Silicon is the mainstream macOS target now
- it keeps the first version smaller and easier to verify
- it avoids spending time on Intel support before the runtime path is stable
### Phase 2: Public Distribution Build
Target:
- signed `.app`
- notarized `.zip` or `.dmg`
- optional `darwin/amd64` or universal build
Why:
- end users expect double-click install and normal launch
- unsigned apps and embedded helper binaries are more likely to be blocked
## Recommended Runtime Layout
### App Bundle
Recommended structure inside the built app:
- `Ant Browser.app/Contents/MacOS/ant-chrome`
- `Ant Browser.app/Contents/Resources/bin/xray`
- `Ant Browser.app/Contents/Resources/bin/sing-box`
- optional placeholder `chrome/README.md` if you want to keep behavior aligned with Linux
### User-Writable State
Recommended macOS state root:
runtime state is redirected to:
- `~/Library/Application Support/ant-browser`
Recommended contents under the state root:
Bundled runtime files remain in the app bundle:
- `Ant Browser.app/Contents/MacOS/bin/xray`
- `Ant Browser.app/Contents/MacOS/bin/sing-box`
Writable files live in the user state root:
- `config.yaml`
- `proxies.yaml`
- `proxies.yaml` if present
- `data/`
- `chrome/`
Rule:
## Local Launch
- runtime binaries stay in the app bundle
- config, database, browser cores, logs, and profile data stay in the user state root
After a successful build:
## Code Changes Required
```bash
open "publish/output/AntBrowser-<version>-macos-<arch>.app"
```
### 1. Add macOS Writable State Handling
For an unsigned local build copied to `/Applications`, remove quarantine if macOS blocks launch:
Current Linux detached-state logic only activates on Linux:
```bash
xattr -dr com.apple.quarantine "/Applications/Ant Browser.app"
open "/Applications/Ant Browser.app"
```
- `backend/internal/apppath/apppath.go`
Required change:
- extend path detection so installed macOS apps also use a detached writable state root
- recommended trigger: when `GOOS=darwin` and app root is not writable, or when running from an `.app` bundle
Expected result:
- app launch from `/Applications` does not try to write config/data into the bundle
### 2. Add Darwin Runtime Binaries
Current runtime manifest has Windows and Linux only:
- `publish/runtime-manifest.json`
Required additions:
- `bin/darwin-arm64/xray`
- `bin/darwin-arm64/sing-box`
- optional `bin/darwin-amd64/xray`
- optional `bin/darwin-amd64/sing-box`
- manifest hash entries for the new targets
Status:
- implemented for both `darwin-arm64` and `darwin-amd64`
- files are committed into the repository
- runtime manifest verification now works for both Darwin targets
Related scripts to extend:
- `tools/runtime/sync-runtime.py`
- `tools/runtime/update-runtime-manifest.py`
- `tools/runtime/verify-runtime.sh`
### 3. Add macOS Publish Script
New file to add:
- `publish/mac/publish-mac.sh`
Recommended responsibilities:
1. verify host is macOS
2. verify target arch (`arm64` first)
3. install frontend dependencies
4. build frontend
5. run `wails build -platform darwin/arm64`
6. place runtime binaries into the app bundle
7. optionally archive to `.zip`
8. optionally sign and notarize when environment variables are provided
Current scaffold status:
- implemented as an unsigned internal-build script
- outputs `.app` plus `.zip`
- requires a native macOS host
- intentionally does not attempt notarization yet
### 4. Add macOS Runtime Placement Logic
The app currently resolves most paths through shared runtime helpers, which is good.
Files likely involved:
- `backend/app.go`
- `backend/app_paths.go`
- `backend/app_utils.go`
- `backend/internal/browser/types.go`
- `backend/internal/proxy/xray.go`
- `backend/internal/proxy/singbox.go`
- `main.go`
Goal:
- all writable files go to the user state root
- helper binaries continue to resolve from the app bundle
### 5. Signing and Notarization
This is not required for a first internal test build, but is required for a serious public release.
Needed later:
- Apple Developer certificate
- `codesign`
- `notarytool`
- entitlements if runtime behavior requires them
Typical flow:
1. sign helper binaries
2. sign the `.app`
3. zip or build dmg
4. notarize
5. staple
## Recommended Implementation Order
1. Deliver `darwin/arm64` internal test build only.
2. Add macOS detached state root.
3. Add Darwin runtime binaries and manifest support.
4. Add `publish/mac/publish-mac.sh`.
5. Verify launch from `/Applications`.
6. Verify browser core placement under user state root.
7. Verify proxy runtime launch on macOS.
8. Add signing and notarization only after the unsigned build is stable.
9. Decide whether `darwin/amd64` is worth supporting.
Unsigned builds are not suitable for public distribution. A public macOS release still needs Developer ID signing, helper binary signing, notarization, and staple.
## Validation Checklist
The macOS work should not be considered complete until all items below are verified on a real Mac.
### Packaging
- build completes on native macOS
- output `.app` exists
- output `.zip` or `.dmg` exists
- bundled helper binaries are executable
### First Launch
- app launches from Finder
- app launches after copying to `/Applications`
- output `.zip` exists
- `Contents/MacOS/ant-chrome` is executable
- bundled `xray` and `sing-box` are executable
- app launches from Finder or `open`
- first launch creates `~/Library/Application Support/ant-browser`
- `config.yaml` is seeded correctly
- database and `data/` are created under the user state root
### Browser Core
- manually placed browser core can be detected
- browser core path persists in config or database
- browser instance can actually start
### Proxy Runtime
- `xray` can be launched from the app bundle
- `sing-box` can be launched from the app bundle
- work directories are created under the user state root
### Exit Behavior
- window close works
- explicit quit works
- no stuck background process remains after quit
### Regression Safety
- Windows packaging still builds
- Linux packaging still builds
- Linux detached state behavior still works
## Difficulty Assessment
### Internal Test Build
Difficulty: medium
Main blockers:
- mac runtime binaries
- detached writable state
- mac packaging script
### Public Release Build
Difficulty: medium-high
Main blockers:
- signing
- notarization
- quarantine / Gatekeeper behavior
- helper binary signing order
## Suggested First Deliverable
The safest first milestone is:
- macOS `arm64`
- native build on a real Mac
- unsigned `.app`
- zipped artifact for internal testing
- detached writable state under `~/Library/Application Support/ant-browser`
- bundled `xray` and `sing-box`
Do not start with:
- universal binary
- dmg beautification
- public distribution
- Intel support
Those can come after the app is proven stable on one Mac target first.
## Files Expected To Be Added Or Updated
Likely new files:
- `publish/mac/publish-mac.sh`
- `publish/mac/README.md`
Likely updated files:
- `backend/internal/apppath/apppath.go`
- `backend/internal/apppath/apppath_test.go`
- `backend/runtime_paths.go`
- `backend/app.go`
- `main.go`
- `publish/runtime-manifest.json`
- `publish/runtime-sources.json`
- `tools/runtime/sync-runtime.py`
- `tools/runtime/update-runtime-manifest.py`
- `tools/runtime/verify-runtime.sh`
## Decision Record
Current recommendation:
- do macOS `arm64` first
- solve writable state before touching signing
- keep Windows and Linux publish flows unchanged unless shared runtime code needs extension
- treat public notarized distribution as Phase 2, not Phase 1
- `config.yaml` is seeded with `--fingerprint-platform=mac`
- SQLite database and `data/` are created under the user state root
- browser core detection works for macOS `.app` cores
- proxy runtime binaries can be launched from the app bundle