Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions .github/workflows/build-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,15 @@ jobs:

- name: Get build matrix
id: matrix
uses: home-assistant/builder/actions/prepare-multi-arch-matrix@7.2.0
uses: home-assistant/builder/actions/prepare-multi-arch-matrix@2026.09.0
with:
architectures: ${{ env.ARCHITECTURES }}
image-name: ${{ env.IMAGE_NAME }}
# 必须显式传小写前缀。该 action 默认取 github.repository_owner,
# 而本仓库 owner 是 "C3H3-AI"(含大写),GHCR 会拒绝:
# invalid tag "ghcr.io/C3H3-AI/amd64-ai-proxy:1.1.0b13":
# repository name must be lowercase
registry-prefix: ghcr.io/c3h3-ai

build:
name: Build ${{ matrix.arch }}
Expand All @@ -79,7 +84,7 @@ jobs:
- uses: actions/checkout@v4

- name: Build image
uses: home-assistant/builder/actions/build-image@7.2.0
uses: home-assistant/builder/actions/build-image@2026.09.0
with:
arch: ${{ matrix.arch }}
container-registry-password: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -99,11 +104,12 @@ jobs:
packages: write
steps:
- name: Publish multi-arch manifest
uses: home-assistant/builder/actions/publish-multi-arch-manifest@7.2.0
uses: home-assistant/builder/actions/publish-multi-arch-manifest@2026.09.0
with:
architectures: ${{ env.ARCHITECTURES }}
container-registry-password: ${{ secrets.GITHUB_TOKEN }}
image-name: ${{ env.IMAGE_NAME }}
registry-prefix: ghcr.io/c3h3-ai
image-tags: |
${{ needs.init.outputs.version }}
latest
Expand All @@ -119,7 +125,9 @@ jobs:
- name: Verify manifest tag matches config.yaml version
run: |
set -euo pipefail
IMAGE="ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}"
# 小写硬编码,与 build/manifest 两个 job 的 registry-prefix 保持一致
# (owner 是 C3H3-AI,GHCR 不接受大写)
IMAGE="ghcr.io/c3h3-ai/${IMAGE_NAME}"
VERSION="${{ needs.init.outputs.version }}"
echo "Checking ${IMAGE}:${VERSION}"
if ! docker manifest inspect "${IMAGE}:${VERSION}" >/dev/null 2>&1; then
Expand Down
37 changes: 33 additions & 4 deletions SYNC.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,31 @@ addon 采用「复制 + 生成式 sync + 适配层隔离」策略,而非 Go mo

| 包 | 来源 | 更新策略 |
|---|---|---|
| `auth` `config` `login` `login_qoder` `login_trae` `pool` `provider` `qoder` `scheduler` `server` `traework` `upstream` | 上游 | **sync 生成**,不带手写改动(见 GENERATED 头) |
| `svc` | addon 独有 | 自行维护(HA 加载 `trae-*.json`) |
| `trae_adaptor` | addon 独有 | 自行维护(公网回调 + refreshToken 兜底,桌面版没有) |
| `auth` `login` `login_qoder` `login_trae` `provider` `qoder` `scheduler` `traework` | 上游 | **sync 生成**,不带手写改动(见 GENERATED 头) |
| `svc` `trae_adaptor` | addon 独有 | 自行维护 |
| **`pool` `config` `server` `upstream`** | 上游 + **addon 改动** | ⚠️ **受保护,不被 sync 覆盖**,见下节 |

## ⚠️ 受保护文件(B 类)——为什么它们不再自动同步

addon 对 `pool` / `config` / `server` / `upstream` 四个包做了改动,这些改动
**无法搬到 addon 独有包**,已逐项验证原因:

| 文件 | addon 改动 | 为何无法外移 |
|---|---|---|
| `pool/pool.go` | `SetLowCredits`、`Pick()` 语义改为排除低积分账号 | 改了 `Pick()`/`pickExcluding()` 的**内部实现**。Go **不支持方法覆写**(同名函数放同包新文件会编译冲突);所需状态 `lowCredit`/`lowCredits` 是**私有字段**,外部包访问不了 |
| `config/config.go` | 新增 `LowCreditThreshold` | 被 `svc.go` 引用;字段是 `config.Config` 的一部分 |
| `server/handler.go` | TraeWork 预刷新窗口按平台区分 | 改 `chatCompletions` 内部逻辑 |
| `upstream/client.go` | 流式请求用无总时长上限的 client | 改 `Client` 结构体与方法 |

**代价(须知情)**:这些文件**不再跟随上游自动更新**。上游若改动了它们,
需要**人工 merge**。

**这个代价目前为零**:经核对,上游自基线 `c62d0bc` 起对
`pool` / `config` / `server` / `upstream` 的改动提交数为 **0**。

**不会成为黑洞**:`sync_vendor.sh` 结束时会对这些文件做**上游差异检查**,
若上游在基线之后改动过,会显式打印 `[WARN]` 并汇总提示需人工 merge
(而不是静默跳过)。

## 上游更新时怎么做(一次性核对)

Expand All @@ -56,8 +78,15 @@ cd D:/ai-hub/integrations/ha-ai-proxy/src
生成脚本会:
1. 复制上游 `internal/{auth,config,login,login_qoder,login_trae,pool,provider,qoder,scheduler,server,traework,upstream}` 到 addon `src/internal/`
2. 在每个生成文件顶部打 `// CODE GENERATED FROM wild-work@<commit> — DO NOT EDIT, run sync_vendor.sh`
3. **不动** `svc` `trae_adaptor`(addon 独有)
3. **不动** `svc` / `trae_adaptor`(addon 独有)与 **B 类受保护文件**(见上节
4. 输出变更文件列表供 review
5. **对 B 类受保护文件做上游差异检查**,上游改过的打印 `[WARN]` 提示人工 merge

> ⚠️ **sync 前务必确认工作区干净**(`git status` 无未提交改动)。
> sync 会直接覆盖 20+ 个文件,未提交的改动无法恢复。
>
> ⚠️ **如果上游尚未更新**(`git log OLD..HEAD` 无输出),**不要跑 sync**。
> 跑了只会把 addon 落后的无关文件一起刷新,制造噪音 commit。

## 本地验证编译(实测可用)

Expand Down
100 changes: 98 additions & 2 deletions src/sync_vendor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,48 @@ COMMIT="$(cd "${UPSTREAM_DIR}" && git rev-parse --short HEAD)"
UP_INTERNAL="${UPSTREAM_DIR}/internal"
SYNC_PKGS="auth config login login_qoder login_trae pool provider qoder scheduler server traework upstream"

# 以下文件是 addon 独有、手维护,sync 时绝不覆盖(保留本地版本)
# 用数组 + 逐个比对,避免把换行塞进 case 模式导致匹配失效(旧写法静默失效过)
# 上次实际同步到的上游 commit。用于判断「上游自基线后是否改动过受保护文件」。
# 同步成功且人工确认无遗漏后,把这里更新为新的 COMMIT。
# 见 SYNC.md「对齐基线」。
BASELINE_COMMIT="c62d0bc"

# ── 受保护文件(sync 时不覆盖,保留 addon 版本)─────────────────────────────
#
# 分两类,原因不同:
#
# 【A 类】addon 独有文件 —— 上游根本没有这些文件,PROTECT 只是显式声明。
#
# 【B 类】addon 修改过的上游文件 —— 这些文件上游有,但 addon 做了**无法外移**的改动。
# 为何无法外移(已逐项验证):
# - pool.go : 改了 Pick()/pickExcluding() 的**内部实现**。Go 不支持方法覆写,
# 同名函数放同包新文件会直接编译冲突;而所需状态
# (lowCredit/lowCredits) 是**私有字段**,外部包访问不了。
# - config.go: 新增 LowCreditThreshold 字段,被 svc.go 引用。
# - server/handler.go: TraeWork 预刷新窗口按平台区分(defaultRefreshSkew)。
# - upstream/client.go: 流式请求改用无总时长上限的 client(StreamHTTP)。
#
# ⚠️ B 类的代价:这些文件**不再跟随上游自动更新**。上游若改动了它们,
# 本脚本会在结尾**显式报告**(不会静默跳过),届时需人工 merge。
PROTECT_FILES=(
# A 类:addon 独有
"internal/login_trae/addon_extras.go"
"internal/svc/svc.go"
# B 类:addon 修改过的上游文件
"internal/pool/pool.go"
"internal/config/config.go"
"internal/server/handler.go"
"internal/upstream/client.go"
"internal/upstream/headers.go"
"internal/traework/constants.go"
)

# B 类保护文件清单:用于结尾的上游差异检查。
# 只对这些文件检查「上游相对当前 addon 副本是否已变化」。
CHECK_UPSTREAM_FILES=(
"internal/pool/pool.go"
"internal/config/config.go"
"internal/server/handler.go"
"internal/upstream/client.go"
"internal/upstream/headers.go"
"internal/traework/constants.go"
)
Expand Down Expand Up @@ -69,3 +106,62 @@ done

echo "==> done: ${CHANGED} files changed (from wild-work@${COMMIT})"
if [ "${CHANGED}" -gt 0 ]; then echo " run: go build ./... && go vet ./internal/..."; fi

# ── B 类保护文件的上游差异检查 ────────────────────────────────────────────
#
# 受保护文件不会被覆盖,但**必须让人知道上游是否动过它们** ——
# 否则 PROTECT 就成了黑洞:上游的修复/改动被永久静默忽略。
#
# 判断方式:把上游当前版本按 sync 的同样方式规范化(去 CRLF、改 module 前缀、
# 去掉 GENERATED 头),与 addon 本地版本比较。若**上游自身**相对上次同步基线
# 发生了变化,本地副本与上游的差异就会超出 addon 自有改动,此处给出提示。
#
# 注意:上游文件是 CRLF、addon 是 LF,比较前必须 tr -d '\r',
# 否则全文件都会显示为「有差异」而失去意义(这个坑踩过)。
echo
echo "==> 受保护文件(B 类)上游差异检查"
NEED_MERGE=0
for pf in "${CHECK_UPSTREAM_FILES[@]}"; do
up_file="${UP_INTERNAL}/${pf#internal/}"
ad_file="${ADDON_INTERNAL}/${pf#internal/}"
if [ ! -f "${up_file}" ]; then
echo " [n/a ] ${pf} — 上游已无此文件,请确认是否应移除"
continue
fi
if [ ! -f "${ad_file}" ]; then
echo " [n/a ] ${pf} — addon 本地不存在"
continue
fi
# 规范化上游版本:去 CRLF、改 module 前缀
norm_up="$(mktemp)"
sed "s#wild-work/internal#github.com/rockswang/workbuddy-wild/internal#g" "${up_file}" | tr -d '\r' > "${norm_up}"
# addon 版本:去掉 GENERATED 头两行
norm_ad="$(mktemp)"
tail -n +3 "${ad_file}" > "${norm_ad}"

diff_lines=$(diff "${norm_up}" "${norm_ad}" 2>/dev/null | grep -c '^[<>]' || true)
rm -f "${norm_up}" "${norm_ad}"

if [ "${diff_lines}" -eq 0 ]; then
echo " [ok ] ${pf} — 与上游一致(addon 未改动或已同步)"
else
# 有差异是正常的(addon 自有改动)。真正要提示的是「上游在基线之后动过它」。
if [ -n "${BASELINE_COMMIT:-}" ]; then
up_changed=$(cd "${UPSTREAM_DIR}" && git log --oneline "${BASELINE_COMMIT}..${COMMIT}" -- "internal/${pf#internal/}" 2>/dev/null | wc -l || echo 0)
if [ "${up_changed}" -gt 0 ]; then
echo " [WARN] ${pf} — 上游自基线 ${BASELINE_COMMIT} 起改动了 ${up_changed} 次,需人工 merge"
NEED_MERGE=$((NEED_MERGE+1))
else
echo " [keep] ${pf} — 差异 ${diff_lines} 行(addon 自有改动;上游未动)"
fi
else
echo " [keep] ${pf} — 差异 ${diff_lines} 行(addon 自有改动)"
fi
fi
done

if [ "${NEED_MERGE}" -gt 0 ]; then
echo
echo "⚠️ 有 ${NEED_MERGE} 个受保护文件上游已改动,请人工 merge 后再提交。"
echo " (这些文件不会自动同步,是设计使然:见 PROTECT_FILES 注释)"
fi
Loading