Files
HomeAgent/.github/workflows/ci.yml
xiao-zhai 75b4c50fa2 Merge main into refactor/rename-batch6-site-docs:吸收前置检查重试脚本,GC_REPO 收为 job 级单一定义点
PR #19 合入后 main 上 ci.yml / release.yml 的前置检查换成了公共脚本
.github/scripts/check-gitcode-repo.sh,与本批「GC_REPO 收为单一定义点」
改了同一段代码,故冲突。解决口径:

- 前置检查主体取 main 的重试脚本版本(不保留本批内联的那份长 case,
  重试/退避/notfound-优先于-403 的能力保留)
- GC_REPO 仍收为 job 级 env 单一定义处:main 上 ci.yml 两处、release.yml
  三处字面量(含 upload_assets 调用里那处)全部改为走 ${GC_REPO}
- 结果:每个文件内部只有 1 个定义点,注释里的实测清单同步为当前状态

零代码改动:.go/.lua/go.mod/go.sum/third_party 变更数 0。
YAML safe_load 两文件通过;check-gitcode-repo.sh bash -n 通过。
2026-10-22 07:17:57 +08:00

298 lines
12 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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。
#
# endpoint-align.test.mjs 跨出 cmd/gui 读 ../../internal/plugins/webui/handler.go
# 做端点对账 —— checkout 深度足够(默认 fetch-depth:1 已含工作树文件),
# 不需要额外 checkout 步骤。
#
# connections-io.test.mjs 同样是沙箱里跑 main.js 的**真实函数**(node:vm),
# 在临时目录里断言连接配置的加载/保存行为,不碰真实用户数据。
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/scripts/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/,跳过"
# ── gitcode 镜像同步(main 分支)──
#
# 背景(2026-10-06 审计):gitcode 的定位是**只读镜像**(ci-cd-runbook §0),
# 但既有 sync job 只在 release.yml 里推 **tag** —— main 分支从不同步,
# 导致镜像的 main 停在 09-29、与主仓分叉 768/746(当时已 force push 对齐一次)。
#
# 本 job 把「镜像 = 主仓」交给 CI 保证:main 每次推送且**全部必需检查通过后**
# 才快进镜像分支 —— 红的代码不进镜像,镜像永远等于主仓。
#
# - 只处理 main(feature/release 分支不镜像,与 tag 语义一致)。
# - 未配置 GITCODE_TOKEN 时显式跳过(与 release.yml 的 sync-gitcode 同口径,
# 不阻断 GitHub 侧 CI)。
# - 用快进 push(非 force):正常流水线下镜像只可能落后,不可能领先;
# 若出现非快进说明有人动过镜像,此时响亮失败而不是悄悄覆盖 ——
# 镜像被旁路写入是「单向性被破坏」的信号,值得一次人工确认。
sync-gitcode-main:
name: Sync main to gitcode (mirror)
runs-on: ubuntu-latest
needs: [go, race, cross, gui, csrc, docs]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
timeout-minutes: 10
env:
# ══════════════════════════════════════════════════════════════════
# ★★ gitcode 镜像目标仓名 —— **本 job 的唯一定义处**。
# 改 gitcode 仓名时只改这一行;下面所有消费点都走 ${GC_REPO}。
# 与 release.yml 的 sync-gitcode 保持同值(两个 workflow 各自独立
# 定义:改的时候两个文件都要改,但每个文件内部只有一个定义处)。
#
# 取值依据(实测 2026-10-21,直连不带代理,非推测):
# https://gitcode.com/JianFeeeee/HologramAgent → 200 ← 当前用这个
# https://gitcode.com/JianFeeeee/HomeAgent → 301 ← 旧名,跳转到上面
# https://gitcode.com/JianFeeeee/hologramagentsdk → 200(SDK 仓,另一对象)
# https://gitcode.com/JianFeeeee/homeagentsdk → 404
#
# 背景:gitcode 侧镜像仓已由 HomeAgent 改名为 HologramAgent(与 GitHub
# 主仓同名)。旧地址仍 301 跳转,跟随后也能到达,但走重定向 ⇒ 别再用它。
# 判定两仓同一对象:两地址 git ls-remote 的 refs 列表 md5 相同。
#
# ⚠ 失败面:本 job 的 needs 是 [go, race, cross, gui, csrc, docs]。
# 前置检查 exit 1 ⇒ 本 job 红 ⇒ **main 的整条 CI 红**(不是只红一格)。
# release.yml 的 sync-gitcode 只红在镜像同步这一格(GitHub 侧产物已发)。
# ══════════════════════════════════════════════════════════════════
GC_REPO: JianFeeeee/HologramAgent
steps:
- id: tok
name: 检查 gitcode 凭据
run: |
if [ -n "${{ secrets.GITCODE_TOKEN }}" ]; then
echo "ok=true" >> "$GITHUB_OUTPUT"
else
echo "ok=false" >> "$GITHUB_OUTPUT"
echo " 未配置 GITCODE_TOKEN —— 跳过 gitcode main 同步"
fi
- uses: actions/checkout@v7
if: steps.tok.outputs.ok == 'true'
with:
# 必须取完整历史:镜像可能在任意旧提交上,快进需要共同祖先。
fetch-depth: 0
# ★ 改名(HomeAgent → HologramAgent)后的必然过渡态:gitcode 上的镜像仓
# 是**另一个账号下的独立仓库**,改 GitHub 仓名不会自动建出它。
# 若不在这里显式探测,缺少目标仓时只会得到 git 的原始报错
# ("repository not found" 混在 push 输出里),看不出是「仓没建」。
# 语义:token 未配置 = 可选项跳过(上面那步);token 有但仓不存在 = 硬失败。
#
# ★ 有界重试(2026-10-21 dfc4e16 的教训):首次跑这一步时 gitcode 网络抖动,
# 一次 ls-remote 判失败 ⇒ job 红;重跑立刻全绿。现在只对**网络层**失败
# 重试(3 次,退避 5s/10s),而「仓不存在」与「凭据问题」**立即失败不重试**
# —— 后两者重试只会把真问题拖长、并把它埋在噪声里。
#
# 实现落在 .github/scripts/check-gitcode-repo.sh,与 release.yml 的
# sync-gitcode **共用同一个文件**(同口径靠同一份代码保证,不靠人抄两遍)。
- name: 前置检查 gitcode 目标仓是否存在
if: steps.tok.outputs.ok == 'true'
env:
GC_TOKEN: ${{ secrets.GITCODE_TOKEN }}
# GC_REPO 来自 job 级 env(唯一定义处),此处不重复字面量。
run: |
set -euo pipefail
bash .github/scripts/check-gitcode-repo.sh
- name: 推 main 到 gitcode(快进)
if: steps.tok.outputs.ok == 'true'
env:
GC_TOKEN: ${{ secrets.GITCODE_TOKEN }}
run: |
set -euo pipefail
GC_URL="https://JianFeeeee:${GC_TOKEN}@gitcode.com"
# GC_REPO 来自 job 级 env(唯一定义处),此处不重复字面量。
git push "${GC_URL}/${GC_REPO}.git" \
"refs/heads/main:refs/heads/main"
echo " gitcode/main 已快进到 ${GITHUB_SHA}"