Documentation
¶
Overview ¶
Package updater fetches release manifests, downloads matching binaries, verifies them by SHA256 checksum, and atomically swaps the running executable.
Design notes:
- Version comparison uses golang.org/x/mod/semver (Go's curated semver) so "v0.10.0" is correctly newer than "v0.9.0" — the original spike used lexicographic string comparison and silently broke double-digit minor versions.
- DownloadRelease writes to dest+".tmp", verifies the SHA256 checksum against the asset's manifest entry, then renames. A mismatch removes the tmp file and returns an error before any swap happens.
- Asset.Checksum is REQUIRED (not omitempty in the JSON sense, and an empty value is rejected at download time as "untrusted binary"). The original spike had VerifyChecksum as a public function with no callers; this rewrite wires it in as a security-required step.
- ApplyUpdate uses os.Rename + os.Rename rollback on Unix-like filesystems. Windows is not currently handled (the running EXE is locked); a future enhancement would write to a .new file and let the orchestrator swap on restart.
- The current version is held in package var Version, settable at build time via: go build -ldflags "-X github.com/dd0wney/graphdb/pkg/updater.Version=v1.2.3" If unset, Version defaults to "dev" and every update check returns "update available" (intentional for dev builds).
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoAsset = errors.New("no asset for current OS/arch in release")
ErrNoAsset is returned when a release contains no asset matching the current runtime.GOOS / runtime.GOARCH.
var ErrNoChecksum = errors.New("asset has no checksum; refusing to download untrusted binary")
ErrNoChecksum is returned when a release asset has an empty checksum. Refusing to download in that case is a security policy: a manifest author who omits checksums is asking us to install an unverified binary.
var Version = "dev"
Version is the current build's semver string. Set via -ldflags:
go build -ldflags "-X github.com/dd0wney/graphdb/pkg/updater.Version=v1.2.3"
If unset (i.e., "dev"), every CheckForUpdates call reports an update available — intentional for development builds.
Functions ¶
func ApplyUpdate ¶
ApplyUpdate atomically swaps the running executable with the binary at newBinaryPath. The current binary is moved to <exe>.old as a backup; if the second rename fails, the backup is restored. Successful swaps remove the backup.
On Unix-like filesystems both renames are atomic. Windows is not currently handled — the running EXE is locked there; callers on Windows will see a rename error. A future enhancement would write to a .new file and let the orchestrator perform the swap on restart.
func DownloadRelease ¶
DownloadRelease downloads the asset matching runtime.GOOS/GOARCH from the given release, writing first to dest+".tmp", then verifying its SHA256 against the manifest checksum, then renaming to dest. On any failure the tmp file is removed and an error is returned — dest is never written unless the checksum verifies.
func IsNewer ¶
IsNewer reports whether latest is a strictly higher semver than current. "dev" and "" current versions are always considered older (so every dev build sees updates).
func VerifyChecksum ¶
VerifyChecksum SHA256-hashes the file at path and returns nil iff its hex digest matches expected (case-insensitive). Wired into DownloadRelease — this is not an opt-in helper, it's a required step.
Types ¶
type Asset ¶
type Asset struct {
Name string `json:"name"`
OS string `json:"os"` // matches runtime.GOOS, e.g. "linux"
Arch string `json:"arch"` // matches runtime.GOARCH, e.g. "amd64"
URL string `json:"url"`
Checksum string `json:"checksum"` // SHA256 hex digest (case-insensitive)
}
Asset represents a downloadable binary for a specific OS/arch. Checksum is required at download time; a release entry with an empty Checksum is rejected as untrusted.
type Release ¶
type Release struct {
Version string `json:"version"` // semver, e.g. "v1.2.3"
Channel string `json:"channel"` // e.g. "stable", "beta"
ReleaseDate time.Time `json:"release_date"` //nolint:tagliatelle // external manifest schema; keep snake_case
Description string `json:"description"`
Assets []Asset `json:"assets"`
}
Release represents a single entry in the release manifest.
type UpdateStatus ¶
type UpdateStatus struct {
CurrentVersion string `json:"current_version"` //nolint:tagliatelle // external API schema
LatestVersion string `json:"latest_version"` //nolint:tagliatelle
UpdateAvailable bool `json:"update_available"` //nolint:tagliatelle
LatestRelease *Release `json:"latest_release,omitempty"` //nolint:tagliatelle
}
UpdateStatus is the result of a CheckForUpdates call.
func CheckForUpdates ¶
func CheckForUpdates(ctx context.Context, manifestURL, channel string) (*UpdateStatus, error)
CheckForUpdates fetches the JSON manifest at manifestURL, picks the highest-semver release in the named channel (channel "" matches any), and returns whether it's newer than the running Version.