Files
homeagent-sdk/.github/workflows/ci.yml
JianFeeeee b6a6168e65 chore(module)!: module 路径随托管地迁移 gitcode → github
SDK 仓已迁到 GitHub 作为权威源,go module 路径同步:
`gitcode.com/JianFeeeee/homeagent-sdk` → `github.com/JianFeeeee/homeagentsdk`

## 范围

74 个文件:71 个 Go 源码 + go.mod/go.sum + hmapdev 模板 + CI 与打包脚本。

主仓(HomeAgent)侧对应改动是 6d38a164。本仓这些文件当时**不在工作树上**
(见下),所以那次的批量替换扫不到它们——这里是补偿。

## 为何这批文件当时不在工作树上(事故与恢复)

10-08 07:58 主仓做 checkout/cherry-pick 序列时,把本仓工作树的 20 个文件
误清理了(`example/*/plugin.go` + `plg.json`,10 个示例)。

根因是**结构误判**:本仓是「一目录两仓库」结构下主仓 `third_party/` 里的
**独立 git 仓库**,而主仓 .gitignore 忽略了 `example/`,于是它在主仓
`git status` 里不可见 —— 看着像「不受跟踪的构建产物」,实际是本仓受跟踪的源码。

已全部恢复(`git checkout -- <D 文件>`,只恢复删除、不动迁移改动),
20 个文件与 HEAD 逐字节一致。

**破坏的连锁后果**:恢复出来的 `plugin.go` 仍写着旧 module 路径,
因为迁移时它们已不在工作树上。所以这次补偿是必须的,不是可选优化。

## 保留 gitcode 引用

以下位置的 `gitcode` **有意保留**,不要"顺手清掉":
- README.md / docs 里「二进制实际分发地址目前是 gitcode」的说明
- mkdocs.yml 的 gitcode 镜像链接(国内直连更快)
- `tools/hmapdev/templates.go` 里「SDK 已从 gitcode 迁到 GitHub」的注释

gitcode 仍是**只读镜像**(源码同步;release 附件暂时仍在那边)。

## 验证

- 本仓根 module:`go build ./...` + `go test ./...` 全通过
- 全部 20 个 example:`go vet` 通过(补偿前 10 个因旧 module 路径失败)
- example/qq:`go test` 通过,且能从源码 build 出可用 plugin 产物
2026-10-09 07:25:25 +08:00

252 lines
10 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 SDK 仓 CI。
#
# 与 release.yml 分工:本文件管「推送到 main / PR 时的检查」,release.yml 管
# 「release/** 推送时的发版」。此前本仓**只有** release.yml —— main 的推送与
# PR 完全没有检查,而 SDK 是被外部插件开发者直接依赖的契约面(sdk/plugin.go
# 的接口一旦破,所有外部插件编译失败),必须有门。
#
# 设计原则与主仓一致:**CI 里每条命令都是本地已实测通过的**。
# 不写「应该有用来试试」的步骤 —— 未验证的 CI 步骤会把假红灯变成常态。
#
# 明确不进 CI 的:
# - `hmapdev skill install`(会写 ~/.claude、~/.codex 等**开发者本机目录**)
# - 需要内核仓在场的检查(本仓独立可测;sync-lua-sdk.sh 会在缺内核时
# 明确 skip 而不是假装成功)
# - 任何网络/真机依赖
name: CI
on:
push:
branches: [main, 'release/**']
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
env:
# SDK 本身不需要 cgo(hmapdev 与 sdk 包都是纯 Go)—— 与主仓相反。
CGO_ENABLED: 0
GOFLAGS: -buildvcs=false
jobs:
# ── 根模块:SDK 对外接口所在 ──
go:
name: Go build / vet / test
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
# ★ 钉 1.25 而不是 go-version-file: go.mod —— 根 go.mod 写的是
# go 1.21.0,但 20 个 example/*/go.mod 都要求 go 1.25.0。
# 读根 go.mod 会装 1.21,examples 构建必然失败
# ("go.mod requires go >= 1.25.0 (running go 1.21.0; GOTOOLCHAIN=local)")。
# GitHub runner 上 GOTOOLCHAIN=local,Go 不会自动下载更高工具链,
# 所以本地「能过」不代表 CI 能过。
- uses: actions/setup-go@v7
with:
go-version: '1.25'
cache: true
- name: go build ./...
run: go build ./...
- name: go vet ./...
run: go vet ./...
- name: go test ./...
run: go test ./... -count=1 -timeout 10m
# ── hmapdev 工具链(嵌套 module,根模块的 ./... 不会进入它)──
hmapdev:
name: hmapdev (${{ matrix.goos }}/${{ matrix.goarch }})
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
# ★ 钉 1.25 而不是 go-version-file: go.mod —— 根 go.mod 写的是
# go 1.21.0,但 20 个 example/*/go.mod 都要求 go 1.25.0。
# 读根 go.mod 会装 1.21,examples 构建必然失败
# ("go.mod requires go >= 1.25.0 (running go 1.21.0; GOTOOLCHAIN=local)")。
# GitHub runner 上 GOTOOLCHAIN=local,Go 不会自动下载更高工具链,
# 所以本地「能过」不代表 CI 能过。
- uses: actions/setup-go@v7
with:
go-version: '1.25'
cache: true
# tools/hmapdev 是**独立 module**(有自己的 go.mod)——
# 根模块的 `go test ./...` 不会进入它,必须单独跑,否则它的测试
# 永远不在 CI 里执行。这是 Go 的模块边界,不是配置问题。
- name: go test(嵌套 module)
working-directory: tools/hmapdev
run: go test ./... -count=1 -timeout 10m
# main 包在 tools/hmapdev —— 不指定工作目录会在仓库根跑 `go build .`,
# 而根目录没有 Go 文件(包在 sdk/ 子目录),报 "no Go files in ..."。
- name: 交叉编译
working-directory: tools/hmapdev
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
run: |
set -euo pipefail
out="hmapdev_${{ matrix.goos }}_${{ matrix.goarch }}${{ matrix.ext }}"
go build -trimpath \
-ldflags "-X github.com/JianFeeeee/homeagentsdk/meta.Version=0.0.0-ci" \
-o "/tmp/$out" .
test -s "/tmp/$out" || { echo "ERROR: 未产出 $out"; exit 1; }
printf " ✓ %-28s %s 字节\n" "$out" "$(stat -c %s "/tmp/$out")"
file "/tmp/$out"
# ── 示例插件:发版产物里最容易被使用者直接安装的东西 ──
#
# 为什么必须验:示例是使用者的模板(拷了就改),且**发版会连同 .hmap 一起发**
# —— 插件二进制与内核是协议绑定的(ProtocolVersion + 统一共享内存区魔数),
# 示例编不出来就意味着这次发版发不出配套产物。
#
# ★ 示例**不能**用 `go build` 验:它们是插件(只有 plugin.go、没有 func main),
# 必须由 hmapdev 注入 main 包装。直接 go build 会得到
# "function main is undeclared in the main package" —— 那不是缺陷,
# 是构建方式不对(本地实测确认)。
examples:
name: Examples (${{ matrix.target }})
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
# windows 不在列:build-examples.sh 会明确拒绝(协议 2 的统一共享
# 内存区未移植到 Windows),那是有意的设计而不是待修缺陷。
target: ['linux/amd64', 'darwin/arm64']
steps:
- uses: actions/checkout@v7
# ★ 钉 1.25 而不是 go-version-file: go.mod —— 根 go.mod 写的是
# go 1.21.0,但 20 个 example/*/go.mod 都要求 go 1.25.0。
# 读根 go.mod 会装 1.21,examples 构建必然失败
# ("go.mod requires go >= 1.25.0 (running go 1.21.0; GOTOOLCHAIN=local)")。
# GitHub runner 上 GOTOOLCHAIN=local,Go 不会自动下载更高工具链,
# 所以本地「能过」不代表 CI 能过。
- uses: actions/setup-go@v7
with:
go-version: '1.25'
cache: true
- name: 构建 hmapdev(宿主)
run: |
set -euo pipefail
cd tools/hmapdev
go build -o "$GITHUB_WORKSPACE/build/hmapdev_host" .
# 用发版同一个脚本(package/build-examples.sh),避免 CI 与发版路径分叉。
- name: 构建全部示例
env:
HMAPDEV: ${{ github.workspace }}/build/hmapdev_host
TARGET: ${{ matrix.target }}
run: |
set -euo pipefail
bash package/build-examples.sh "$TARGET" /tmp/examples
echo
n=$(find /tmp/examples -name '*.hmap' | wc -l)
echo " .hmap 产物数: $n"
# 示例目录数应与产物数一致(少一个都要红)
want=$(find example -mindepth 2 -maxdepth 2 -name plugin.go | wc -l)
echo " 示例源码数: $want"
[ "$n" -ge "$want" ] || { echo "ERROR: 产物少于示例数"; exit 1; }
for f in /tmp/examples/*.hmap; do
printf " %9.1fKB %s\n" \
"$(stat -c %s "$f" | awk '{print $1/1024}')" "$(basename "$f")"
done
# ── 一致性与文档 ──
consistency:
name: Consistency & docs
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
# sdk.lua 有**四份**副本(本仓三份 + 内核仓一份),历史上漂移过,
# 出现「mock 有、内核没有」的静默失配。此处钉住本仓内的三份。
- name: Lua SDK 副本一致性
run: |
set -euo pipefail
canon=sdk/lua/sdk.lua
[ -f "$canon" ] || { echo "ERROR: 缺 $canon"; exit 1; }
want=$(sha256sum "$canon" | cut -d' ' -f1)
fail=0
for f in tools/hmapdev/assets/sdk.lua example/luademo/sdk.lua; do
have=$(sha256sum "$f" | cut -d' ' -f1)
if [ "$have" = "$want" ]; then
echo " ✓ $f"
else
echo " ✗ $f 与 $canon 不一致" >&2
echo " canon=$want" >&2
echo " this =$have" >&2
echo " 解法:bash scripts/sync-lua-sdk.sh" >&2
fail=1
fi
done
[ "$fail" = "0" ] || exit 1
# ★ 钉 1.25 而不是 go-version-file: go.mod —— 根 go.mod 写的是
# go 1.21.0,但 20 个 example/*/go.mod 都要求 go 1.25.0。
# 读根 go.mod 会装 1.21,examples 构建必然失败
# ("go.mod requires go >= 1.25.0 (running go 1.21.0; GOTOOLCHAIN=local)")。
# GitHub runner 上 GOTOOLCHAIN=local,Go 不会自动下载更高工具链,
# 所以本地「能过」不代表 CI 能过。
- uses: actions/setup-go@v7
with:
go-version: '1.25'
cache: true
# skills/ 会随 SDK store 分发给所有 agent(hmapdev skill install)。
# skill list 会读 skills/ 目录并报告可用项 —— 目录结构坏掉会在这里红。
- name: 技能目录可被 hmapdev 识别
run: |
set -euo pipefail
# ★ 必须把 hmapdev 建在**仓库内**:resolveSkillsSource() 先从
# 可执行文件位置向上找仓库(tools/hmapdev → ../../skills),
# 落空才回退到 SDK store 的活跃版本 —— 而 CI 里没有 store,
# 于是报 "no active SDK version set"。建到 /tmp 就会落空。
mkdir -p build
( cd tools/hmapdev && go build -o "$GITHUB_WORKSPACE/build/hmapdev_ci" . )
./build/hmapdev_ci skill list
./build/hmapdev_ci skill path
- uses: actions/setup-python@v7
with:
python-version: '3.12'
# 文档站会被部署(deploy-sdk-site.sh)——站点构建坏了此前无从发现。
# --strict 把 warning 升级为 error:断链、缺失引用都会红。
#
# ★ 不能用 yaml.safe_load 校验 mkdocs.yml:它含 mkdocs-material 的
# `!!python/name:` 标签(配置 emoji 的官方写法),safe_load 处理不了,
# 会报 ConstructorError —— 那是校验方式不对,不是配置有问题。
- name: 文档站构建(mkdocs --strict)
run: |
set -euo pipefail
python3 -m pip install --quiet mkdocs-material
mkdocs build --strict --site-dir /tmp/site