Documentation
¶
Overview ¶
Package selfupdate implements ARTEX 的页面一键更新:从 GitHub Release 拉取新版 二进制、校验、暂存,并在下次启动时原子换装。
整体分工(见 start.sh / start.bat):
启动脚本 = 傻瓜守护循环,只负责"进程退出后按退出码决定是否再拉起" 本包 = 全部易错逻辑(下载 / SHA256 校验 / 冒烟 / 换装 / 失败回滚)
之所以把换装放在 Go 而不是脚本里,是因为 sha256 校验和冒烟测试在 sh 和 bat 上 要写两套(sha256sum / shasum / certutil),而这恰恰是最不能出错的一环——换上一个 跑不起来的二进制,守护进程会忠实地反复拉起它,用户只能上机器手工救。
一次完整升级经过三次进程启动:
① 旧版 server 收到 /api/update/apply → 下载校验 → 暂存 artex.new → exit 75 ② 脚本重新拉起旧版 → Bootstrap 发现 artex.new → 校验+冒烟 → 换装 → exit 75 ③ 脚本重新拉起,此时已是新版 → Bootstrap 记一次尝试 → 启动成功后清除标记
任何一步失败都退回旧版:② 校验不过就删掉暂存件继续跑旧版;③ 连续 3 次没活到 清除标记(起不来就崩)则自动把 artex.old 换回去。
Index ¶
- Constants
- func AssetName(tag, goos, goarch string) string
- func Bootstrap() (Action, State)
- func CompareVersions(a, b string) (int, bool)
- func HasBackup() bool
- func InDocker() bool
- func NewClient(proxy string) *http.Client
- func Rollback() error
- func Settle()
- func Stage(ctx context.Context, c *http.Client, rel *Release, currentVersion string, ...) error
- type Action
- type Asset
- type Paths
- type Phase
- type Progress
- type Release
- type State
Constants ¶
const ExitRestart = 75
ExitRestart 是"请守护进程重新拉起我"的退出码(EX_TEMPFAIL)。启动脚本看到它 就立刻重跑,不计入崩溃退避。0 表示用户正常停止(脚本退出循环),其余均视为崩溃。
const Repo = "Autumn-27/artex"
Repo 是发布源。写死而不是做成配置项:更新源可配等于给任何能改配置的人一条 远程代码执行通道,对一个渗透测试平台来说这个口子开不得。
const SettleDelay = 30 * time.Second
SettleDelay 是判定"新版本活下来了"所需的运行时长。
Variables ¶
This section is empty.
Functions ¶
func AssetName ¶
AssetName 返回当前平台对应的发布包名,与 build.sh 的 package_binary 保持一致: artex-<版本>-<os>-<arch>.zip(版本号不带 v 前缀)。
func Bootstrap ¶
Bootstrap 在 main 的最开头运行,必须在任何监听端口、打开数据库之前调用。
三种局面:
① 存在暂存件 artex.new → 校验 + 冒烟,通过则换装并要求重启;不通过则丢弃继续跑旧版 ② 只剩标记文件 → 说明刚换装完,累计一次尝试;连续失败够多次则回滚 ③ 什么都没有 → 正常启动
func CompareVersions ¶
CompareVersions 比较两个版本号,返回 -1/0/1(a<b / a==b / a>b)。 ok=false 表示至少一边不是可比较的版本号(例如本地开发构建的 "dev" 或 git describe 产出的 "0.3.7-2-gabc1234-dirty"),此时调用方应禁用一键更新, 否则会把开发中的构建"升级"成正式版、覆盖掉未提交的改动。
func InDocker ¶
func InDocker() bool
InDocker 报告进程是否跑在容器里。Docker 下换装写的是容器可写层, `docker compose up -d` 重建容器会退回镜像自带的版本——这是预期行为 (那时用户本来就在拉新镜像),但前端要能据此把话说清楚。
func NewClient ¶
NewClient 构造一个只认 GitHub 域名的 HTTP 客户端。proxy 为空则直连。
刻意不复用默认 Transport:升级链路必须强制走 TLS 且校验证书,不能被别处 设置的 InsecureSkipVerify 之类影响到。
func Rollback ¶
func Rollback() error
Rollback 是 /api/update/rollback 的实现:主动退回上一版本。 只做换装,重启同样交给守护脚本(调用方随后以 ExitRestart 退出)。
func Settle ¶
func Settle()
Settle 确认新版本已稳定运行,清除升级标记。
由 main 在 HTTP 监听起来之后延迟调用:活过这段时间才算数,否则标记留在原地, 下次启动继续累计尝试次数,直到触发回滚。
func Stage ¶
func Stage(ctx context.Context, c *http.Client, rel *Release, currentVersion string, prog Progress) error
Stage 下载指定 Release 的当前平台发布包,校验后把新二进制暂存为 artex.new。
走的是完整 zip 而不是裸二进制,理由有两个:现有 Release 的 SHA256SUMS 本来就 只覆盖 zip,走 zip 不需要改 CI,也能兼容已经发布出去的历史版本;zip 里还带着 skills/,为将来同步内置 skill 留了口子。代价只是多下载 skills 那几百 KB。
函数返回即代表暂存完成,调用方随后优雅关闭并以 ExitRestart 退出。
Types ¶
type Asset ¶
type Asset struct {
Name string `json:"name"`
URL string `json:"browser_download_url"`
Size int64 `json:"size"`
}
Asset 是 Release 上挂的一个文件。
type Paths ¶
type Paths struct {
Dir string // 可执行文件所在目录
Current string // 当前运行的二进制 artex / artex.exe
New string // 暂存的新版本 artex.new / artex.new.exe
Sum string // 新版本的 sha256(hex) artex.new.sha256 / artex.new.exe.sha256
Old string // 换装前备份的旧版本 artex.old / artex.old.exe
Marker string // 升级状态标记 artex.upgrade.json
}
Paths 是一次升级涉及的全部文件,统一挂在**可执行文件所在目录**下。 刻意不用 CWD:服务化运行时工作目录可能是 / 或任意路径,用 CWD 会让暂存件落到 别处,换装逻辑直接失效。
func ResolvePaths ¶
ResolvePaths 按当前可执行文件推导全部升级路径。
Windows 上 .new/.old 也必须带 .exe 后缀,否则冒烟测试和换装后的执行都会失败, 所以先把后缀摘掉再拼,两个平台的命名才对称。
type Release ¶
type Release struct {
TagName string `json:"tag_name"`
Name string `json:"name"`
Body string `json:"body"`
Draft bool `json:"draft"`
Prerelease bool `json:"prerelease"`
PublishedAt time.Time `json:"published_at"`
HTMLURL string `json:"html_url"`
Assets []Asset `json:"assets"`
}
Release 是 GitHub Release 里我们关心的字段。
func FetchLatest ¶
FetchLatest 查询最新正式版。