Skip to content

Commit f6aeb14

Browse files
committed
fix: build release sidecar from fork source
1 parent f7767f2 commit f6aeb14

4 files changed

Lines changed: 142 additions & 27 deletions

File tree

.github/workflows/release.yml

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -97,23 +97,12 @@ jobs:
9797
- name: Install Wails
9898
run: go install github.com/wailsapp/wails/v2/cmd/wails@latest
9999

100-
# ── Fetch sidecar binary ──────────────────────────────────────────────
101-
- name: Fetch CLIProxyAPI sidecar (macOS/Linux)
102-
if: runner.os != 'Windows'
103-
run: |
104-
chmod +x scripts/fetch-sidecar.sh
105-
./scripts/fetch-sidecar.sh ${{ matrix.goos }} ${{ matrix.goarch == 'universal' && 'arm64' || matrix.goarch }} latest build/bin
106-
# For darwin/universal also fetch amd64
107-
if [ "${{ matrix.goos }}" = "darwin" ]; then
108-
./scripts/fetch-sidecar.sh darwin amd64 latest build/bin/amd64
109-
fi
110-
111-
- name: Fetch CLIProxyAPI sidecar (Windows)
112-
if: runner.os == 'Windows'
100+
# ── Build sidecar from fork source ───────────────────────────────────
101+
- name: Build CLIProxyAPI sidecar from source
113102
shell: bash
114103
run: |
115-
chmod +x scripts/fetch-sidecar.sh
116-
./scripts/fetch-sidecar.sh windows amd64 latest build/bin
104+
chmod +x scripts/build-sidecar.sh
105+
./scripts/build-sidecar.sh ${{ matrix.goos }} ${{ matrix.goarch }} build/bin
117106
118107
# ── Platform-specific dependencies ───────────────────────────────────
119108
- name: Install macOS packaging tools
@@ -201,6 +190,14 @@ jobs:
201190
${{ matrix.wails-extra || '' }} \
202191
-ldflags "-X main.Version=${{ github.ref_name }} -X main.ReleaseLabel=${RELEASE_LABEL}"
203192
193+
- name: Reinstall universal sidecar into macOS app bundle
194+
if: runner.os == 'macOS'
195+
shell: bash
196+
run: |
197+
cp build/bin/cli-proxy-api build/bin/GetTokens.app/Contents/MacOS/cli-proxy-api
198+
chmod +x build/bin/GetTokens.app/Contents/MacOS/cli-proxy-api
199+
lipo -info build/bin/GetTokens.app/Contents/MacOS/cli-proxy-api
200+
204201
- name: Package updater asset
205202
if: runner.os != 'macOS'
206203
shell: bash

docs-linhay/dev/20260426-release-prep-guide.md

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,42 @@
2424
- macOS 保留 `tar.gz` 资产用于检测最新版本和统一校验链,但签名发布包不做 bundle 内原地替换,设置页会跳转到 release 页面安装。
2525
- 原因是 Apple 对签名 bundle 的 seal 有要求;修改 `.app` 主可执行文件会破坏签名边界。参见 Apple Technical Note TN2206。
2626

27+
## sidecar 构建边界
28+
发布前必须先从仓库内维护的 `docs-linhay/references/CLIProxyAPI` 源码构建 sidecar,不能直接下载上游 release 二进制。
29+
30+
原因:
31+
32+
1. `CLIProxyAPI` 已经进入 fork 维护态,行为修复首先落在 fork 源码,而不是等 fork release。
33+
2. fork release 可能滞后,直接下载 release 无法保证包含本轮补丁。
34+
3.`darwin/universal`,最终写入 `GetTokens.app``Contents/MacOS/cli-proxy-api` 必须是 universal binary;不能只塞一份 `arm64`
35+
36+
对应脚本:
37+
- `scripts/build-sidecar.sh <goos> <goarch> <output-dir>`
38+
39+
示例:
40+
41+
```bash
42+
./scripts/build-sidecar.sh darwin universal build/bin
43+
./scripts/build-sidecar.sh windows amd64 build/bin
44+
./scripts/build-sidecar.sh linux amd64 build/bin
45+
```
46+
2747
## macOS 签名与公证
2848
macOS release 现在按以下顺序处理:
2949

30-
1. 使用 `Developer ID Application` 证书对 `GetTokens.app` 做 hardened runtime 签名
31-
2. 使用 `notarytool` 提交 `.app` 的 zip 包并等待公证完成
32-
3.`.app` 执行 `stapler staple`
33-
4. 基于已 stapled 的 `.app` 生成 DMG
34-
5. 对 DMG 重新签名后再次提交 notarization
35-
6. 对 DMG 执行 `stapler staple`
36-
7. 最后再从签名后的 `.app` 中提取 updater 原始可执行文件,打成 `tar.gz`
50+
0.`docs-linhay/references/CLIProxyAPI` 源码构建 universal sidecar,写入 `build/bin/cli-proxy-api`
51+
1. 执行 `wails build`
52+
2.`build/bin/cli-proxy-api` 显式覆盖 `build/bin/GetTokens.app/Contents/MacOS/cli-proxy-api`,确保 app bundle 内 sidecar 仍是 universal binary
53+
3. 使用 `Developer ID Application` 证书对 `GetTokens.app` 做 hardened runtime 签名
54+
4. 使用 `notarytool` 提交 `.app` 的 zip 包并等待公证完成
55+
5.`.app` 执行 `stapler staple`
56+
6. 基于已 stapled 的 `.app` 生成 DMG
57+
7. 对 DMG 重新签名后再次提交 notarization
58+
8. 对 DMG 执行 `stapler staple`
59+
9. 最后再从签名后的 `.app` 中提取 updater 原始可执行文件,打成 `tar.gz`
3760

3861
对应脚本:
62+
- [scripts/build-sidecar.sh](/Users/linhey/Desktop/linhay-open-sources/GetTokens/scripts/build-sidecar.sh)
3963
- [scripts/sign-notarize-macos-release.sh](/Users/linhey/Desktop/linhay-open-sources/GetTokens/scripts/sign-notarize-macos-release.sh)
4064
- [scripts/package-updater-asset.sh](/Users/linhey/Desktop/linhay-open-sources/GetTokens/scripts/package-updater-asset.sh)
4165

@@ -65,20 +89,21 @@ CI release workflow 需要以下 secrets:
6589
3. UI 展示版本时间使用 `ReleaseLabel`,不和 `Version` 混用。
6690
4. Windows 安装包必须避开 `go-selfupdate` 的默认匹配规则,因此使用 `_installer.exe` 后缀。
6791
5. macOS universal updater 资产需要和 `UniversalArch=universal` 对齐。
68-
6. macOS release workflow 必须先 notarize `.app`再从已 stapled 的 `.app` 生成 DMG,最后再 notarize DMG;不能先打 DMG 再签 app
92+
6. macOS release workflow 必须先把源码构建出来的 universal sidecar 回填进 `.app`,再 notarize `.app`然后从已 stapled 的 `.app` 生成 DMG,最后再 notarize DMG。
6993
7. 已签名 macOS `.app` 在当前框架下只支持“检查更新 + 跳转 release 页面”,不支持 bundle 内 `ApplyUpdate`
7094

7195
## 建议发布步骤
7296
1. 确认工作区只包含本次准备发布的变更。
7397
2. 运行前端类型检查、Go 测试、文档校验。
74-
3. 合并到干净提交后创建 tag:`v0.1.0`
75-
4. 推送 tag 触发 GitHub Actions release workflow。
76-
5. 在生成的 release 页面检查:
98+
3. 从 fork 源码构建 sidecar,并确认 macOS 场景下 `build/bin/cli-proxy-api` 为 universal binary。
99+
4. 合并到干净提交后创建 tag:`v0.1.0`
100+
5. 推送 tag 触发 GitHub Actions release workflow。
101+
6. 在生成的 release 页面检查:
77102
- 安装包资产存在
78103
- updater 资产存在
79104
- `checksums.txt` 包含全部资产
80105
- macOS DMG 已经 stapled,`xcrun stapler validate` 通过
81-
6. 使用非 dev 构建验证:
106+
7. 使用非 dev 构建验证:
82107
- `CheckUpdate` 能发现新版本
83108
- Windows / Linux: `ApplyUpdate` 能下载并替换二进制,应用退出后重启进入新版本
84109
- macOS: 设置页能打开对应 release 页面,下载安装后进入新版本

docs-linhay/memory/2026-04-26.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,7 @@
146146
- 新增 `Feature / Page Boundary`
147147
- 新增 `Go Large-File Split Heuristics`
148148
- 本轮判断这些规则已稳定到项目级 skill / dev 文档,但还不需要上升为 `AGENTS.md` 的 repo-wide 硬约束。
149+
- 发布流程纠偏:GetTokens 打包前的 sidecar 不能再通过 `scripts/fetch-sidecar.sh` 拉上游 release 二进制;必须先从 `docs-linhay/references/CLIProxyAPI` 的 fork 源码构建。
150+
- 发布修复:新增 `scripts/build-sidecar.sh`,统一从本地 fork 源码构建 sidecar;`darwin/universal` 场景会先分别构建 `arm64/x86_64`,再用 `lipo` 合成为单一 universal `build/bin/cli-proxy-api`
151+
- 发布治理:`.github/workflows/release.yml` 已切换为源码构建 sidecar,不再依赖上游 `router-for-me/CLIProxyAPI` release 资产。
152+
- 额外发现:Wails `darwin/universal` 打包后,`GetTokens.app/Contents/MacOS/cli-proxy-api` 会退回单架构副本;因此 workflow 需要在 `wails build` 之后显式把 `build/bin/cli-proxy-api` 覆盖回 app bundle,再进入签名与 DMG。

scripts/build-sidecar.sh

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
if [[ $# -ne 3 ]]; then
5+
echo "Usage: $0 <goos> <goarch> <output-dir>" >&2
6+
exit 1
7+
fi
8+
9+
GOOS="$1"
10+
GOARCH="$2"
11+
OUTPUT_DIR="$3"
12+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13+
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
14+
SOURCE_DIR="${CLI_PROXY_SOURCE_DIR:-${ROOT_DIR}/docs-linhay/references/CLIProxyAPI}"
15+
BINARY_NAME="cli-proxy-api"
16+
17+
if [[ "$GOOS" == "windows" ]]; then
18+
BINARY_NAME="${BINARY_NAME}.exe"
19+
fi
20+
21+
if [[ ! -d "${SOURCE_DIR}" ]]; then
22+
echo "CLIProxyAPI source dir not found: ${SOURCE_DIR}" >&2
23+
exit 1
24+
fi
25+
26+
if [[ ! -f "${SOURCE_DIR}/go.mod" ]]; then
27+
echo "CLIProxyAPI source dir is missing go.mod: ${SOURCE_DIR}" >&2
28+
exit 1
29+
fi
30+
31+
resolve_git_value() {
32+
local cmd="$1"
33+
if git -C "${SOURCE_DIR}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
34+
git -C "${SOURCE_DIR}" ${cmd} 2>/dev/null || true
35+
fi
36+
}
37+
38+
VERSION="${CLI_PROXY_VERSION:-$(resolve_git_value "describe --tags --always --dirty")}"
39+
COMMIT="${CLI_PROXY_COMMIT:-$(resolve_git_value "rev-parse --short HEAD")}"
40+
BUILD_DATE="${CLI_PROXY_BUILD_DATE:-$(date -u +'%Y-%m-%dT%H:%M:%SZ')}"
41+
42+
VERSION="${VERSION:-fork-source}"
43+
COMMIT="${COMMIT:-unknown}"
44+
45+
LDFLAGS="-s -w -X main.Version=${VERSION} -X main.Commit=${COMMIT} -X main.BuildDate=${BUILD_DATE}"
46+
47+
mkdir -p "${OUTPUT_DIR}"
48+
49+
build_single() {
50+
local target_goarch="$1"
51+
local output_path="$2"
52+
53+
(
54+
cd "${SOURCE_DIR}"
55+
CGO_ENABLED=0 GOOS="${GOOS}" GOARCH="${target_goarch}" \
56+
go build -trimpath -ldflags "${LDFLAGS}" -o "${output_path}" ./cmd/server
57+
)
58+
}
59+
60+
case "${GOOS}:${GOARCH}" in
61+
darwin:universal)
62+
if ! command -v lipo >/dev/null 2>&1; then
63+
echo "lipo is required for darwin/universal sidecar builds" >&2
64+
exit 1
65+
fi
66+
67+
STAGE_DIR="$(mktemp -d)"
68+
trap 'rm -rf "${STAGE_DIR}"' EXIT
69+
70+
ARM64_PATH="${STAGE_DIR}/cli-proxy-api-arm64"
71+
AMD64_PATH="${STAGE_DIR}/cli-proxy-api-amd64"
72+
UNIVERSAL_PATH="${OUTPUT_DIR}/cli-proxy-api"
73+
74+
build_single arm64 "${ARM64_PATH}"
75+
build_single amd64 "${AMD64_PATH}"
76+
lipo -create -output "${UNIVERSAL_PATH}" "${ARM64_PATH}" "${AMD64_PATH}"
77+
chmod +x "${UNIVERSAL_PATH}"
78+
;;
79+
darwin:arm64 | darwin:amd64 | linux:amd64 | linux:arm64 | windows:amd64 | windows:arm64)
80+
build_single "${GOARCH}" "${OUTPUT_DIR}/${BINARY_NAME}"
81+
chmod +x "${OUTPUT_DIR}/${BINARY_NAME}"
82+
;;
83+
*)
84+
echo "Unsupported sidecar target: ${GOOS}:${GOARCH}" >&2
85+
exit 1
86+
;;
87+
esac
88+
89+
echo "✓ Built CLIProxyAPI from source: ${OUTPUT_DIR}/${BINARY_NAME}"

0 commit comments

Comments
 (0)