versionup

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 23 Imported by: 0

README

versionup

Go Reference

versionup 是一个纯 Go 实现的桌面软件版本升级公共库。它帮助客户端周期性向服务端询问工具的最新稳定版本,与当前版本比对,发现更新时发出升级事件(含下载地址、版本号、工具 ID 等),并提供事件上报、通知开关、定时通知、版本下载等能力。

设计边界:库负责「发现 + 通知 + 下载」,不内置「自替换 / 重启 / 提权 / 配置迁移 / 回滚」等平台相关执行逻辑——这些通过 Applier 接口由调用方注入(详见应用层)。

特性

  • 周期检查 / 立即检查(Start / CheckNow
  • 语义化版本比对(SemVer 2.0,预发布不参与稳定升级)
  • 升级事件分发(Notifier)与事件上报(Reporter
  • 通知开关(Enable / Disable)与指定时间通知(NotifyAt
  • HTTP 下载(进度回调 + sha256 校验 + 原子落盘)
  • 零 CGO、仅依赖标准库,跨平台编译
  • 可组合:所有组件均为接口,可自定义实现

Install

go get github.com/y288cn/versionup

要求 Go 1.22+(本仓库使用 go 1.22)。

Quick Start

package main

import (
    "context"
    "log"
    "time"

    versionup "github.com/y288cn/versionup"
)

func main() {
    up := versionup.New(
        "github-speed", "v1.3.0",
        versionup.WithSource(versionup.NewHTTPSource("https://api.example.com")),
        versionup.WithReporter(versionup.NewHTTPReporter("https://api.example.com/report")),
        versionup.WithInterval(24*time.Hour),
        versionup.WithLogger(log.Default()),
    )

    // 订阅升级事件(事件含下载地址、版本号、工具 id 等)
    up.OnUpdate(func(ev versionup.UpdateEvent) {
        log.Printf("发现新版本 %s -> %s, 下载: %s", ev.CurrentVersion, ev.LatestVersion, ev.DownloadURL)
    })

    // 阻塞运行,直到 ctx 取消
    if err := up.Start(context.Background()); err != nil {
        log.Fatal(err)
    }
}

更多场景见 examples/basic(可直接 go run ./examples/basic 运行)。

服务端协议

NewHTTPSource 默认访问:

GET {baseURL}/version?tool={toolID}

返回 JSON:

{
  "tool_id": "github-speed",
  "version": "v1.4.0",
  "stable": true,
  "download_url": "https://example.com/github-speed-v1.4.0.zip",
  "release_notes": "修复若干 bug",
  "published_at": "2026-07-01T00:00:00Z",
  "checksum": "sha256:9f86d0818..."
}
  • stable=false 的版本不会触发升级。
  • checksum 可选,下载时可用 WithChecksum 校验。
  • 也可用 NewManifestSource 指向一个静态 version.json(支持 map[string]Release 或单个 Release)。

接口一览

接口 职责 默认实现
Source 拉取某工具最新稳定版 NewHTTPSource / NewManifestSource
Reporter 上报升级事件 NewHTTPReporter
Notifier 通知开关 / 订阅 / 定时 NewNotifier
Downloader 下载安装包(进度/校验) NewHTTPDownloader
Updater 顶层聚合(周期/即时检查) New
升级事件 UpdateEvent
type UpdateEvent struct {
    ToolID         string    // 工具 ID
    CurrentVersion string    // 当前版本
    LatestVersion  string    // 最新稳定版本
    DownloadURL    string    // 下载地址
    ReleaseNotes   string    // 更新说明
    PublishedAt    time.Time // 发布时间
    DetectedAt     time.Time // 检测到的时间
}

应用层(Applier / 自动更新)

下载到的包可能是安装器 / 压缩包 / 单个可执行文件三种形态。库提供抽象接口与跨平台安全原语,具体「如何应用」由调用方注入,避免把平台相关逻辑固化进公共包。

// Applier 应用(安装)下载到的更新包。
type Applier interface {
    Apply(ctx context.Context, pkgPath string, ev UpdateEvent) error
}

库提供的原语(纯 Go,零 CGO):

  • ExtractArchive(src, dest string, opts ...ArchiveOption) error:解压 zip / tar / tar.gz / tar.xz;WithFileHook 可在解压每个条目时跳过(用于保留用户配置)。
  • RunInstaller(path string, args ...string) error:启动安装器进程。
  • VerifyChecksum(path, sum string) error:sha256 校验。

三种形态对应策略(建议放项目侧或可选子包 versionup/apply,核心库不内置):

  • 安装器:InstallerStrategy 直接 RunInstaller
  • 压缩包:ArchiveStrategy 解压 + Preserve(rel) hook 跳过用户配置
  • 单可执行文件:SelfReplaceStrategy 平台相关(Windows 文件锁需改名/重启),推荐参考 go-update / Squirrel 等成熟方案

通过 WithApplier / WithAutoApply(bool) 接入 UpdaterAutoApply 默认 false,即仅通知)。

License

MIT – 可自由使用、修改、再发布,仅需保留版权与许可声明。

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrSkip 在 WithFileHook 中返回,表示跳过当前归档条目(用于保留用户配置)。
	ErrSkip = errors.New("versionup: skip this archive entry")
	// ErrNotImplemented 表示某策略尚未实现(如 SelfReplaceStrategy 需平台相关逻辑)。
	ErrNotImplemented = errors.New("versionup: not implemented")
)

应用层相关错误。

Functions

func Compare

func Compare(a, b string) (int, error)

Compare 比较两个语义化版本号 a 与 b(允许前缀 "v"/"V",忽略构建元数据 "+xxx")。 返回 -1(a<b)、0(相等)、1(a>b)。 预发布版本(如 1.5.0-rc1)按 SemVer 规则低于同核心版本的正式版。

func ExtractArchive

func ExtractArchive(src, dest string, opts ...ArchiveOption) error

ExtractArchive 解压 src 到 dest 目录,按扩展名识别格式: zip / tar / tar.gz(.tgz) / tar.bz2(.tbz/.tbz2)。 tar.xz(.txz) 因标准库无 xz 解码器,返回 ErrNotImplemented(需外部依赖)。

func NeedsUpgrade

func NeedsUpgrade(current, latest string, stable bool) bool

NeedsUpgrade 判断从 current 升级到 latest 是否必要。 仅当 latest > current 且 stable 为真时返回 true。

func RunInstaller

func RunInstaller(path string, args ...string) error

RunInstaller 启动下载到的安装器(.exe/.msi/.pkg/.deb 等)并等待其退出。 提权、是否等待完成由调用方决定;本函数仅负责启动进程。

func VerifyChecksum

func VerifyChecksum(path, sum string) error

VerifyChecksum 校验文件 sha256 是否与 sum("sha256:..." 或纯 hex)一致。

Types

type Applier

type Applier interface {
	Apply(ctx context.Context, pkgPath string, ev UpdateEvent) error
}

Applier 应用(安装)下载到的更新包。具体策略由项目选择并注入, 库不固化平台相关的「替换/重启/提权/配置迁移/回滚」逻辑。

type ArchiveOption

type ArchiveOption func(*archiveConfig)

ArchiveOption 配置解压行为。

func WithFileHook

func WithFileHook(fn func(rel string, r io.Reader) error) ArchiveOption

WithFileHook 注册每个普通文件条目的回调,在写入前调用。 回调收到相对路径与条目内容读取器:

  • 返回 ErrSkip 跳过该条目(如保留现有用户配置、不覆盖);
  • 返回其他错误则中止解压;
  • 返回 nil 表示由库写入;此时回调【不得】消费读取器。

func WithStripComponents

func WithStripComponents(n int) ArchiveOption

WithStripComponents 跳过路径前 n 个组件(类似 tar --strip-components)。

type ArchiveStrategy

type ArchiveStrategy struct {
	Dest            string                // 解压目标目录
	StripComponents int                   // 跳过压缩包内前 N 层目录
	Preserve        func(rel string) bool // 返回 true 表示保留现有文件(跳过不覆盖)
}

ArchiveStrategy 处理「压缩包」形态:解压到 Dest,并通过 Preserve 保留用户配置(不覆盖)。

func (ArchiveStrategy) Apply

func (s ArchiveStrategy) Apply(ctx context.Context, pkg string, ev UpdateEvent) error

Apply 解压压缩包,按 Preserve 跳过用户配置文件。

type Checker

type Checker struct {
	// contains filtered or unexported fields
}

Checker 周期检查器:按间隔向 Source 询问最新版本,发现更新则分发事件并上报。

func (*Checker) CheckOnce

func (c *Checker) CheckOnce(ctx context.Context) (*UpdateEvent, error)

CheckOnce 立即执行一次检查。返回发现的升级事件(无更新时为 nil, nil)。

func (*Checker) Start

func (c *Checker) Start(ctx context.Context)

Start 启动周期检查,阻塞直到 ctx 取消。启动后立即检查一次,之后按 interval 周期检查。

type DownloadOption

type DownloadOption func(*downloadConfig)

DownloadOption 下载选项。

func WithChecksum

func WithChecksum(sum string) DownloadOption

WithChecksum 设置下载后校验的 sha256 值("sha256:..." 或纯 hex)。

func WithProgress

func WithProgress(fn func(done, total int64)) DownloadOption

WithProgress 设置下载进度回调(done/total,total 未知时为 -1)。

type Downloader

type Downloader interface {
	Download(ctx context.Context, url, dest string, opts ...DownloadOption) error
}

Downloader 下载器:把版本包下载到本地。

func NewHTTPDownloader

func NewHTTPDownloader(opts ...DownloaderOption) Downloader

NewHTTPDownloader 创建默认 HTTP 下载器。

type DownloaderOption

type DownloaderOption func(*httpDownloader)

DownloaderOption 配置下载器。

func WithDownloadClient

func WithDownloadClient(c *http.Client) DownloaderOption

WithDownloadClient 设置自定义 *http.Client。

type InstallerStrategy

type InstallerStrategy struct {
	Args []string
}

InstallerStrategy 处理「安装器」形态:直接启动安装器,后续由其自身完成。

func (InstallerStrategy) Apply

func (s InstallerStrategy) Apply(ctx context.Context, pkg string, ev UpdateEvent) error

Apply 启动安装器。

type Logger

type Logger interface {
	Printf(format string, args ...any)
}

Logger 最小日志接口,*log.Logger 直接满足(*slog.Logger 可用适配包装)。 默认 nil 表示静默。

type Notifier

type Notifier interface {
	Enable()                             // 开启事件通知
	Disable()                            // 关闭事件通知
	Enabled() bool                       // 查询当前是否开启
	Subscribe(obs Observer)              // 订阅升级通知
	NotifyAt(t time.Time, e UpdateEvent) // 指定时间投递一次通知
	Dispatch(e UpdateEvent)              // 若开启则广播给订阅者
}

Notifier 通知管理器:控制通知开关与定时。

func NewNotifier

func NewNotifier() Notifier

NewNotifier 创建默认通知管理器(默认开启通知)。

type Observer

type Observer func(UpdateEvent)

Observer 事件观察者(通知订阅者)。

type Option

type Option func(*Updater)

Option 配置 Updater。

func WithApplier

func WithApplier(a Applier) Option

WithApplier 注入应用更新包的策略(安装器/解压/自替换)。

func WithAutoApply

func WithAutoApply(b bool) Option

WithAutoApply 控制「发现更新后是否自动调用 Applier.Apply」,默认 false(仅通知)。

func WithInterval

func WithInterval(d time.Duration) Option

WithInterval 设置检查间隔(默认 24h)。

func WithLogger

func WithLogger(l Logger) Option

WithLogger 设置日志输出(默认静默)。

func WithNotifier

func WithNotifier(n Notifier) Option

WithNotifier 设置通知管理器(可选,默认 NewNotifier 且开启)。

func WithReporter

func WithReporter(r Reporter) Option

WithReporter 设置事件上报器(可选)。

func WithSource

func WithSource(s Source) Option

WithSource 设置版本源(必填,否则 Start/CheckNow 返回错误)。

type Release

type Release struct {
	ToolID       string    `json:"tool_id"`
	Version      string    `json:"version"`       // 语义化版本,如 v1.2.3
	Stable       bool      `json:"stable"`        // 是否稳定版
	DownloadURL  string    `json:"download_url"`  // 安装包下载地址
	ReleaseNotes string    `json:"release_notes"` // 更新说明
	PublishedAt  time.Time `json:"published_at"`
	Checksum     string    `json:"checksum"` // 可选,sha256 hex(可带 "sha256:" 前缀)
}

Release 服务端返回的一个稳定发布版本。

type Reporter

type Reporter interface {
	Report(ctx context.Context, event UpdateEvent) error
}

Reporter 版本升级事件上报接口。

func NewHTTPReporter

func NewHTTPReporter(endpoint string, opts ...ReporterOption) Reporter

NewHTTPReporter 创建默认 HTTP 上报器:POST 事件 JSON 到 endpoint。 上报失败仅返回错误,由调用方决定是否忽略(检查循环不因此中断)。

type ReporterOption

type ReporterOption func(*httpReporter)

ReporterOption 配置上报器。

func WithReporterClient

func WithReporterClient(c *http.Client) ReporterOption

WithReporterClient 设置自定义 *http.Client。

type SelfReplaceStrategy

type SelfReplaceStrategy struct {
	CurrentExe string
}

SelfReplaceStrategy 处理「单可执行文件」形态:平台相关(Windows 文件锁需改名/重启), 核心库不实现。推荐参考 go-update / Squirrel 等成熟方案,或项目侧按目标平台实现:

  1. 下载新 exe 到临时名;2) 将当前 exe 重命名为 .old;
  2. 新 exe 落到原路径;4) 启动新 exe 并退出自己; 若仍被锁则用 MoveFileEx 的 MOVEFILE_DELAY_UNTIL_REBOOT 延迟到重启替换。

func (SelfReplaceStrategy) Apply

Apply 返回 ErrNotImplemented 占位。

type Source

type Source interface {
	Latest(ctx context.Context, toolID string) (*Release, error)
}

Source 版本源:拉取某工具最新稳定版。

func NewHTTPSource

func NewHTTPSource(baseURL string, opts ...SourceOption) Source

NewHTTPSource 创建基于 HTTP 端点的版本源。 端点约定:GET {baseURL}/version?tool={toolID} 返回 Release JSON(见文档 §7)。

func NewManifestSource

func NewManifestSource(rawURL string, opts ...SourceOption) Source

NewManifestSource 创建基于静态清单文件的版本源。

type SourceOption

type SourceOption func(*sourceConfig)

SourceOption 配置版本源(如自定义 HTTP Client)。

func WithSourceClient

func WithSourceClient(c *http.Client) SourceOption

WithSourceClient 设置自定义 *http.Client。

func WithSourceUserAgent

func WithSourceUserAgent(ua string) SourceOption

WithSourceUserAgent 设置 User-Agent。

type UpdateEvent

type UpdateEvent struct {
	ToolID         string
	CurrentVersion string
	LatestVersion  string
	DownloadURL    string
	ReleaseNotes   string
	PublishedAt    time.Time
	DetectedAt     time.Time
}

UpdateEvent 版本升级事件。当比对发现「有新稳定版本」时由库构造并分发。

type Updater

type Updater struct {
	ToolID   string
	Current  string
	Source   Source
	Reporter Reporter
	Notifier Notifier
	Checker  *Checker
	// contains filtered or unexported fields
}

Updater 版本升级器:组合 Source / Reporter / Notifier / Checker,提供周期检查与即时检查。

func New

func New(toolID, current string, opts ...Option) *Updater

New 创建 Updater。Source 必须通过 WithSource 提供,否则 Start/CheckNow 会返回错误。

func (*Updater) CheckNow

func (u *Updater) CheckNow(ctx context.Context) (*UpdateEvent, error)

CheckNow 立即执行一次检查。

func (*Updater) OnUpdate

func (u *Updater) OnUpdate(obs Observer)

OnUpdate 订阅升级通知(便捷方法)。

func (*Updater) Start

func (u *Updater) Start(ctx context.Context) error

Start 启动周期检查,阻塞直到 ctx 取消或 Stop 调用。

func (*Updater) Stop

func (u *Updater) Stop()

Stop 停止周期检查。

Directories

Path Synopsis
examples
basic command
Command basic 演示 versionup 的最小可运行用法: 启动一个本地假服务端(httptest),周期检查更新、订阅通知、下载安装包、并演示定时通知。
Command basic 演示 versionup 的最小可运行用法: 启动一个本地假服务端(httptest),周期检查更新、订阅通知、下载安装包、并演示定时通知。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL