autoupdate

package
v0.8.2 Latest Latest
Warning

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

Go to latest
Published: Aug 22, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package autoupdate tells a user that a newer release exists, and nothing else.

It never installs. goselfupdate.Update installs, and the command wrapping it is where errors are printed; a failure here is recorded in the state file and swallowed. That single rule is what keeps a development build from printing an update failure on every invocation.

session := autoupdate.Start(ctx, autoupdate.Config{
	Update: goselfupdate.Config{Owner: "you", Repo: "tool", Binary: "tool", Version: version},
})
err := rootCmd.ExecuteContext(ctx)
session.Finish()

The check runs concurrently with the caller's own work and the notice is printed afterwards, so a fast command pays nothing and the line is not buried in the command's output.

A separate package from goselfupdate so that a program wanting only the update half links none of this.

Index

Constants

View Source
const (
	FleetDisable  = "NO_AUTO_UPDATE"
	FleetInterval = "AUTO_UPDATE_INTERVAL"
)

Environment variables. Presence-only, any value including empty -- the NO_COLOR convention -- so that NO_AUTO_UPDATE=0 cannot mean "on". The interval gets its own name precisely so presence and value never have to share one variable.

View Source
const DefaultInterval = 24 * time.Hour

DefaultInterval is how long a check is good for.

View Source
const DefaultTimeout = 5 * time.Second

DefaultTimeout bounds the check. It is generous because the check runs concurrently with the caller's own work and is abandoned if that finishes first, so the only thing this limits is how long Finish will wait.

View Source
const Schema = 1

Schema is the version of the on-disk state format.

The same schema is written by pyselfupdate and bashselfupdate, so any tool can read any other tool's state and one dashboard can glob ~/.local/state/*/autoupdate.json with no per-tool knowledge. Adding a field is safe; renaming or repurposing one breaks the other two.

View Source
const StateFilename = "autoupdate.json"

StateFilename is the file written inside the per-tool state directory.

Variables

This section is empty.

Functions

func StateHome

func StateHome() string

StateHome is the directory state files live under, honoring XDG_STATE_HOME.

func StatePath

func StatePath(tool string) string

StatePath is where a tool's state file lives.

Types

type Config

type Config struct {
	// Update is the underlying update configuration. Required.
	Update goselfupdate.Config

	// Tool names the state directory and the per-tool environment variables.
	// Defaults to Update.Binary.
	Tool string

	// Interval between checks. Zero means DefaultInterval.
	Interval time.Duration

	// Timeout bounds the check. Zero means DefaultTimeout.
	Timeout time.Duration

	// Out receives the one line this package is allowed to print. Defaults to
	// os.Stderr.
	Out io.Writer

	// Interactive overrides terminal detection. Nil detects. Set it to false
	// from a program that already knows it is writing somewhere a human will
	// not read -- into a pager, a log, a structured-output mode -- since
	// nothing about the streams themselves reveals that.
	Interactive *bool

	// Suppress skips the check unconditionally. Intended for a command that
	// must never trigger one: an update command, a shell-completion callback.
	// See [cobracmd] for the list that matters in practice.
	Suppress bool

	// StateDir overrides where state is written. Defaults to [StateHome].
	StateDir string

	// Clock supplies the current time. Defaults to time.Now.
	Clock func() time.Time

	// Environ looks up an environment variable, as os.LookupEnv does.
	Environ func(string) (string, bool)
}

Config describes one updatable tool.

type Outcome

type Outcome struct {
	Checked bool
	Skip    Skip
	Current string
	Latest  string
}

Outcome reports what happened. Returned for tests and dashboards, not for control flow: a caller has nothing useful to do with it.

func (Outcome) UpdateAvailable

func (o Outcome) UpdateAvailable() bool

UpdateAvailable reports whether the check found a newer release.

type Session

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

Session is an in-flight check.

func Start

func Start(ctx context.Context, config Config) *Session

Start runs the gate and, if it passes, begins a check in the background.

It returns immediately and never blocks the command about to run. Call Session.Finish once that command has finished.

func (*Session) Finish

func (s *Session) Finish() Outcome

Finish prints the notice, if there is one, and returns what happened.

It never returns an error and never panics: an update notice must not be able to break the command the user actually typed. Safe to call more than once.

type Skip

type Skip string

Skip explains why a check did not happen.

const (
	SkipNone       Skip = ""
	SkipDisabled   Skip = "disabled"
	SkipDevBuild   Skip = "dev-build"
	SkipNotATTY    Skip = "not-a-tty"
	SkipCI         Skip = "ci"
	SkipInterval   Skip = "interval"
	SkipFailed     Skip = "failed"
	SkipSuppressed Skip = "suppressed"
)

func Enabled

func Enabled(config Config) (bool, Skip)

Enabled reports whether a check would run, without touching the network, the clock or the state file.

The interval is deliberately not consulted: this answers "is this tool opted in", not "is it due". It backs a fleet dashboard and a `<tool> update --why`.

type State

type State struct {
	Schema int    `json:"schema"`
	Tool   string `json:"tool"`

	CheckedAt string `json:"checked_at"`

	// CheckedAtEpoch is the same instant as CheckedAt. Redundant on purpose:
	// BSD date on macOS cannot parse ISO-8601 without -j -f gymnastics, and the
	// bash implementation has to do interval arithmetic with jq and date +%s
	// alone. One duplicated field buys a portable bash sibling.
	CheckedAtEpoch int64 `json:"checked_at_epoch"`

	CurrentVersion string `json:"current_version"`
	LatestVersion  string `json:"latest_version"`

	// LastError is non-empty when the last check failed. There is deliberately
	// no separate "skip reason" field: a gate that declines to check does not
	// write this file at all, which is what makes the absence of a state file
	// observable proof that the network was never touched.
	LastError string `json:"last_error"`
}

State is one tool's record of its last update check.

This is state, not configuration and not cache: it persists across runs, it is not authored by the user, and deleting it changes behavior rather than merely costing a recompute. That is XDG_STATE_HOME by the Base Directory specification, and it is where gh puts the same thing.

func ReadState

func ReadState(tool string) State

ReadState returns a tool's state, or a zero value when it has never been written.

A corrupt or unreadable file reads as empty rather than failing: the file is a throttle, and breaking a user's command because the throttle cannot be read would be worse than checking one extra time.

Jump to

Keyboard shortcuts

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