mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-22 09:58:06 +00:00
docs: 文档与发布脚本同步到 v1.0.0 子进程架构
README/架构文档仍在描述 C ABI 动态库加载,与 v1.0.0 实际实现不符。
新用户按文档走会去做 -buildmode=c-shared,产物新内核根本不加载。
README.md / README_EN.md:
- 设计要点补子进程架构段(三面通信、崩溃自愈、真热重载)
- 代码结构 plugin/ 描述:.so 动态加载器 → 子进程加载器
- 项目状态补 v1.0.0 条目(6 类缺陷 + 实测数字),v0.9.0 标注 ABI 已退场
- 新增「下载」章节:三变体对照 + 各平台包格式 + macOS 限制
assets/docs/{zh,en}/ARCHITECTURE.md:
- 四种加载方式表:外部 .so/C ABI → 外部子进程/握手+stdio JSON-RPC
- 加载流程改写为 exec.Command → 继承 fd → 握手 → init → start
- 内置 vs 外部对照表 7 行更新
- 新增「子进程插件的三个通信面」小节,含每个面的选择理由
assets/docs/{zh,en}/OVERVIEW.md:插件系统段落改写
deploy/ 发布脚本三处回归(v0.7.2 的 2c5f9ff 把 package/ 移到
deploy/packaging/ 使目录深度 1→2,但没改相对路径,此后两个版本
的发布都没有二进制资产):
- build.sh:.syso 按目标平台 hide/restore(trap 兜底),恢复
windows 目标的 CXX,arm64 刻意不带 CXX
- installer.nsi:5 处 ..\build → ..\..\build,PRODUCT_VERSION 可注入
(原先硬编码 0.8.0)
- homeagent.spec:server 变体补装 waiter(control-server 声明了 CLI 却没装)
deploy/scripts/upload_assets.py:release 资产上传(两步签名 URL → OBS
PUT)。放 deploy/scripts/ 而非 scripts/,因为后者在 .gitignore 里。
支持 GITCODE_REPO/ASSET_DIR 环境变量以复用于 SDK 仓。
This commit is contained in:
@ -27,10 +27,15 @@ COMPONENT="${2:-all}"
|
||||
case "$TARGET" in
|
||||
native) GOOS="" GOARCH="" ;;
|
||||
linux/amd64) GOOS=linux GOARCH=amd64 CC="${CC:-}" ;;
|
||||
# arm64 刻意不设 CXX:设了会让 Go 用 aarch64 的 g++ 去链接,
|
||||
# 而它对 host 产生的 .o 报 "file format not recognized"。
|
||||
# gojieba 的 C++ 源仍由 CC 对应的 gcc 驱动编译(gcc 能编 C++)。
|
||||
linux/arm64) GOOS=linux GOARCH=arm64 CC="${CC:-aarch64-linux-gnu-gcc}" ;;
|
||||
darwin/amd64) GOOS=darwin GOARCH=amd64 CC="${CC:-}" ;;
|
||||
darwin/arm64) GOOS=darwin GOARCH=arm64 CC="${CC:-}" ;;
|
||||
windows/amd64) GOOS=windows GOARCH=amd64 CC="${CC:-x86_64-w64-mingw32-gcc}" ;;
|
||||
# Windows 必须同时给 CXX:gojieba 是 C++,缺 CXX 时 cgo 回退到宿主 g++,
|
||||
# 而宿主 g++ 不认 mingw 的 -mthreads,报 unrecognized command-line option。
|
||||
windows/amd64) GOOS=windows GOARCH=amd64 CC="${CC:-x86_64-w64-mingw32-gcc}" CXX="${CXX:-x86_64-w64-mingw32-g++}" ;;
|
||||
all)
|
||||
"$0" linux/amd64 "$COMPONENT"
|
||||
"$0" linux/arm64 "$COMPONENT"
|
||||
@ -60,6 +65,34 @@ export CGO_ENABLED="${CGO_ENABLED:-1}"
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
# ---- .syso 隔离 ----
|
||||
#
|
||||
# cmd/{homed,waiter}/*.syso 是 Windows 资源对象(COFF,含图标/版本信息)。
|
||||
# Go 会把同目录的 .syso 无条件链进任何目标,于是交叉编译到非 Windows 平台时:
|
||||
# - linux/arm64、darwin/arm64 报 "unknown ARM64 relocation type 3"
|
||||
# - 其他架构报 "file format not recognized"
|
||||
# package-linux.sh 有 hide_syso(),但直接调本脚本时没有那层保护——
|
||||
# 这正是 arm64 产物长期缺失的原因(曾被误判为缺 g++ 交叉编译器)。
|
||||
SYSO_HIDDEN=()
|
||||
hide_syso_for_target() {
|
||||
[ "${GOOS:-}" = "windows" ] && return 0
|
||||
local f
|
||||
for f in "$PROJECT_ROOT"/cmd/homed/*.syso "$PROJECT_ROOT"/cmd/waiter/*.syso; do
|
||||
[ -f "$f" ] || continue
|
||||
mv "$f" "$f.hidden"
|
||||
SYSO_HIDDEN+=("$f")
|
||||
done
|
||||
}
|
||||
restore_syso_for_target() {
|
||||
local f
|
||||
for f in "${SYSO_HIDDEN[@]:-}"; do
|
||||
[ -n "$f" ] && [ -f "$f.hidden" ] && mv "$f.hidden" "$f"
|
||||
done
|
||||
SYSO_HIDDEN=()
|
||||
}
|
||||
trap restore_syso_for_target EXIT
|
||||
hide_syso_for_target
|
||||
|
||||
# ---- homed (CGO, sqlite3) ----
|
||||
build_homed() {
|
||||
local out="$BUILD_DIR/homed${SUFFIX:+_$SUFFIX}"
|
||||
|
||||
@ -10,7 +10,12 @@
|
||||
|
||||
!define PRODUCT_NAME "HomeAgent"
|
||||
!define PRODUCT_PUBLISHER "HomeAgent Team"
|
||||
!define PRODUCT_VERSION "0.8.0"
|
||||
# 版本号由 makensis -DPRODUCT_VERSION=X.Y.Z 注入;缺省值仅供本地手工构建。
|
||||
# 此前硬编码 0.8.0 而 release 已到 1.0.0,装出来的包在「添加/删除程序」里
|
||||
# 会显示错误版本(DisplayVersion 也取自这个宏)。
|
||||
!ifndef PRODUCT_VERSION
|
||||
!define PRODUCT_VERSION "1.0.0"
|
||||
!endif
|
||||
|
||||
!if "${VARIANT}" == "full"
|
||||
!define PRODUCT_DISPLAY_NAME "HomeAgent 完整版"
|
||||
@ -38,7 +43,7 @@
|
||||
!endif
|
||||
|
||||
Name "${PRODUCT_DISPLAY_NAME}"
|
||||
OutFile "..\build\${OUTPUT_FILE}"
|
||||
OutFile "..\..\build\${OUTPUT_FILE}"
|
||||
InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}"
|
||||
InstallDirRegKey HKLM "Software\${PRODUCT_NAME}" ""
|
||||
RequestExecutionLevel admin
|
||||
@ -217,17 +222,17 @@ Section "Install" SEC_INSTALL
|
||||
CreateDirectory "$INSTDIR\data\adapters"
|
||||
|
||||
!if "${HAS_CORE}" == "1"
|
||||
File "..\build\initconfig.exe"
|
||||
File "..\build\homed.exe"
|
||||
File "..\..\build\initconfig.exe"
|
||||
File "..\..\build\homed.exe"
|
||||
!endif
|
||||
|
||||
!if "${HAS_WAITER}" == "1"
|
||||
File "..\build\waiter.exe"
|
||||
File "..\..\build\waiter.exe"
|
||||
!endif
|
||||
|
||||
!if "${HAS_GUI}" == "1"
|
||||
SetOutPath "$INSTDIR\homeagent-gui-win32-x64"
|
||||
File /r "..\build\homeagent-gui-win32-x64\*.*"
|
||||
File /r "..\..\build\homeagent-gui-win32-x64\*.*"
|
||||
SetOutPath "$INSTDIR"
|
||||
!endif
|
||||
|
||||
|
||||
@ -63,9 +63,9 @@ install -m 755 %{_sourcedir}/homed %{buildroot}%{_bindir}/homed
|
||||
install -m 644 %{_sourcedir}/homeagent.service %{buildroot}%{_unitdir}/homeagent.service
|
||||
%endif
|
||||
|
||||
%if "%{variant}" == "full" || "%{variant}" == "client"
|
||||
# waiter 三个变体都要:server 也含 CLI(对齐 deb 的 stage_variant
|
||||
# 与 control-server 的 "waiter: command-line client" 描述)。
|
||||
install -m 755 %{_sourcedir}/waiter %{buildroot}%{_bindir}/waiter
|
||||
%endif
|
||||
|
||||
%if "%{variant}" == "full" || "%{variant}" == "client"
|
||||
mkdir -p %{buildroot}%{_datadir}/homeagent-gui
|
||||
@ -81,9 +81,7 @@ cp -r %{_sourcedir}/homeagent-gui-linux-*/* %{buildroot}%{_datadir}/homeagent-gu
|
||||
%dir %{_varlibdir}
|
||||
%endif
|
||||
|
||||
%if "%{variant}" == "full" || "%{variant}" == "client"
|
||||
%{_bindir}/waiter
|
||||
%endif
|
||||
|
||||
%if "%{variant}" == "full" || "%{variant}" == "client"
|
||||
%dir %{_datadir}/homeagent-gui
|
||||
|
||||
107
deploy/scripts/upload_assets.py
Executable file
107
deploy/scripts/upload_assets.py
Executable file
@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
"""上传 release 资产到 gitcode(两步:取签名 URL → PUT 到 OBS)。
|
||||
|
||||
用法: upload_assets.py <tag> <token> [file...]
|
||||
不传 file 时上传 dist/release/ 下全部发布产物。
|
||||
|
||||
环境变量:
|
||||
GITCODE_REPO 目标仓库,默认 JianFeeeee/HomeAgent(SDK 仓传 JianFeeeee/homeagent-sdk)
|
||||
ASSET_DIR 资产目录,默认 <repo>/dist/release
|
||||
|
||||
为何两步:gitcode 的 release 附件不走 API 直传,而是先向
|
||||
`releases/<tag>/upload_url` 要一个 OBS 预签名 URL(带 x-obs-* 回调头),
|
||||
再把文件 PUT 到那个 URL。回调头必须原样透传,否则 OBS 收下了文件但
|
||||
gitcode 侧不会登记为 release 附件。
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
REPO = os.environ.get("GITCODE_REPO", "JianFeeeee/HomeAgent")
|
||||
API = "https://gitcode.com/api/v5/repos"
|
||||
|
||||
# 发布产物后缀。注意 Windows 安装器是 HomeAgent_v*_win64.exe,
|
||||
# 与 bin/ 里的裸 .exe 靠 _win64.exe 后缀区分。
|
||||
ARTIFACT_SUFFIXES = (
|
||||
".tar.gz",
|
||||
".zip",
|
||||
".deb",
|
||||
".rpm",
|
||||
".pkg",
|
||||
"_win64.exe",
|
||||
)
|
||||
|
||||
|
||||
def is_artifact(name: str) -> bool:
|
||||
return name == "SHA256SUMS" or name.endswith(ARTIFACT_SUFFIXES)
|
||||
|
||||
|
||||
def get_upload_url(tag: str, token: str, filename: str) -> tuple[str, dict]:
|
||||
q = urllib.parse.urlencode({"file_name": filename})
|
||||
url = f"{API}/{REPO}/releases/{tag}/upload_url?{q}"
|
||||
req = urllib.request.Request(url, headers={"private-token": token})
|
||||
with urllib.request.urlopen(req, timeout=30) as r:
|
||||
data = json.loads(r.read())
|
||||
return data["url"], data.get("headers", {})
|
||||
|
||||
|
||||
def put_file(url: str, headers: dict, path: str) -> tuple[int, str]:
|
||||
size = os.path.getsize(path)
|
||||
with open(path, "rb") as f:
|
||||
body = f.read()
|
||||
req = urllib.request.Request(url, data=body, method="PUT")
|
||||
for k, v in headers.items():
|
||||
req.add_header(k, v)
|
||||
req.add_header("Content-Length", str(size))
|
||||
try:
|
||||
# 大文件(Full 变体安装包近 100MB)给足超时。
|
||||
with urllib.request.urlopen(req, timeout=900) as r:
|
||||
return r.status, r.read().decode("utf-8", "replace")[:300]
|
||||
except urllib.error.HTTPError as e:
|
||||
return e.code, e.read().decode("utf-8", "replace")[:300]
|
||||
except Exception as e: # noqa: BLE001
|
||||
return 0, f"{type(e).__name__}: {e}"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) < 3:
|
||||
print(__doc__)
|
||||
return 2
|
||||
tag, token = sys.argv[1], sys.argv[2]
|
||||
outdir = os.environ.get("ASSET_DIR") or os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
"dist",
|
||||
"release",
|
||||
)
|
||||
files = sys.argv[3:] or sorted(
|
||||
f for f in os.listdir(outdir) if is_artifact(f)
|
||||
)
|
||||
print(f"repo={REPO} tag={tag} dir={outdir}", flush=True)
|
||||
failed = []
|
||||
for name in files:
|
||||
path = os.path.join(outdir, name)
|
||||
if not os.path.isfile(path):
|
||||
print(f"skip (missing): {name}", flush=True)
|
||||
continue
|
||||
mib = os.path.getsize(path) / 1048576
|
||||
print(f"==> {name} ({mib:.1f} MiB)", flush=True)
|
||||
try:
|
||||
url, headers = get_upload_url(tag, token, name)
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f" upload_url FAILED: {e}", flush=True)
|
||||
failed.append(name)
|
||||
continue
|
||||
status, body = put_file(url, headers, path)
|
||||
ok = 200 <= status < 300
|
||||
print(f" PUT -> {status} {'OK' if ok else body}", flush=True)
|
||||
if not ok:
|
||||
failed.append(name)
|
||||
print(f"\n{'ALL OK' if not failed else f'{len(failed)} FAILED: ' + ', '.join(failed)}")
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user