From 6d7de92bbd51ed073cd518e22a2fa6297cdd01f0 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Tue, 29 Sep 2026 10:23:21 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20=E5=BB=BA=E7=AB=8B=20GitHub=20Actions=20?= =?UTF-8?q?=E6=B5=81=E6=B0=B4=E7=BA=BF=EF=BC=88=E5=85=AD=E4=B8=AA=20job?= =?UTF-8?q?=EF=BC=8C=E5=85=A8=E9=83=A8=E5=91=BD=E4=BB=A4=E5=B7=B2=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E5=AE=9E=E6=B5=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 为什么现在做 这次排查「QQ 收不到回复」花了大半程才定位到根因,途中我犯了两类错: 先断言「提示词没写 output_send 规则」(实际 buildSystemPrompt:48-52 写了), 又断言「适配器丢了内容」(实际两版等价、直连上游正常)。 两次都是**在无自动化判据的情况下凭局部证据外推**。 仓库已有 60 个 Go 包、`go build ./...` 仅 2.2 秒,成本极低却无人强制跑。 AtomGit 停用流水线后更无兜底,故迁到 GitHub 补齐。 ## 设计原则:CI 里每条命令都是本地已实测通过的 不写「可能有用先试试」的步骤 —— 未验证的 CI 步骤会把假红灯变成常态, 最后所有人都学会忽略它。本文六个 job 的每条命令都本地跑过: go build ./... ✓ go vet ./... ✓ go test ./... -count=1 ✓(干净克隆亦通过) make check-client-versions ✓ go test -race core + waiter ✓ waiter/initconfig/mock-server 交叉 ✓(5 平台) npm test(cmd/gui) ✓ make check-csrc ✓(告警/ABI/ASan+UBSan/跨架构) ## 六个 job | job | 覆盖 | |-------|----------------------------------------------------------| | go | build + vet + test + **跨平台客户端版本一致性** | | race | 并发核心的竞态检测 | | cross | linux/darwin/windows × amd64/arm64(仅可纯交叉的 3 个 cmd)| | gui | Electron 仓的 Node 测试 | | csrc | C 基础设施门禁 | | docs | 站点配置可解析 | ## 关键事实(都由实测确立,不是推断) 1. **只有 waiter/initconfig/mock-server 能纯交叉编译**。homed、memgc、 homed-kb-migrate 依赖 cgo(gojieba / onnx),必须原生构建 ⇒ 不进 cross matrix。 2. **CGO 必须为 1**:gojieba 需要 cgo,`CGO_ENABLED=0` 下 internal/memory 直接编译失败(实测)。 3. **`go test ./...` 不会碰到 cmd/gui**。该目录是纯 Electron(0 个 .go、 无 go.mod),`./...` 只匹配含 Go 文件的包;只有显式 `go test ./cmd/gui` 才报 "no Go files"。这不是缺陷,是 Go 的包匹配语义 —— 之前把它当 [setup failed] 是误读。 4. **cmd/gui 的 npm test 零依赖**:三个 .mjs 只 import node: 内置模块 (fs/url/path/vm)⇒ 不需要 npm ci、不需要 electron,秒级完成。 5. **测试自足,CI 上不会因缺本地服务而红**:webui 测试用 httptest 与 `127.0.0.1:0`,真实 LLM 测试带 t.Skip 守卫。 6. **action 版本已核实存在**:checkout/setup-go/setup-node/setup-python 均用 v7(经 GitHub API 逐个确认 tag 存在,避免「版本不存在 ⇒ 立刻红」)。 ## 明确不进 CI(依赖真机/密钥/内网,否则只会变 flaky 噪音) deploy-*.sh、waiter 真机(192.168.2.x)、`npm run test-live`(需真 Electron + Xvfb + 真后端)、scripts/kernel-stress/*、需 DEEPSEEK_API_KEY 的真实 LLM 测试。 --- .github/workflows/ci.yml | 199 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9e99aaf --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,199 @@ +# HomeAgent 主仓 CI。 +# +# 设计原则:**CI 里跑的每一条命令,都是本地已实测通过的命令**。 +# 不写「应该有用来试试」的步骤 —— 未验证的 CI 步骤会把假红灯变成常态, +# 最后所有人学会忽略它。 +# +# 覆盖范围与本地 `make test` 对齐(build / vet / test / client-versions / +# gui / csrc),并按依赖拆成独立 job,便于失败定位。 +# +# 明确**不在** CI 里跑的东西(依赖真机/密钥/内网,跑了只会变 flaky 噪音): +# - deploy-*.sh / homed 生产部署 +# - waiter 真机验证(192.168.2.x) +# - cmd/gui 的 `npm run test-live`(需真 Electron + Xvfb + 真后端) +# - scripts/kernel-stress/*(需 llmsproxy 与压测端点) +# - 需要 DEEPSEEK_API_KEY / MEDIALIVE_* 的真实 LLM 测试(已自带 t.Skip) +name: CI + +on: + push: + branches: [main, 'release/**'] + pull_request: + workflow_dispatch: + +# 只读权限:CI 不需要写仓库。 +permissions: + contents: read + +# 同一分支连续推送时取消旧跑,省额度也避免过期结果误导。 +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +env: + # gojieba / onnx 相关包需要 cgo ⇒ 不能用 CGO_ENABLED=0。 + CGO_ENABLED: 1 + # 减少 go test 输出噪音。 + GOFLAGS: -buildvcs=false + +jobs: + # ── Go 后端:构建 + 静态检查 + 全量测试 + 跨平台客户端版本一致性 ── + go: + name: Go build / vet / test + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + cache: true + + # cgo 需要 gcc/g++(gojieba 会编译自带 C++ 源码)。 + - name: 确认 cgo 工具链 + run: | + gcc --version | head -1 + g++ --version | head -1 + + - name: go build ./... + run: go build ./... + + - name: go vet ./... + run: go vet ./... + + # ./... 不点名 cmd/gui(该目录是纯 Electron,无 .go 文件): + # 显式 `go test ./cmd/gui` 会报 "no Go files",那是误报,不是缺陷。 + - name: go test ./... + run: go test ./... -count=1 -timeout 20m + + # 跨平台客户端版本一致性:内核 internal/meta 是唯一事实源, + # GUI(package.json) / 鸿蒙(AppScope/app.json5) / waiter 都必须跟它一致。 + - name: 客户端版本一致性 + run: make check-client-versions + + # ── 竞态检测(并发改动的主要防线)── + race: + name: Race detector + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + cache: true + - name: go test -race(并发核心) + run: | + go test -race \ + ./internal/agent/core/ ./cmd/waiter/ \ + -count=1 -timeout 15m + + # ── 交叉编译:可在无 cgo 下构建的客户端/工具 ── + # + # 只有这三个 cmd 支持纯交叉编译。另外三个依赖 cgo(gojieba / onnx): + # homed / memgc / homed-kb-migrate → internal/memory(gojieba) + # homed → internal/agent/api(onnx) + # 它们必须在原生平台构建(见 Makefile 的 build target)。 + cross: + name: Cross-compile + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - goos: linux + goarch: amd64 + ext: "" + - goos: linux + goarch: arm64 + ext: "" + - goos: darwin + goarch: amd64 + ext: "" + - goos: darwin + goarch: arm64 + ext: "" + - goos: windows + goarch: amd64 + ext: ".exe" + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + cache: true + - name: 构建 ${{ matrix.goos }}/${{ matrix.goarch }} + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + CGO_ENABLED: 0 + run: | + set -euo pipefail + mkdir -p dist + for c in waiter initconfig mock-server; do + out="dist/${c}_${{ matrix.goos }}_${{ matrix.goarch }}${{ matrix.ext }}" + go build -trimpath -o "$out" "./cmd/${c}" + echo " ✓ ${c} ${{ matrix.goos }}/${{ matrix.goarch }}" + done + + # ── Electron GUI(纯 Node 测试,零依赖)── + # + # `npm test` 只跑三个 .mjs,全部只 import node: 内置模块(fs/url/path/vm), + # 所以**不需要 npm ci、不需要 electron**,秒级完成。 + # `npm run test-live` 需真 Electron + 真后端 ⇒ 不进 CI。 + gui: + name: GUI (node) + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: '22' + - name: npm test + working-directory: cmd/gui + run: npm test + + # ── C 基础设施门禁(ABI / 告警 / ASan+UBSan / 跨架构)── + csrc: + name: C infrastructure gates + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v7 + + # clang 供双编译器告警对照;gcc-aarch64 供跨架构编译门禁。 + # 门禁在缺工具时是显式 SKIP 而不是假通过,这里装齐以免静默降级。 + - name: 安装 C 工具链 + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq cmake clang gcc-aarch64-linux-gnu + + - name: make check-csrc + run: make check-csrc + + # ── 文档站构建(mkdocs,纯 Python,无外部依赖)── + docs: + name: Docs build + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-python@v7 + with: + python-version: '3.12' + - name: 校验站点配置可解析 + # 这里只做「配置与文档源没坏」的轻量校验,不做完整 mkdocs build + # (站点发布有独立流水线,见 deploy-sdk-site.sh)。 + run: | + set -euo pipefail + if [ -f mkdocs.yml ]; then + python -c \ + "import yaml; yaml.safe_load(open('mkdocs.yml'))" \ + && echo "mkdocs.yml OK" + else + echo "无 mkdocs.yml,跳过" + fi + test -d docs || echo "无 docs/,跳过"